Comparar commits

...

147 Commits

Autor SHA1 Mensagem Data
Steve Heffernan 3540fcfee8 Merge branch 'hotfix/multiple-control-fades-fix' into stable 2012-05-02 20:55:57 -07:00
Steve Heffernan f82312e5fb Adding line to CHANGELOG: Fixed error with multiple controls fading listeners 2012-05-02 20:55:57 -07:00
Steve Heffernan aa7d5a2839 Bumping version to 3.2.2. 2012-05-02 20:55:35 -07:00
Steve Heffernan a787b5ca3a Fixed multiple event listeners added for controls fading on play event. fixes #173 2012-05-02 20:54:40 -07:00
Steve Heffernan 5fc725c088 Merge branch 'hotfix/options-width-fix' into stable 2012-04-06 16:50:16 -07:00
Steve Heffernan 2ef7617c66 Adding line to CHANGELOG: Fixed setting width/height with javascript options 2012-04-06 16:50:16 -07:00
Steve Heffernan c222eb1381 Bumping version to 3.2.1. 2012-04-06 16:49:47 -07:00
Steve Heffernan dca5644878 Updated options loading to be better. 2012-04-06 16:42:09 -07:00
Steve Heffernan 00567cff6c Spelling error. 2012-03-23 17:10:35 -07:00
Steve Heffernan a9567e2574 removed outline from button focus. 2012-03-20 13:01:10 -07:00
Steve Heffernan 352bd386c3 Merge branch 'release/baxter' into stable 2012-03-20 11:17:27 -07:00
Steve Heffernan 8e50338da3 Rotating CHANGELOG. 2012-03-20 11:17:27 -07:00
Steve Heffernan 150a1796b6 Bumping version to 3.2.0. 2012-03-20 11:17:27 -07:00
Steve Heffernan 4518fb0cef Merge branch 'stable' into release/baxter 2012-03-20 11:17:27 -07:00
Steve Heffernan 0ad74d53f6 Cleaned up demo file. 2012-03-20 11:07:14 -07:00
Steve Heffernan 1fe7855ec7 Added note in demo about Flash SWF location. 2012-03-20 11:02:47 -07:00
Steve Heffernan 83dbc92a59 Updated menu button styles. 2012-03-20 10:55:00 -07:00
Steve Heffernan df72cc6eb1 Added captions file to release files. Added caption to demo.
Changed demo URLs to be local, so that they work with current release file, not old release on server.
2012-03-20 10:28:10 -07:00
Steve Heffernan 0657d3d118 Merge branch 'feature/update-docs' 2012-03-19 16:02:21 -07:00
Steve Heffernan bf16a832ef Adding line to CHANGELOG: Updated docs with more options. 2012-03-19 16:02:21 -07:00
Steve Heffernan 83ed670dc3 Fixed no-text-tracks error 2012-03-19 15:55:05 -07:00
Steve Heffernan 10ec069e71 Changed 'poster' component to 'posterImage' to prevent conflicts with API. 2012-03-19 15:50:05 -07:00
Steve Heffernan d06badf061 Updated tracks docs 2012-03-19 15:49:12 -07:00
Steve Heffernan 393973775c Removed dumb leftover log output. 2012-03-19 09:42:41 -07:00
Steve Heffernan 10c4ef87a3 Made cues display block so they'd be on their own line. 2012-03-16 16:25:43 -07:00
Steve Heffernan d0b9903395 Started Adding tracks docs.
Added example captions track.
Updated webvtt parser to support no id line in captions.
Updated webvtt parser to support no hours place in the time.
2012-03-16 15:32:48 -07:00
Steve Heffernan 71a7e5f68a Update docs content. 2012-03-16 13:54:03 -07:00
Steve Heffernan 2fe90f9c5c Udpated changelog 2012-03-16 13:28:38 -07:00
Steve Heffernan 8d09420b6a Removed controls showing test. 2012-03-16 13:26:35 -07:00
Steve Heffernan 9943405289 Logging flash errors. 2012-03-16 13:25:15 -07:00
Steve Heffernan 6c44a19fba Logging Flash errors. 2012-03-16 13:17:42 -07:00
Steve Heffernan fd1e8e9edf Merge branch 'feature/tracks' 2012-03-16 12:55:16 -07:00
Steve Heffernan cae79a9fe0 Adding line to CHANGELOG: Overhauled HTML5 Track support. 2012-03-16 12:55:16 -07:00
Steve Heffernan 7aa70fe7be Cleaned up code from text track additions. 2012-03-16 12:45:51 -07:00
Steve Heffernan 2aa5a2ee09 Finishing off TextTrack support. 2012-03-16 12:29:38 -07:00
Steve Heffernan 72a423237c Updated track styles and make subtitles button work. 2012-03-10 08:54:46 -08:00
Steve Heffernan aeecc92194 Refactored tracks to better match HTML5 spec 2012-03-09 17:12:38 -08:00
Steve Heffernan 12816b8409 Added captions button and display styles. 2012-03-02 13:52:06 -08:00
Steve Heffernan 96a3b03fd7 Updated rake file with special case lib generation.
Added notes to changelog.
2012-02-15 13:31:54 -08:00
Steve Heffernan 043203781e Merge pull request #140 from carlocapocasa/master
Flash src api call autoplay fix
2012-02-14 16:56:19 -08:00
Steve Heffernan 5702380c16 Merge pull request #142 from GianlucaGuarini/patch-3
fix  localStorage context
2012-02-14 16:33:05 -08:00
Steve Heffernan 4d8316676f Merge branch 'feature/fullscreen-change-event-fix' 2012-02-13 17:53:16 -08:00
Steve Heffernan 4da9ae8d07 Adding line to CHANGELOG: Updated 'fullscreenchange' even to be calledd d even if the user presses escape to exit fullscreen. 2012-02-13 17:53:16 -08:00
Steve Heffernan b0ac995f26 Updated fullscreenchange event to work on user escape 2012-02-13 17:51:52 -08:00
Steve Heffernan 765ef8e93e Updated fullscreenchange event to trigger on users escape 2012-02-13 17:42:22 -08:00
Steve Heffernan 168ae2cbe8 Merge branch 'feature/relative-flash-urls' 2012-02-13 17:14:50 -08:00
Steve Heffernan 6932c64a18 Adding line to CHANGELOG: Automatically converting URsource URL to absolute for Flash fallback. 2012-02-13 17:14:50 -08:00
Steve Heffernan e4723240b6 Made src() update URL to absolute also. 2012-02-13 17:13:14 -08:00
Steve Heffernan c714781aef Added translation of Flash URL to absolute URL. 2012-02-13 16:59:57 -08:00
Steve Heffernan 6afb6d8e75 Added event comment 2012-02-13 13:23:06 -08:00
Steve Heffernan ca6a04acfe Merge branch 'feature/loadedalldata-event' 2012-02-13 13:16:53 -08:00
Steve Heffernan 842d761a37 Adding line to CHANGELOG: Created new 'loadedalldata' event for when the source is completely downloaded 2012-02-13 13:16:53 -08:00
Steve Heffernan df6d6647f7 Created new 'loadedalldata' event, and test. 2012-02-13 13:15:20 -08:00
Steve Heffernan 20ec85189c Fixed some docs text. 2012-02-13 13:00:13 -08:00
Steve Heffernan cc2d1e136c Fixed doc link.
Added tests.
2012-02-07 11:10:35 -08:00
Steve Heffernan c762accb39 Fixed issue with .one scope.
Improved destroy method.
Still building out tests.
2012-02-03 17:53:59 -08:00
Steve Heffernan 2751be7e0f Updated qUnit tests. 2012-02-03 15:22:57 -08:00
Steve Heffernan 0f45812b71 Merge branch 'feature/api-ready-refactor' 2012-02-03 10:51:38 -08:00
Steve Heffernan 4fdbf91222 Adding line to CHANGELOG: Refactored API to be more immediately available. 2012-02-03 10:51:38 -08:00
Steve Heffernan 45f519820a Updated techGet to handle common errors. 2012-02-03 10:28:00 -08:00
Steve Heffernan 5c4c2d30f2 Introduced techSet/techCall methods. techCall caches method calls if tech isn't ready. techGet provides defaults. 2012-02-02 19:16:45 -08:00
Steve Heffernan a839c89f0b Updated player API to be avialable immediately.
Moved auto setup code to setup.js.
2012-02-02 14:56:47 -08:00
Steve Heffernan 56355e572a Fixed an error in reporting no method name 2012-01-31 15:22:25 -08:00
Steve Heffernan 32c9ecf242 Updated video tag attribute setting to not include null values. 2012-01-31 11:51:37 -08:00
Gianluca Guarini 35eb539d00 fix localStorage context 2012-01-31 11:52:26 +01:00
Carlo Capocasa 62feb5ad5f Fix: Flash always autoplays on src api call 2012-01-31 08:28:34 +01:00
Steve Heffernan c4fbc914cb Merge branch 'master' into stable 2012-01-30 15:14:04 -08:00
Steve Heffernan 1eda892c86 Fixed a double declared var. 2012-01-30 15:13:18 -08:00
Steve Heffernan 3fb6a14bbd Merge branch 'release/leonardo' 2012-01-30 13:59:31 -08:00
Steve Heffernan 5337efb0e7 Merge branch 'release/leonardo' into stable 2012-01-30 13:59:29 -08:00
Steve Heffernan 05338dc704 Rotating CHANGELOG. 2012-01-30 13:59:29 -08:00
Steve Heffernan c3da40b3d8 Bumping version to 3.1.0. 2012-01-30 13:59:29 -08:00
Steve Heffernan c84f0386bb Merge branch 'stable' into release/leonardo 2012-01-30 13:59:28 -08:00
Steve Heffernan e9e1eeae63 Updated changelog with FF9 fullscreen fix. 2012-01-30 12:57:17 -08:00
Heff 2ecee69838 Merge pull request #139 from GianlucaGuarini/patch-1
fix for firefox9 fullscreen (only if it is enabled)
2012-01-30 12:54:24 -08:00
Steve Heffernan 69088f3c20 Merge branch 'feature/removing-swfobject' 2012-01-30 11:07:15 -08:00
Steve Heffernan 4e6c5bc539 Adding line to CHANGELOG: Replaced swfobject with custom embed to save file size. 2012-01-30 11:07:14 -08:00
Steve Heffernan 43f10ab655 Removed swfobject from build. Using custom Flash embed to save file size. 2012-01-30 11:05:55 -08:00
Steve Heffernan 234f7acf84 Merge branch 'feature/flash-iframe' 2012-01-30 11:01:02 -08:00
Steve Heffernan c40cd9b03c Adding line to CHANGELOG: Added flash iframe-mode, an experimental method for getting around flash reloading issues. 2012-01-30 11:01:02 -08:00
Steve Heffernan f5681765fd Swapped out swfobject version check for new custom version check. 2012-01-30 10:58:26 -08:00
Gianluca Guarini a0f06a5908 fix for firefox9 fullscreen (only if it is enabled) 2012-01-30 12:08:58 +01:00
Steve Heffernan f67c5d951c Made iframe mode no longer the default for further testing. 2012-01-29 20:04:58 -08:00
Steve Heffernan f5ab5d5c43 Tried to fix iframe mode for Firefox. Ended up having to rule it out.
Fixed an issue with fullscreen event calling.
2012-01-29 20:03:44 -08:00
Steve Heffernan 8719371e21 Updated iFrame mode 2012-01-28 18:56:28 -08:00
Steve Heffernan 372ec827a3 Merge branch 'master' into feature/flash-iframe 2012-01-28 17:27:45 -08:00
Steve Heffernan b994a177f9 Merge branch 'feature/slider-knobb-placement-fix' 2012-01-28 16:28:59 -08:00
Steve Heffernan 9bea1b768f Adding line to CHANGELOG: Fixed issue with volume knob position. Improved controls fading. 2012-01-28 16:28:59 -08:00
Steve Heffernan 52975871e1 Fixed issue with volume knob positioning.
Using visiblity for for hiding in IE6.
Updated controls fading.
2012-01-28 16:27:03 -08:00
Steve Heffernan ca9d044ae9 Working towards getting styles to work on controls 2012-01-27 16:57:24 -08:00
Steve Heffernan 84e1f508e7 Component lists are now objects that allow you to set options on. 2012-01-27 15:57:03 -08:00
Steve Heffernan 79d5694c99 Starting on new component loading. 2012-01-27 15:04:25 -08:00
Steve Heffernan fbadb72eb1 Merge branch 'feature/fullscreen-second-click-fix' 2012-01-27 12:27:56 -08:00
Steve Heffernan a4213763be Adding line to CHANGELOG: Fixed ian issue with triggering fullscreen a second time. 2012-01-27 12:27:55 -08:00
Steve Heffernan cd4f7e1945 Changed videoIsFullScreen to isFullScreen.
Watching for fullscreen event to update player status when escape key is used.
2012-01-27 12:22:05 -08:00
Steve Heffernan 58aaa100aa Cleaned up whitespace 2012-01-27 10:36:02 -08:00
Steve Heffernan 8437f704c7 Added feature note 2012-01-27 09:25:44 -08:00
Heff 375a201303 Merge pull request #138 from dready/ff3
Firefox 3.9 fixes
2012-01-27 09:23:25 -08:00
Thomas Subera ecd8fcc46e fix Attribute getters, ff3 needs getAttrobute instead of just the magic getter. 2012-01-27 09:47:52 +01:00
Thomas Subera 304921128c fix children in ff3
ff3 does not support children, only childNodes
2012-01-25 17:32:50 +01:00
Steve Heffernan 20d5342da2 Fixed a hotfix note. 2012-01-23 17:00:21 -08:00
Steve Heffernan 14e250cdf4 Merge branch 'master' of github.com:zencoder/video-js 2012-01-23 16:59:58 -08:00
Steve Heffernan 66068858d7 Added feature note. 2012-01-23 16:59:51 -08:00
Heff 17cec68bbb Merge pull request #131 from ykomatsu/master
Patch for issue #130
2012-01-23 16:59:04 -08:00
Steve Heffernan 740fca702c Merge branch 'hotfix/fix-ie-controls-hiding' 2012-01-23 16:02:53 -08:00
Steve Heffernan c4fb8b6fb4 Merge branch 'hotfix/fix-ie-controls-hiding' into stable 2012-01-23 16:02:50 -08:00
Steve Heffernan e967754c27 Adding line to CHANGELOG: CFixed issue with controls not hiding in IE due to no opacity support 2012-01-23 16:02:50 -08:00
Steve Heffernan 2b59be792d Bumping version to 3.0.8. 2012-01-23 16:01:39 -08:00
Steve Heffernan c0c6db7071 Added a fix for IE controls hiding 2012-01-23 16:00:51 -08:00
Steve Heffernan e14909468a Created new flash embed method. 2012-01-23 14:55:06 -08:00
Steve Heffernan 865f914dad Added flash version check 2012-01-20 18:20:00 -08:00
Steve Heffernan 7631edae24 commented out some currently unused code 2012-01-20 17:42:38 -08:00
Steve Heffernan 24bda85d73 Created iframe mode for getting around flash reloading issues. 2012-01-20 17:34:18 -08:00
Yoshito Komatsu 528fa52b29 Added encodeURIComponent to escape source.src 2012-01-18 23:24:02 +09:00
Steve Heffernan c6c63df7fd Fixed a check for current tech in source loading 2012-01-17 15:31:11 -08:00
Steve Heffernan fa9e97bb56 Fixed an error in the docs. 2012-01-17 11:02:18 -08:00
Steve Heffernan 0c2e3f5f5a Added line to feature list for Fullscreen edit pull request.
Added require.js for including individual source files during development.
2012-01-17 10:18:19 -08:00
Steve Heffernan 7106c005ed Removed compare code out of project. 2012-01-16 16:26:22 -08:00
Steve Heffernan fb446cd059 Merge branch 'feature/removing-stalled-spinner' 2012-01-16 15:44:09 -08:00
Steve Heffernan b9af34b33d Adding line to CHANGELOG: Stopped spinner from showing on 'stalled' events since browsers sometimes don't show that they've recovered. 2012-01-16 15:44:09 -08:00
Steve Heffernan baab54c560 Removed spinner on stall. 2012-01-16 15:42:31 -08:00
Steve Heffernan 244159d9e9 Added comment reference to mozFullScreenEnabled info 2012-01-16 15:03:38 -08:00
Heff 61aad619a8 Merge pull request #128 from rainboxx/patch-1
Changed checking whether fullscreen API is available or not.  Firefox 9 ...
2012-01-16 15:01:54 -08:00
Steve Heffernan a2249ddf3e Merge branch 'feature/fixing-cdn-version-var' 2012-01-14 18:15:29 -08:00
Steve Heffernan 23c219d0ce Adding line to CHANGELOG: Fixed CDN Version which was breaking dev.html 2012-01-14 18:15:29 -08:00
Steve Heffernan a6b549813e Moved setup.js to end of build file. 2012-01-14 18:14:17 -08:00
Steve Heffernan 4f34034c51 Fixed CDN Version that was breaking dev.html.
Fixed Double auto-setup
2012-01-14 18:13:13 -08:00
Steve Heffernan 9b881cdf13 Merge branch 'feature/independent-full-window' 2012-01-14 18:05:29 -08:00
Steve Heffernan 219f83e1f2 Adding line to CHANGELOG: Made full-window mode more independent 2012-01-14 18:05:29 -08:00
Steve Heffernan e6967528cc Made full window mode more independent 2012-01-14 18:03:37 -08:00
Steve Heffernan 3c21510d72 Merge branch 'feature/release-rakefile' 2012-01-13 11:37:17 -08:00
Steve Heffernan 24e6e72dc4 Adding line to CHANGELOG: Added rakefile for release generation 2012-01-13 11:37:17 -08:00
Steve Heffernan 34896d1f2c Fixed download version to include all files. 2012-01-13 11:35:44 -08:00
Steve Heffernan 18ef75afbb Added rakefile for generating releases.
Updated swf url to adjust based on CDN version.
2012-01-13 09:26:18 -08:00
Matthias Dietrich 16d9525fa8 Changed checking whether fullscreen API is available or not. Firefox 9 will be handled as "not possible" (document.mozFullScreenEnabled not implemented, fullscreen API deactivated by default) and Firefox 10 can be handled according its config (document.mozFullScreenEnabled represent the current config).
Additional info:
* https://bugzilla.mozilla.org/show_bug.cgi?id=694690
* https://developer.mozilla.org/en/DOM/document.mozFullScreenEnabled
2012-01-13 17:34:39 +01:00
Steve Heffernan 0deba6e673 Merge branch 'hotfix/fixing-ie8-poster-bug' 2012-01-12 17:40:46 -08:00
Steve Heffernan 3708b1b6a5 Merge branch 'hotfix/fixing-ie8-poster-bug' into stable 2012-01-12 17:40:44 -08:00
Steve Heffernan 386161c5b9 Adding line to CHANGELOG: Fixed an ie8 breaking bug with the poster 2012-01-12 17:40:44 -08:00
Steve Heffernan aa72d9b050 Bumping version to 3.0.7. 2012-01-12 17:40:24 -08:00
Steve Heffernan c345889535 Fixed an issue where ie8 was borking on a bad image attribute. 2012-01-12 17:39:25 -08:00
Steve Heffernan b7e7da427f Merge branch 'hotfix/docs-url-fix' 2012-01-12 16:35:18 -08:00
Steve Heffernan 1fedf3c1b9 Merge branch 'hotfix/event-layer-x-deprecation-fix' 2012-01-12 15:44:53 -08:00
Steve Heffernan 35dfa99e59 Merge branch 'hotfix/undefined-source-fix' 2012-01-12 14:30:16 -08:00
Heff a4986cd5f9 Merge pull request #125 from raycohen/master
Check if source is defined in loadTech before accessing source.src
2012-01-12 14:18:03 -08:00
Raymond Cohen 2d8f283680 If there are no sources when the player is initialized, loadTech is called with an undefined source.
It would then error on source.src
2012-01-12 16:56:38 -05:00
Steve Heffernan f9c1905840 Merge branch 'hotfix/doc-change'
Conflicts:
	CHANGELOG.md
	VERSION.yml
2012-01-12 13:33:02 -08:00
Steve Heffernan aca861a190 Set up zenflow workflow management config. 2012-01-12 12:58:02 -08:00
34 arquivos alterados com 5172 adições e 1027 exclusões
+37
Ver Arquivo
@@ -16,3 +16,40 @@ CHANGELOG
---- 3.0.6 / 2012-01-12 / docs-url-fix -----------------------------------------
* Fixed wrong URL for CDN in docs
---- 3.0.7 / 2012-01-12 / fixing-ie8-poster-bug --------------------------------
* Fixed an ie8 breaking bug with the poster
---- 3.0.8 / 2012-01-23 / fix-ie-controls-hiding -------------------------------
* Fixed issue with controls not hiding in IE due to no opacity support
---- 3.1.0 / 2012-01-30 / leonardo ---------------------------------------------
* Added CSS fix for Firefox 9 fullscreen (in the rare case that it's enabled)
* Replaced swfobject with custom embed to save file size.
* Added flash iframe-mode, an experimental method for getting around flash reloading issues.
* Fixed issue with volume knob position. Improved controls fading.
* Fixed ian issue with triggering fullscreen a second time.
* Fixed issue with getting attributes in Firefox 3.0
* Escaping special characters in source URL for Flash
* Added a check for if Firefox is enabled which fixes a Firefox 9 issue
* Stopped spinner from showing on 'stalled' events since browsers sometimes don't show that they've recovered.
* Fixed CDN Version which was breaking dev.html
* Made full-window mode more independent
* Added rakefile for release generation
---- 3.2.0 / 2012-03-20 / baxter -----------------------------------------------
* Updated docs with more options.
* Overhauled HTML5 Track support.
* Fixed Flash always autoplaying when setting source.
* Fixed localStorage context
* Updated 'fullscreenchange' event to be called even if the user presses escape to exit fullscreen.
* Automatically converting URsource URL to absolute for Flash fallback.
* Created new 'loadedalldata' event for when the source is completely downloaded
* Improved player.destroy(). Now removes elements and references.
* Refactored API to be more immediately available.
---- 3.2.1 / 2012-04-06 / options-width-fix ------------------------------------
* Fixed setting width/height with javascript options
---- 3.2.2 / 2012-05-02 / multiple-control-fades-fix ---------------------------
* Fixed error with multiple controls fading listeners
+203
Ver Arquivo
@@ -0,0 +1,203 @@
require 'rubygems'
require 'yaml'
require 'httparty'
namespace :build do
desc "Build version for current '/c/' CDN copy and locked in version"
task :current do
Rake::Task["build:source"].execute
cdn_version_num = "#{version['major']}.#{version['minor']}"
['c', cdn_version_num].each do |vsn|
Rake::Shell["mkdir dist/#{vsn}"]
File.open("dist/#{vsn}/video.js", "w+") do |file|
file.puts File.read("dist/video.min.js").sub('GENERATED_CDN_VSN', vsn)
end
Rake::Shell["cp dist/video-js.min.css dist/#{vsn}/video-js.css"]
Rake::Shell["cp dist/video-js.swf dist/#{vsn}/video-js.swf"]
Rake::Shell["cp dist/video-js.png dist/#{vsn}/video-js.png"]
Rake::Shell["cp dist/demo.html dist/#{vsn}/demo.html"]
Rake::Shell["cp dist/captions.vtt dist/#{vsn}/captions.vtt"]
end
Rake::Shell["mkdir dist/video-js"]
File.open("dist/video-js/video.min.js", "w+") do |file|
file.puts File.read("dist/video.min.js").sub('GENERATED_CDN_VSN', cdn_version_num)
end
File.open("dist/video-js/video.js", "w+") do |file|
file.puts File.read("dist/video.js").sub('GENERATED_CDN_VSN', cdn_version_num)
end
Rake::Shell["cp dist/video-js.min.css dist/video-js/video-js.min.css"]
Rake::Shell["cp dist/video-js.css dist/video-js/video-js.css"]
Rake::Shell["cp dist/video-js.swf dist/video-js/video-js.swf"]
Rake::Shell["cp dist/video-js.png dist/video-js/video-js.png"]
Rake::Shell["cp dist/demo.html dist/video-js/demo.html"]
Rake::Shell["cp dist/captions.vtt dist/video-js/captions.vtt"]
Rake::Shell["cd dist && zip -r video-js-#{version_number}.zip video-js && cd .."]
if `git name-rev --name-only HEAD`.strip != 'stable'
Rake::Log["*** WARNING: NOT ON STABLE BRANCH!!! ***"]
end
end
desc "Build source files for packaging"
task :source do
Rake::Log["Building Version: " << version_number]
if File.exist?("dist")
Rake::Shell["rm -r dist"]
end
# Make distribution folder
Rake::Shell["mkdir dist"]
Rake::Log["Combining source files"]
combined = ""
first_files = [ '_begin.js', 'core.js', 'lib.js' ]
first_files.each do |item|
Rake::Log[item]
combined << File.read("src/#{item}")
end
Dir.foreach('src') do |item|
next if (['.', '..', '.DS_Store', 'setup.js', '_end.js'] + first_files).include? item
combined << File.read("src/#{item}")
end
# combined << File.read("flash/swfobject.js")
combined << File.read("src/setup.js")
combined << File.read("src/_end.js")
Rake::Log["Adding version number"]
combined = combined.gsub('GENERATED_AT_BUILD', version_number)
File.open('dist/video.js', "w+") do |file|
file.puts "" << combined
end
Rake::Log["Copying CSS and updated version"]
File.open('dist/video-js.css', "w+") do |file|
file.puts File.read("design/video-js.css").gsub('GENERATED_AT_BUILD', version_number)
end
Rake::Log["Copying suppporting files"]
Rake::Shell["cp design/video-js.png dist/video-js.png"]
Rake::Shell["cp flash/video-js.swf dist/video-js.swf"]
Rake::Shell["cp build/release-files/README.md dist/README.md"]
Rake::Shell["cp build/release-files/demo.html dist/demo.html"]
Rake::Shell["cp build/release-files/captions.vtt dist/captions.vtt"]
Rake::Shell["cp LGPLv3-LICENSE.txt dist/LGPLv3-LICENSE.txt"]
Rake::Log["Minimizing JavaScript"]
Rake::Shell["java -jar build/lib/yuicompressor-2.4.7.jar dist/video.js -o dist/video.min.js"]
Rake::Log["Minimizing CSS"]
Rake::Shell["java -jar build/lib/yuicompressor-2.4.7.jar dist/video-js.css -o dist/video-js.min.css"]
Rake::Log[version_number << " Built"]
end
desc "Build list of source files for easy inclusion in projects"
task :js_source do
File.open("dev/source-list.js", "w+") do |file|
file.puts "var vjsSourceList = [];"
src_array = ["src/core", "src/lib"]
last = ["src/setup"] # "flash/swfobject",
exclude = [".", "..", ".DS_Store", "_end.js", "_begin.js"]
Dir.foreach('src') do |item|
next if exclude.include? item
item_name = "src/" << item.sub(".js", "")
next if (src_array + last).include? item_name
src_array << item_name
end
src_array = src_array + last
src_array.each do |item|
file.puts "vjsSourceList.push('#{item}')"
end
# file.puts "vjsSourceList.push('src/#{item.sub(".js", "")}')"
# file.puts "vjsSourceList.push('flash/swfobject.js')"
end
end
desc "Build list of source files for easy inclusion in projects"
task :source_html do
File.open("dev/source-list.html", "w+") do |file|
file.puts "<!-- Video.js Source Files -->"
src_array = ["src/core", "src/lib"]
last = ["src/setup"] # "flash/swfobject",
exclude = [".", "..", ".DS_Store", "_end.js", "_begin.js"]
Dir.foreach('src') do |item|
next if exclude.include? item
item_name = "src/" << item.sub(".js", "")
next if (src_array + last).include? item_name
src_array << item_name
end
src_array = src_array + last
src_array.each do |item|
file.puts "<script src='#{item}.js'></script>"
end
# file.puts "vjsSourceList.push('src/#{item.sub(".js", "")}')"
# file.puts "vjsSourceList.push('flash/swfobject.js')"
file.puts "<!-- END Video.js Source Files -->"
end
end
end
def version
YAML.load(File.read("VERSION.yml"))
end
def version_number
"#{version['major']}.#{version['minor']}.#{version['patch']}"
end
module Rake
class Shell
def self.[](command)
output = %x[#{command}]
if $?.to_i > 0
puts "-----> Process aborted"
puts " Exit status: #{$?}"
exit($?.to_i)
end
puts output
end
end
class Log
def self.[](message)
puts "-----> #{message.split("\n").join("\n ")}"
end
end
end
+2 -2
Ver Arquivo
@@ -1,4 +1,4 @@
---
minor: 2
major: 3
patch: 6
minor: 0
patch: 2
+1 -1
Ver Arquivo
@@ -29,7 +29,7 @@ cat src/controls.js >> dist/video.js
cat src/tracks.js >> dist/video.js
# h5swf temporarily requires swfobject
cat flash/swfobject.js >> dist/video.js
# cat flash/swfobject.js >> dist/video.js
cat src/setup.js >> dist/video.js
cat src/_end.js >> dist/video.js
+41
Ver Arquivo
@@ -0,0 +1,41 @@
WEBVTT
00:00.700 --> 00:04.110
Captions describe all relevant audio for the hearing impaired.
[ Heroic music playing for a seagull ]
00:04.500 --> 00:05.000
[ Splash!!! ]
00:05.100 --> 00:06.000
[ Sploosh!!! ]
00:08.000 --> 00:09.225
[ Splash...splash...splash splash splash ]
00:10.525 --> 00:11.255
[ Splash, Sploosh again ]
00:13.500 --> 00:14.984
Dolphin: eeeEEEEEeeee!
00:14.984 --> 00:16.984
Dolphin: Squawk! eeeEEE?
00:25.000 --> 00:28.284
[ A whole ton of splashes ]
00:29.500 --> 00:31.000
Mine. Mine. Mine.
00:34.300 --> 00:36.000
Shark: Chomp
00:36.800 --> 00:37.900
Shark: CHOMP!!!
00:37.861 --> 00:41.193
EEEEEEOOOOOOOOOOWHALENOISE
00:42.593 --> 00:45.611
[ BIG SPLASH ]
+8 -1
Ver Arquivo
@@ -3,11 +3,17 @@
<head>
<title>Video.js | HTML5 Video Player</title>
<!-- Chang URLs to wherever Video.js files will be hosted -->
<link href="video-js.css" rel="stylesheet" type="text/css">
<!-- video.js must be in the <head> for older IEs to work. -->
<script src="video.js"></script>
<!-- Unless using the CDN hosted version, update the URL to the Flash SWF -->
<script>
_V_.options.flash.swf = "video-js.swf";
</script>
</head>
<body>
@@ -17,6 +23,7 @@
<source src="http://video-js.zencoder.com/oceans-clip.mp4" type='video/mp4' />
<source src="http://video-js.zencoder.com/oceans-clip.webm" type='video/webm' />
<source src="http://video-js.zencoder.com/oceans-clip.ogv" type='video/ogg' />
<track kind="captions" src="captions.vtt" srclang="en" label="English" />
</video>
</body>
-7
Ver Arquivo
@@ -1,7 +0,0 @@
body { background-color: #ccc; font-size: 11px; font-family: 'Helvetica Neue', helvetica, arial; }
#main { width: 1000px; margin: 10px auto 0; }
td, th { text-align: left; vertical-align: top; }
td.info-col { width: 240px; }
td.data { text-align: right; }
span.na { color: red; }
span.undefined { color: #999; }
-75
Ver Arquivo
@@ -1,75 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>HTML5 Video Player</title>
<link rel="stylesheet" href="../design/video-js.css" type="text/css">
<!--[if lt IE 9]>
<script>
document.createElement("video"); // HTML5 Shiv. Must be in <head>.
</script>
<![endif]-->
<!--[if IE]>
<script src="https://getfirebug.com/firebug-lite.js" type="text/javascript" charset="utf-8"></script>
<![endif]-->
<script src="../src/core.js"></script>
<script src="../src/lib.js"></script>
<script src="../src/ecma.js"></script>
<script src="../src/json.js"></script>
<script src="../src/component.js"></script>
<script src="../src/player.js"></script>
<script src="../src/tech.js"></script>
<script src="../src/controls.js"></script>
<script src="../src/events.js"></script>
<script src="../src/tracks.js"></script>
<script src="../flash/swfobject.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<link rel="stylesheet" href="compare.css" type="text/css">
<script type="text/javascript" src="compare.js"></script>
</head>
<body>
<div id="main">
<table border="0" cellspacing="5" cellpadding="5">
<tr><th colspan="2">HTML5</th><th colspan="2">Flash</th></tr>
<tr>
<td colspan="2">
<video id="vid1" class="video-js vjs-default-skin" controls preload="auto" width="480" height="198"
poster="http://video-js.zencoder.com/oceans-clip.png">
<source src="http://video-js.zencoder.com/oceans-clip.mp4" type='video/mp4'>
<source src="http://video-js.zencoder.com/oceans-clip.webm" type='video/webm'>
</video>
</td>
<td colspan="2">
<video id="vid2" class="video-js vjs-default-skin" controls preload="auto" width="480" height="198"
poster="http://video-js.zencoder.com/oceans-clip.png">
<source src="http://video-js.zencoder.com/oceans-clip.mp4" type='video/mp4'>
<!-- <source src="http://video-js.zencoder.com/oceans-clip.webm" type='video/webm'> -->
</video>
</td>
</tr>
<tr>
<td class="info-col">
<table id="html5_props" border="0" cellspacing="0" cellpadding="0"></table>
</td>
<td class="info-col">
<div id="html5_events"></div>
</td>
<td class="info-col">
<table id="flash_props" border="0" cellspacing="0" cellpadding="0"></table>
</td>
<td class="info-col">
<div id="flash_events"></div>
</td>
</tr>
</table>
</div>
</body>
</html>
-70
Ver Arquivo
@@ -1,70 +0,0 @@
_V_.options.flash.swf = "../flash/video-js.swf";
_V_.options.flash.swf = "http://andylemay.com/dev/videojs/VideoJS.swf";
$(function(){
var tech, i, tname, player,
techList = ["html5","flash"],
props = "error,currentSrc,networkState,buffered,readyState,seeking,initialTime,duration,startOffsetTime,paused,played,seekable,ended,videoWidth,videoHeight,textTracks,preload,currentTime,defaultPlaybackRate,playbackRate,autoplay,loop,controls,volume,muted,defaultMuted,poster".split(","),
methods = "play,pause,src,load,canPlayType,addTextTrack",
notUsed = "mediaGroup,controller,videoTracks,audioTracks,defaultPlaybackRate";
for (i=0; i < techList.length; i++) {
tech = techList[i];
tname = tech.toLowerCase();
player = _V_("vid"+(i+1), { "techOrder":[tech] });
_V_.each(_V_.html5.events, function(evt){
player.addEvent(evt, _V_.proxy(tname, function(evt){
var eventsId = "#"+this+"_events",
type = evt.type,
prev = $(eventsId+" div").first();
if (prev && prev.html() && prev.html().indexOf(type + " ") === 0) {
var countSpan = prev.children(".count");
countSpan.html(parseInt(countSpan.html() || 1) + 1);
} else {
$("#"+this+"_events").prepend("<div>" + evt.type + " <span class='count'></span></div>");
}
}));
});
var propTable = $("#"+tname+"_props");
_V_.each(props, function(prop){
propTable.append("<tr><th>"+prop+"</th><td id='"+tname+prop+"' class='data'></td></tr>")
});
setInterval(_V_.proxy(player, function(){
_V_.each(props, _V_.proxy(this, function(prop){
var result = ""
try {
result = this[prop]();
if (result === false) result = "false";
if (result === true) result = "true";
if (result === "") result = "''";
if (result === null) result = "<span class='undefined'>null</span>";
if (result === undefined) result = "<span class='undefined'>undefined</span>";
if (typeof result.start == "function") {
var newResult = "", i;
if (result.length > 0) {
for (i=0;i<result.length;i++) {
newResult += "l:"+result.length+" s:"+result.start(i)+" e:"+result.end(i);
}
} else {
newResult = "-";
}
result = newResult;
// result = result.toString();
// result = (result.length > 0) ? "s:"+result.start(0)+" e:"+result.end(0) : "-";
}
} catch(e) {
result = "<span class='na'>N/A</span>";
}
$("#"+this.techName+prop).html(result);
}));
}), 500);
};
});
+109 -15
Ver Arquivo
@@ -1,7 +1,9 @@
/*
VideoJS Default Styles (http://videojs.com)
Version 3.0
Version GENERATED_AT_BUILD
*/
/*
REQUIRED STYLES (be careful overriding)
================================================================================ */
/* When loading the player, the video tag is replaced with a DIV,
@@ -25,6 +27,9 @@ REQUIRED STYLES (be careful overriding)
/* Playback technology elements expand to the width/height of the containing div. <video> or <object> */
.video-js .vjs-tech { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
/* Fix for Firefox 9 fullscreen (only if it is enabled). Not needed when checking fullScreenEnabled. */
.video-js:-moz-full-screen { position: absolute; }
/* Fullscreen Styles */
body.vjs-full-window {
padding: 0; margin: 0;
@@ -43,12 +48,43 @@ body.vjs-full-window {
margin: 0 auto; padding: 0; cursor: pointer;
/* Scale with the size of the player div. Works when poster is vertically shorter, but stretches when it's less wide. */
position: relative; width: 100%; max-height: 100%;
position: relative; width: 100%; max-height: 100%;
}
/* Text Track Styles */
/* Overall track holder for both captions and subtitles */
.video-js .vjs-text-track-display { text-align: center; position: absolute; bottom: 4em; left: 1em; right: 1em; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; }
/* Individual tracks */
.video-js .vjs-text-track {
display: none; color: #fff; font-size: 1.4em; text-align: center; margin-bottom: 0.1em;
/* Transparent black background, or fallback to all black (IE6) */
background: rgb(0, 0, 0); background: rgba(0, 0, 0, 0.50);
}
.video-js .vjs-subtitles { color: #fff; }
.video-js .vjs-captions { color: #fc6; }
.vjs-tt-cue { display: block; }
/* Subtiles Styles */
.video-js .vjs-subtitles { color: #fff; font-size: 20px; text-align: center; position: absolute; bottom: 40px; left: 0; right: 0; }
/* Fading sytles, used to fade control bar. */
.vjs-fade-in {
visibility: visible !important; /* Needed to make sure things hide in older browsers too. */
opacity: 1 !important;
-webkit-transition: visibility 0s linear 0s, opacity 0.3s linear;
-moz-transition: visibility 0s linear 0s, opacity 0.3s linear;
-ms-transition: visibility 0s linear 0s, opacity 0.3s linear;
-o-transition: visibility 0s linear 0s, opacity 0.3s linear;
transition: visibility 0s linear 0s, opacity 0.3s linear;
}
.vjs-fade-out {
visibility: hidden !important;
opacity: 0 !important;
-webkit-transition: visibility 0s linear 1.5s,opacity 1.5s linear;
-moz-transition: visibility 0s linear 1.5s,opacity 1.5s linear;
-ms-transition: visibility 0s linear 1.5s,opacity 1.5s linear;
-o-transition: visibility 0s linear 1.5s,opacity 1.5s linear;
transition: visibility 0s linear 1.5s,opacity 1.5s linear;
}
/* DEFAULT SKIN (override in another file to create new skins)
================================================================================
@@ -60,8 +96,6 @@ so you can upgrade to newer versions easier. You can remove all these styles by
position: absolute;
bottom: 0; /* Distance from the bottom of the box/video. Keep 0. Use height to add more bottom margin. */
left: 0; right: 0; /* 100% width of div */
opacity: 0.85;
display: none; /* Start hidden */
margin: 0; padding: 0; /* Controls are absolutely position, so no padding necessary */
height: 2.6em; /* Including any margin you want above or below control items */
color: #fff; border-top: 1px solid #404040;
@@ -78,12 +112,10 @@ so you can upgrade to newer versions easier. You can remove all these styles by
/*filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#242424', endColorstr='#171717',GradientType=0 );*/ /* IE6-9 */
background: linear-gradient(top, #242424 50%,#1f1f1f 50%,#171717 100%); /* W3C */
/* Fade-in using CSS Transitions */
-webkit-transition: opacity 0.3s linear;
-moz-transition: opacity 0.3s linear;
-o-transition: opacity 0.3s linear;
-ms-transition: opacity 0.3s linear;
transition: opacity 0.3s linear;
/* Start hidden and with 0 opacity. Opacity is used to fade in modern browsers. */
/* Can't use display block to hide initially because widths of slider handles aren't calculated and avaialbe for positioning correctly. */
visibility: hidden;
opacity: 0;
}
/* General styles for individual controls. */
@@ -95,6 +127,7 @@ so you can upgrade to newer versions easier. You can remove all these styles by
.vjs-default-skin .vjs-control:focus {
outline: 0;
/* background-color: #555;*/
}
/* Hide control text visually, but have it available for screenreaders: h5bp.com/v */
@@ -297,8 +330,8 @@ so you can upgrade to newer versions easier. You can remove all these styles by
---------------------------------------------------------*/
.vjs-default-skin .vjs-big-play-button {
display: block; /* Start hidden */ z-index: 2;
position: absolute; top: 50%; left: 50%; width: 8.0em; height: 8.0em; margin: -43px 0 0 -43px; text-align: center; vertical-align: center; cursor: pointer !important;
border: 0.3em solid #fff; opacity: 0.95;
position: absolute; top: 50%; left: 50%; width: 8.0em; height: 8.0em; margin: -42px 0 0 -42px; text-align: center; vertical-align: center; cursor: pointer !important;
border: 0.2em solid #fff; opacity: 0.95;
-webkit-border-radius: 25px; -moz-border-radius: 25px; border-radius: 25px;
background: #454545;
@@ -402,4 +435,65 @@ div.vjs-loading-spinner .ball7 { opacity: 0.87; position:absolute; left: 0px; to
border-radius: 13px; -webkit-border-radius: 13px; -moz-border-radius: 13px; border: 1px solid #ccc; }
div.vjs-loading-spinner .ball8 { opacity: 1.00; position:absolute; left: 6px; top: 6px; width: 13px; height: 13px; background: #fff;
border-radius: 13px; -webkit-border-radius: 13px; -moz-border-radius: 13px; border: 1px solid #ccc; }
border-radius: 13px; -webkit-border-radius: 13px; -moz-border-radius: 13px; border: 1px solid #ccc; }
/* Menu Buttons (Captions/Subtitles/etc.)
-------------------------------------------------------------------------------- */
.vjs-default-skin .vjs-menu-button {
float: right; margin: 0.2em 0.5em 0 0; padding: 0; width: 3em; height: 2em; cursor: pointer !important;
border: 1px solid #111; -moz-border-radius: 0.3em; -webkit-border-radius: 0.3em; border-radius: 0.3em;
background: #4d4d4d;
background: -moz-linear-gradient(top, #4d4d4d 0%, #3f3f3f 50%, #333333 50%, #252525 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4d4d4d), color-stop(50%,#3f3f3f), color-stop(50%,#333333), color-stop(100%,#252525));
background: -webkit-linear-gradient(top, #4d4d4d 0%,#3f3f3f 50%,#333333 50%,#252525 100%);
background: -o-linear-gradient(top, #4d4d4d 0%,#3f3f3f 50%,#333333 50%,#252525 100%);
background: -ms-linear-gradient(top, #4d4d4d 0%,#3f3f3f 50%,#333333 50%,#252525 100%);
background: linear-gradient(top, #4d4d4d 0%,#3f3f3f 50%,#333333 50%,#252525 100%);
}
/* Button Icon */
.vjs-default-skin .vjs-menu-button div { background: url('video-js.png') 0px -75px no-repeat; width: 16px; height: 16px; margin: 0.2em auto 0; padding: 0; }
/* Button Pop-up Menu */
.vjs-default-skin .vjs-menu-button ul {
display: none; /* Start hidden. Hover will show. */
opacity: 0.8;
padding: 0; margin: 0;
position: absolute; width: 10em; bottom: 2em; max-height: 15em;
left: -3.5em; /* Width of menu - width of button / 2 */
background-color: #111;
border: 2px solid #333;
-moz-border-radius: 0.7em; -webkit-border-radius: 1em; border-radius: .5em;
-webkit-box-shadow: 0 2px 4px 0 #000; -moz-box-shadow: 0 2px 4px 0 #000; box-shadow: 0 2px 4px 0 #000;
overflow: auto;
}
.vjs-default-skin .vjs-menu-button:focus ul,
.vjs-default-skin .vjs-menu-button:hover ul { display: block; list-style: none; }
.vjs-default-skin .vjs-menu-button ul li { list-style: none; margin: 0; padding: 0.3em 0 0.3em 20px; line-height: 1.4em; font-size: 1.2em; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; text-align: left; }
.vjs-default-skin .vjs-menu-button ul li.vjs-selected { text-decoration: underline; background: url('video-js.png') -125px -50px no-repeat; }
.vjs-default-skin .vjs-menu-button ul li:focus,
.vjs-default-skin .vjs-menu-button ul li:hover,
.vjs-default-skin .vjs-menu-button ul li.vjs-selected:focus,
.vjs-default-skin .vjs-menu-button ul li.vjs-selected:hover { background-color: #ccc; color: #111; outline: 0; }
.vjs-default-skin .vjs-menu-button ul li.vjs-menu-title {
text-align: center; text-transform: uppercase; font-size: 1em; line-height: 2em; padding: 0; margin: 0 0 0.3em 0;
color: #fff; font-weight: bold;
cursor: default;
background: #4d4d4d;
background: -moz-linear-gradient(top, #4d4d4d 0%, #3f3f3f 50%, #333333 50%, #252525 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4d4d4d), color-stop(50%,#3f3f3f), color-stop(50%,#333333), color-stop(100%,#252525));
background: -webkit-linear-gradient(top, #4d4d4d 0%,#3f3f3f 50%,#333333 50%,#252525 100%);
background: -o-linear-gradient(top, #4d4d4d 0%,#3f3f3f 50%,#333333 50%,#252525 100%);
background: -ms-linear-gradient(top, #4d4d4d 0%,#3f3f3f 50%,#333333 50%,#252525 100%);
background: linear-gradient(top, #4d4d4d 0%,#3f3f3f 50%,#333333 50%,#252525 100%);
}
/* Subtitles Button */
.vjs-default-skin .vjs-captions-button div { background-position: -25px -75px; }
.vjs-default-skin .vjs-chapters-button div { background-position: -100px -75px; }
.vjs-default-skin .vjs-chapters-button ul { width: 20em; left: -8.5em; /* Width of menu - width of button / 2 */ }
Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 7.3 KiB

Depois

Largura:  |  Altura:  |  Tamanho: 8.0 KiB

-2
Ver Arquivo
@@ -21,8 +21,6 @@
<script src="src/controls.js"></script>
<script src="src/events.js"></script>
<script src="src/tracks.js"></script>
<script src="flash/swfobject.js"></script>
<script src="src/setup.js"></script>
+31
Ver Arquivo
@@ -0,0 +1,31 @@
// Attempting to create a portable script that loads source files in order. So we can change which files are included and have it change multiple places.
var vjsSourceList = ["require",
'order!../../src/core.js',
'order!../../src/lib.js',
'order!../../src/component.js',
'order!../../src/controls.js',
'order!../../src/ecma.js',
'order!../../src/events.js',
'order!../../src/json.js',
'order!../../src/player.js',
'order!../../src/tech.js',
'order!../../src/tracks.js',
'order!../../flash/swfobject.js',
'order!../../src/setup.js'
];
// Not going to be used in production, so eval ok.
require([vjsSourceList])
// var requireEval = '';
// for (var i=0; i < vjsSourceList.length; i++) {
// requireEval += 'require(["order!'+vjsSourceList[i]+'"], function() { ';
// }
//
// requireEval += 'var libsLoaded = true;'
//
// for (var i=0; i < vjsSourceList.length; i++) {
// requireEval += ' }); ';
// }
//
// eval(requireEval);
+2144
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+13
Ver Arquivo
@@ -0,0 +1,13 @@
<!-- Video.js Source Files -->
<script src='src/core.js'></script>
<script src='src/lib.js'></script>
<script src='src/component.js'></script>
<script src='src/controls.js'></script>
<script src='src/ecma.js'></script>
<script src='src/events.js'></script>
<script src='src/json.js'></script>
<script src='src/player.js'></script>
<script src='src/tech.js'></script>
<script src='src/tracks.js'></script>
<script src='src/setup.js'></script>
<!-- END Video.js Source Files -->
+12 -12
Ver Arquivo
@@ -1,13 +1,13 @@
var vjsSourceList = [];
vjsSourceList.push('_begin.js')
vjsSourceList.push('_end.js')
vjsSourceList.push('component.js')
vjsSourceList.push('controls.js')
vjsSourceList.push('core.js')
vjsSourceList.push('ecma.js')
vjsSourceList.push('events.js')
vjsSourceList.push('json.js')
vjsSourceList.push('lib.js')
vjsSourceList.push('player.js')
vjsSourceList.push('tech.js')
vjsSourceList.push('tracks.js')
vjsSourceList.push('src/core')
vjsSourceList.push('src/lib')
vjsSourceList.push('src/component')
vjsSourceList.push('src/controls')
vjsSourceList.push('src/ecma')
vjsSourceList.push('src/events')
vjsSourceList.push('src/json')
vjsSourceList.push('src/player')
vjsSourceList.push('src/tech')
vjsSourceList.push('src/tracks')
vjsSourceList.push('flash/swfobject')
vjsSourceList.push('src/setup')
+150 -105
Ver Arquivo
@@ -12,13 +12,13 @@ The Video.js API allows you to interact with the video through Javascript, wheth
Referencing the Player
----------------------
To use the API functions, you need access to the player object. Luckily this is easy to get. You just need to make sure your video tag has an ID. The example embed code has an ID of "example_video_1". If you have multiple videos on one page, make sure every video tag has a unique ID.
To use the API functions, you need access to the player object. Luckily this is easy to get. You just need to make sure your video tag has an ID. The example embed code has an ID of "example\_video_1". If you have multiple videos on one page, make sure every video tag has a unique ID.
{% highlight javascript %}
<code type="javascript">
var myPlayer = _V_("example_video_1");
var myPlayer = _V_("example_video_1");
{% endhighlight %}
</code>
(If the player hasn't been initialized yet via the data-setup attribute or another method, this will also initialize the player.)
@@ -26,7 +26,7 @@ Wait Until the Player is Ready
------------------------------
The time it takes Video.js to set up the video and API will vary depending on the playback technology being used (HTML5 will often be much faster to load than Flash). For that reason we want to use the player's 'ready' function to trigger any code that requires the player's API.
{% highlight javascript %}
<code type="javascript">
_V_("example_video_1").ready(function(){
@@ -37,192 +37,233 @@ The time it takes Video.js to set up the video and API will vary depending on th
});
{% endhighlight %}
</code>
API Methods
-----------
Now that you have access to a ready player, you can control the video or respond to video events using the following functions. The Video.js API function names follow the [HTML5 media API](http://www.w3.org/TR/html5/video.html). The main difference is that attributes which you would get or set on a video element using the equals sign ( `myVideoElement.currentTime = "120";` ), you would use a function argument syntax for Video.js ( `myPlayer.currentTime(120);` )
Now that you have access to a ready player, you can control the video, get values, or respond to video events using the following functions. The Video.js API function names follow the [HTML5 media API](http://www.w3.org/TR/html5/video.html). The main difference is that attributes which you would get or set on a video element using the equals sign ( `myVideoElement.currentTime = "120";` ), you would use a function argument syntax for Video.js ( `myPlayer.currentTime(120);` )
### play()
Start video playback. Returns the player object.
### play() ###
Start video playback. Returns the player object.
{% highlight javascript %}
<code type="javascript">
myPlayer.play();
myPlayer.play();
{% endhighlight %}
</code>
### pause() ###
Pause the video playback. Returns the player object
### pause()
Pause the video playback. Returns: the player object
<code type="javascript">
{% highlight javascript %}
myPlayer.pause();
myPlayer.pause();
</code>
{% endhighlight %}
### src(newSource) ###
The source function updates the video source. There are three types of variables you can pass as the argument.
### currentTime()
Returns the current time of the video in seconds.
**URL String**: A URL to the the video file. Use this method if you're sure the current playback technology (HTML5/Flash) can support the source you provide. Currently only MP4 files can be used in both HTML5 and Flash.
<code type="javascript">
{% highlight javascript %}
myPlayer.src("http://www.example.com/path/to/video.mp4");
var whereYouAt = myPlayer.currentTime();
</code>
{% endhighlight %}
**Source Object (or element):** A javascript object containing information about the source file. Use this method if you want the player to determine if it can support the file using the type information.
<code type="javascript">
### currentTime(seconds) // Type: Integer or Float
Seek to the supplied time (seconds).
Returns the player object.
myPlayer.src({ type: "video/mp4", src: "http://www.example.com/path/to/video.mp4" });
{% highlight javascript %}
</code>
myPlayer.currentTime(120); // 2 minutes into the video
**Array of Source Objects:** To provide multiple versions of the source so that it can be played using HTML5 across browsers you can use an array of source objects. Video.js will detect which version is supported and load that file.
<code type="javascript">
{% endhighlight %}
myPlayer.src([
{ type: "video/mp4", src: "http://www.example.com/path/to/video.mp4" },
{ type: "video/webm", src: "http://www.example.com/path/to/video.webm" },
{ type: "video/ogg", src: "http://www.example.com/path/to/video.ogv" }
]);
</code>
Returns the player object.
### currentTime() ###
Returns the current time of the video in seconds.
<code type="javascript">
var whereYouAt = myPlayer.currentTime();
</code>
### duration()
Returns the length in time of the video in seconds. NOTE: The video must have started loading before the duration can be known, and in the case of Flash, may not be known until the video starts playing.
### currentTime(seconds) // Type: Integer or Float ###
Seek to the supplied time (seconds). Returns the player object.
<code type="javascript">
myPlayer.currentTime(120); // 2 minutes into the video
</code>
{% highlight javascript %}
### duration() ###
Returns the length in time of the video in seconds. NOTE: The video must have started loading before the duration can be known, and in the case of Flash, may not be known until the video starts playing.
var howLongIsThis = myPlayer.duration();
<code type="javascript">
{% endhighlight %}
var howLongIsThis = myPlayer.duration();
### buffered()
Returns a [TimeRange](http://videojs.com/docs/glossary.html#timerange) with sections of the video that have been downloaded. If you just want the percent of the video that's been downloaded, use bufferedPercent.
</code>
{% highlight javascript %}
var whatHasBeenBuffered = myPlayer.buffered();
### buffered() ###
Returns a [TimeRange](http://videojs.com/docs/glossary.html#timerange) object with sections of the video that have been downloaded. If you just want the percent of the video that's been downloaded, use bufferedPercent.
{% endhighlight %}
<code type="javascript">
### bufferedPercent()
Returns the percent (as a decimal) of the video that's been downloaded.
var bufferedTimeRange = myPlayer.buffered(),
{% highlight javascript %}
// Number of different ranges of time have been buffered. Usually 1.
numberOfRanges = bufferedTimeRange.length,
var howMuchIsDownloaded = myPlayer.bufferedPercent();
// Time in seconds when the first range starts. Usually 0.
firstRangeStart = bufferedTimeRange.start(0),
// Time in seconds when the first range ends
firstRangeEnd = bufferedTimeRange.end(0),
{% endhighlight %}
// Length in seconds of the first time range
firstRangeLength = firstRangeEnd - firstRangeStart;
### volume()
Returns the current volume of the video as a percent in decimal form. 0 is off (muted), 1.0 is all the way up, 0.5 is half way.
</code>
{% highlight javascript %}
var howLoudIsIt = myPlayer.volume();
### bufferedPercent() ###
Returns the percent (as a decimal) of the video that's been downloaded. 0 means none, 1 means all.
{% endhighlight %}
<code type="javascript">
### volume(percentAsDecimal)
Set the volume to the supplied percent (as a decimal between 0 and 1).
var howMuchIsDownloaded = myPlayer.bufferedPercent();
{% highlight javascript %}
</code>
myPlayer.volume(0.5); // Set volume to half
{% endhighlight %}
### volume() ###
Returns the current volume of the video as a percent in decimal form. 0 is off (muted), 1.0 is all the way up, 0.5 is half way.
### width()
Returns the current width of the video in pixels.
<code type="javascript">
{% highlight javascript %}
var howLoudIsIt = myPlayer.volume();
</code>
### volume(percentAsDecimal) ###
Set the volume to the supplied percent (as a decimal between 0 and 1).
<code type="javascript">
myPlayer.volume(0.5); // Set volume to half
</code>
### width() ###
Returns the current width of the video in pixels.
<code type="javascript">
var howWideIsIt = myPlayer.width();
{% endhighlight %}
### width(pixels)
Change the width of the video to the supplied width in pixels.
Returns the player object
{% highlight javascript %}
myPlayer.width(640);
{% endhighlight %}
</code>
### height()
Returns the current height of the video in pixels.
### width(pixels) ###
Change the width of the video to the supplied width in pixels. Returns the player object
{% highlight javascript %}
<code type="javascript">
var howTallIsIt = myPlayer.height();
myPlayer.width(640);
{% endhighlight %}
</code>
### height(pixels)
Change the height of the video to the supplied height in pixels.
Returns the player object
### height() ###
Returns the current height of the video in pixels.
{% highlight javascript %}
<code type="javascript">
myPlayer.height(480);
var howTallIsIt = myPlayer.height();
{% endhighlight %}
</code>
### size(width, height)
Changes the width and height of the video to the supplied width and height. This is more efficient if you're changing both width and height (only triggers the player's resize event once). Returns the player object.
### height(pixels) ###
Change the height of the video to the supplied height in pixels. Returns the player object
{% highlight javascript %}
<code type="javascript">
myPlayer.size(640,480);
myPlayer.height(480);
{% endhighlight %}
</code>
### requestFullScreen()
Increase the size of the video to full screen. In some browsers, full screen is not supported natively, so it enters full window mode, where the fills the browser window. In browsers that support native full screen, typically the browser's default controls will be shown, and not the Video.js custom skin. In full window mode, the Video.js controls and skin will always be used.
Returns the player object.
### size(width, height) ###
Changes the width and height of the video to the supplied width and height. This is more efficient if you're changing both width and height (only triggers the player's resize event once). Returns the player object.
{% highlight javascript %}
<code type="javascript">
myPlayer.enterFullScreen();
myPlayer.size(640,480);
{% endhighlight %}
</code>
### cancelFullScreen()
Return the video to its normal size after having been in full screen mode.
Returns the player object.
### requestFullScreen() ###
Increase the size of the video to full screen. In some browsers, full screen is not supported natively, so it enters full window mode, where the video fills the browser window. In browsers and devices that support native full screen, sometimes the browser's default controls will be shown, and not the Video.js custom skin. This includes most mobile devices (iOS, Android) and older versions of Safari. Returns the player object.
{% highlight javascript %}
<code type="javascript">
myPlayer.exitFullScreen();
myPlayer.requestFullScreen();
{% endhighlight %}
</code>
### cancelFullScreen() ###
Return the video to its normal size after having been in full screen mode. Returns the player object.
<code type="javascript">
myPlayer.cancelFullScreen();
</code>
Events
------
You can attach event listeners to the player similarly to how you would for a video element.
{% highlight javascript %}
<code type="javascript">
var myFunc = function(){
// Do something when the event is fired
};
myPlayer.addEvent("eventName", myFunc);
{% endhighlight %}
var myFunc = function(){
var myPlayer = this;
// Do something when the event is fired
};
myPlayer.addEvent("eventName", myFunc);
</code>
You can also remove the listeners later.
{% highlight javascript %}
<code type="javascript">
myPlayer.removeEvent("eventName", myFunc);
myPlayer.removeEvent("eventName", myFunc);
{% endhighlight %}
</code>
### Event Types
@@ -231,6 +272,9 @@ List of player events you can add listeners for.
<table border="0" cellspacing="5" cellpadding="5">
<tr><th>Name</th><th>Description</th></tr>
<tr><td>loadstart</td><td>Fired when the user agent begins looking for media data.</td></tr>
<tr><td>loadedmetadata</td><td>Fired when the player has initial duration and dimension information.</td></tr>
<tr><td>loadeddata</td><td>Fired when the player has downloaded data at the current playback position.</td></tr>
<tr><td>loadedalldata</td><td>Fired when the player has finished downloading the source data.</td></tr>
<tr><td>play</td><td>Fired whenever the media begins or resumes playback.</td></tr>
<tr><td>pause</td><td>Fired whenever the media has been paused.</td></tr>
<tr><td>timeupdate</td><td>Fired when the current playback position has changed. During playback this is fired every 15-250 milliseconds, depnding on the playback technology in use.</td></tr>
@@ -240,4 +284,5 @@ List of player events you can add listeners for.
<tr><td>resize</td><td>Fired when the width and/or height of the video window changes.</td></tr>
<tr><td>volumechange</td><td>Fired when the volume changes.</td></tr>
<tr><td>error</td><td>Fired when there is an error in playback.</td></tr>
<tr><td>fullscreenchange</td><td>Fired when the player switches in or out of fullscreen mode.</td></tr>
</table>
+122 -13
Ver Arquivo
@@ -9,22 +9,131 @@ body_class: docs subpage
Options
=======
The Video.js emebed code is simply an HTML5 video tag with the video.js classes. So for many of the options you can use the standard tag attributes to set video.js options.
Setting Options
---------------
{% highlight html %}
<video controls autoplay preload="auto" ...>
{% endhighlight %}
The Video.js emebed code is simply an HTML5 video tag, so for many of the options you can use the standard tag attributes to set the options.
Alternatively, you can use the data-setup attribute to provide options in the JSON format. This is also how you would set options that aren't standard to the video tag.
<code type="html">
{% highlight html %}
<video data-setup='{ "controls": true, "autoplay": false, "preload": "auto" }'...>
{% endhighlight %}
<video controls autoplay preload="auto" ...>
Finally, if you're not using the data-setup attribute to trigger the player setup, you can pass in an object with the player options as the second argument in the setup function.
</code>
{% highlight javascript %}
_V_("myVideoID", { "controls": true, "autoplay": false, "preload": "auto" });
{% endhighlight %}
Alternatively, you can use the data-setup attribute to provide options in the [JSON](http://json.org/example.html) format. This is also how you would set options that aren't standard to the video tag.
(More options documentation coming soon.)
<code type="html">
<video data-setup='{ "controls": true, "autoplay": false, "preload": "auto" }'...>
</code>
Finally, if you're not using the data-setup attribute to trigger the player setup, you can pass in an object with the player options as the second argument in the javascript setup function.
<code type="javascript">
_V_("example_video_1", { "controls": true, "autoplay": false, "preload": "auto" });
</code>
Individual Options
------------------
> ### Note on Video Tag Attributes ###
> With HTML5 video tag attributes that can only be true or false (boolean), you simply include the attribute (no equals sign) to turn it on, or exclude it to turn it off. For example, to turn controls on:
>
> WRONG
> <code type="html">
>
> <video controls="true" ...>
>
> </code>
>
> RIGHT
> <code type="html">
>
> <video controls ...>
>
> </code>
>
> The biggest issue people run into is trying to set these values to false using false as the value (e.g. controls="false") which actually does the opposite and sets the value to true because the attribute is still included. If you need the attribute to include an equals sign for XHTML validation, you can set the attribute's value to the same as its name (e.g. controls="controls").
### controls ###
The controls option sets whether or not the player has controls that the user can interact with. Without controls the only way to start the video playing is with the autoplay attribute or through the API.
<code type="html">
<video controls ...>
or
{ "controls": true }
</code>
### autoplay ###
If autoplay is true, the video will start playing as soon as page is loaded (without any interaction from the user).
NOT SUPPORTED BY APPLE iOS DEVICES. Apple blocks the autoplay functionality in an effort to protect it's customers from unwillingly using a lot of their (often expensive) monthly data plans. A user touch/click is required to start the video in this case.
<code type="html">
<video autoplay ...>
or
{ "autoplay": true }
</code>
### preload ###
The preload attribute informs the browser whether or not the video data should begin downloading as soon as the video tag is loaded. The options are auto, metadata, and none.
'auto': Start loading the video immediately (if the browser agrees). Some mobile devices like iPhones and iPads will not preload the video in order to protect their users' bandwidth. This is why the value is called 'auto' and not something more final like 'true'.
'metadata': Load only the meta data of the video, which includes information like the duration and dimensions of the video.
'none': Don't preload any of the video data. This will wait until the user clicks play to begin downloading.
<code type="html">
<video preload ...>
or
{ "preload": "auto" }
</code>
### poster ###
The poster attribute sets the image that displays before the video begins playing. This is often a frame of the video or a custom title screen. As soon as the user clicks play the image will go away.
<code type="html">
<video poster="myPoster.jpg" ...>
or
{ "poster": "myPoster.jpg" }
</code>
### loop ###
The loop attribute causes the video to start over as soon as it ends. This could be used for a visual affect like clouds in the background.
<code type="html">
<video loop ...>
or
{ "loop": "true" }
</code>
### width ###
The width attribute sets the display width of the video.
<code type="html">
<video width="640" ...>
or
{ "width": 640 }
</code>
### height ###
The height attribute sets the display height of the video.
<code type="html">
<video height="480" ...>
or
{ "height": 480 }
</code>
+55 -18
Ver Arquivo
@@ -13,38 +13,75 @@ Video.js is pretty easy to set up. It can take a matter of seconds to get the pl
Step 1: Include the Video.js Javascript and CSS files in the head of your page.
------------------------------------------------------------------------------
You can download the Video.js source and host it on your own servers, or use the free CDN hosted version (thanks to Zencoder). It's often recommended now to put JavaScript before the end \</body\> tag instead of the head but Video.js includes an 'HTML5 Shiv', which needs to be in the \<head\> for older IE versions. If you
{% highlight html %}
You can download the Video.js source and host it on your own servers, or use the free CDN hosted version. It's often recommended now to put JavaScript before the end body tag (&lt;/body>) instead of the head (&lt;head>), but Video.js includes an 'HTML5 Shiv', which needs to be in the head for older IE versions to respect the video tag as a valid element.
<script src="http://vjs.zencdn.net/c/video.js"></script>
<link href="http://vjs.zencdn.net/c/video-js.css" rel="stylesheet">
> NOTE: If you're already using an HTML5 shiv like [Modernizr](http://modernizr.com/) you can include the Video.js JavaScript anywhere, however make sure your version of Modernizr includes the shiv for video.
{% endhighlight %}
### CDN Version ###
<code type="html">
<link href="http://vjs.zencdn.net/c/video-js.css" rel="stylesheet">
<script src="http://vjs.zencdn.net/c/video.js"></script>
</code>
### Self Hosted. ###
With the self hosted option you'll also want to update the location of the video-js.swf file.
<code type="html">
<link href="http://example.com/path/to/video-js.css" rel="stylesheet">
<script src="http://example.com/path/to/video.js"></script>
<script>
_V_.options.flash.swf = "http://example.com/path/to/video-js.swf"
</script>
</code>
Step 2: Add an HTML5 video tag to your page.
--------------------------------------------
Use the video tag as normal, with a few extra pieces for Video.js:
With Video.js you just use an HTML5 video tag to embed a video. Video.js will then read the tag and make it work in all browsers, not just ones that support HTML5 video. Beyond the basic markup, Video.js needs a few extra pieces.
1. The 'data-setup' Atrribute tells Video.js to automatically set up the video when the page is ready, and read any options (in JSON format) from the attribute (see ['options'](http://videojs.com/docs/options/)). There are other methods for initializing the player, but this is the easiest.
1. The 'data-setup' Atrribute tells Video.js to automatically set up the video when the page is ready, and read any options (in JSON format) from the attribute (see ['options'](http://videojs.com/docs/options.html)).
2. The 'id' Attribute: Should be used and unique for every video on the same page.
3. The 'class' attribute contains two classes:
- 'video-js' applies styles that are required for Video.js functionality, like fullscreen and subtitles.
- 'vjs-default-skin' applies the default skin to the HTML controls, and can be removed or overridden to create your own controls design.
Otherwise include/exclude attributes, settings, sources, and tracks exactly as you would for HTML5 video (see ['video-tag'](http://videojs.com/docs/video-tag.html)).
Otherwise include/exclude attributes, settings, sources, and tracks exactly as you would for HTML5 video.
{% highlight html %}
<code type="html">
<video id="example_video_1" class="video-js vjs-default-skin"
controls preload="auto" width="640" height="264"
poster="http://video-js.zencoder.com/oceans-clip.png"
data-setup='{"example_option":true}'>
<source src="http://video-js.zencoder.com/oceans-clip.mp4" type='video/mp4' />
<source src="http://video-js.zencoder.com/oceans-clip.webm" type='video/webm' />
<source src="http://video-js.zencoder.com/oceans-clip.ogv" type='video/ogg' />
</video>
<video id="example_video_1" class="video-js vjs-default-skin"
controls preload="auto" width="640" height="264"
poster="http://video-js.zencoder.com/oceans-clip.png"
data-setup='{"example_option":true}'>
<source src="http://video-js.zencoder.com/oceans-clip.mp4" type='video/mp4' />
<source src="http://video-js.zencoder.com/oceans-clip.webm" type='video/webm' />
<source src="http://video-js.zencoder.com/oceans-clip.ogv" type='video/ogg' />
</video>
{% endhighlight %}
</code>
Alternative Setup for Dynamically Loaded HTML
---------------------------------------------
If your web page or application loads the video tag dynamically (ajax, appendChild, etc.), so that it may not exist when the page loads, you'll want to manually set up the player instead of relying on the data-setup attribute. To do this, first remove the data-setup attribute from the tag so there's no confusion around when the player is initialized. Next, run the following javascript some time after the Video.js javascript library has loaded, and after the video tag has been loaded into the DOM.
<code type="javascript">
_V_("example_video_1", {}, function(){
// Player (this) is initialized and ready.
});
</code>
The first argument in the \_V_ function is the ID of your video tag. Replace it with your own.
The second argument is an options object. It allows you to set additional options like you can with the data-setup attribute.
The third argument is a 'ready' callback. Once Video.js has initialized it will call this function.
+235
Ver Arquivo
@@ -0,0 +1,235 @@
---
layout: docs
title: HTML5 Video Text Tracks (Subtitles, Captions, Chapters)
description: Video.js support for captions, subtitles, and chapters through the use of the HTML5 video track element.
body_id: tracks
body_class: docs subpage
---
Tracks
======
Text Tracks are a function of HTML5 video for providing time triggered text to the viewer. Video.js makes tracks work across all browsers. There are currently five types of tracks:
- **Subtitles**: Translations of the dialogue in the video for when audio is available but not understood. Subtitles are shown over the video.
- **Captions**: Transcription of the dialogue, sound effects, musical cues, and other audio information for when the viewer is deaf/hard of hearing, or the video is muted. Captions are also shown over the video.
- **Chapters**: Chapter titles that are used to create navigation within the video. Typically they're in the form of a list of chapters that the viewer can click on to go to a specific chapter.
- **Descriptions** (not supported yet): Text descriptions of what's happening in the video for when the video portion isn't available, because the viewer is blind, not using a screen, or driving and about to crash because they're trying to enjoy a video while driving. Descriptions are read by a screen reader or turned into a separate audio track.
- **Metadata** (not supported yet): Tracks that have data meant for javascript to parse and do something with. These aren't shown to the user.
Creating the Text File
----------------------
Timed text requires a text file in [WebVTT](http://dev.w3.org/html5/webvtt/) format. This format defines a list of "cues" that have a start time, and end time, and text to display. [Microsoft has a builder](http://ie.microsoft.com/testdrive/Graphics/CaptionMaker/) that can help you get started on the file.
When creating captions, there's also additional (caption formatting techniques)[http://www.theneitherworld.com/mcpoodle/SCC_TOOLS/DOCS/SCC_FORMAT.HTML#style] that would be good to use, like brackets around sound effects. [ sound effect ]
Adding to Video.js
------------------
Once you have your WebVTT file created, you can add it to Video.js using the track trag. Put your track track tag after all the source elements, and before any fallback content.
<code type="html">
<video id="example_video_1" class="video-js vjs-default-skin"
controls preload="auto" width="640" height="264"
data-setup='{"example_option":true}'>
<source src="http://video-js.zencoder.com/oceans-clip.mp4" type='video/mp4' />
<source src="http://video-js.zencoder.com/oceans-clip.webm" type='video/webm' />
<source src="http://video-js.zencoder.com/oceans-clip.ogv" type='video/ogg' />
<track kind="captions" src="http://example.com/path/to/captions.vtt" srclang="en" label="English" default>
</video>
</code>
Track Attributes
----------------
Additional settings for track tags.
### kind
One of the five track types listed above. Kind defaults to subtitles if no kind is included.
### label
The label for the track that will be show to the user, for example in a menu that list the different languages available for subtitles.
### default
The default attribute can be used to have a track default to showing. Otherwise the viewer would need to select their language from the captions or subtitles menu.
NOTE: For chapters, default is required if you want the chapters menu to show.
### srclang
The two-letter code (valid BCP 47 language tag) for the language of the text track, for example "en" for English. Here's a list of available language codes.
<table border="0" cellspacing="5" cellpadding="5">
<tr>
<td>
<table>
<tr><th>ab<th><td>Abkhazian</td></tr>
<tr><th>aa<th><td>Afar</td></tr>
<tr><th>af<th><td>Afrikaans</td></tr>
<tr><th>sq<th><td>Albanian</td></tr>
<tr><th>am<th><td>Amharic</td></tr>
<tr><th>ar<th><td>Arabic</td></tr>
<tr><th>an<th><td>Aragonese</td></tr>
<tr><th>hy<th><td>Armenian</td></tr>
<tr><th>as<th><td>Assamese</td></tr>
<tr><th>ay<th><td>Aymara</td></tr>
<tr><th>az<th><td>Azerbaijani</td></tr>
<tr><th>ba<th><td>Bashkir</td></tr>
<tr><th>eu<th><td>Basque</td></tr>
<tr><th>bn<th><td>Bengali (Bangla)</td></tr>
<tr><th>dz<th><td>Bhutani</td></tr>
<tr><th>bh<th><td>Bihari</td></tr>
<tr><th>bi<th><td>Bislama</td></tr>
<tr><th>br<th><td>Breton</td></tr>
<tr><th>bg<th><td>Bulgarian</td></tr>
<tr><th>my<th><td>Burmese</td></tr>
<tr><th>be<th><td>Byelorussian (Belarusian)</td></tr>
<tr><th>km<th><td>Cambodian</td></tr>
<tr><th>ca<th><td>Catalan</td></tr>
<tr><th>zh<th><td>Chinese (Simplified)</td></tr>
<tr><th>zh<th><td>Chinese (Traditional)</td></tr>
<tr><th>co<th><td>Corsican</td></tr>
<tr><th>hr<th><td>Croatian</td></tr>
<tr><th>cs<th><td>Czech</td></tr>
<tr><th>da<th><td>Danish</td></tr>
<tr><th>nl<th><td>Dutch</td></tr>
<tr><th>en<th><td>English</td></tr>
<tr><th>eo<th><td>Esperanto</td></tr>
<tr><th>et<th><td>Estonian</td></tr>
<tr><th>fo<th><td>Faeroese</td></tr>
<tr><th>fa<th><td>Farsi</td></tr>
<tr><th>fj<th><td>Fiji</td></tr>
<tr><th>fi<th><td>Finnish</td></tr>
</table>
</td>
<td>
<table>
<tr><th>fr<th><td>French</td></tr>
<tr><th>fy<th><td>Frisian</td></tr>
<tr><th>gl<th><td>Galician</td></tr>
<tr><th>gd<th><td>Gaelic (Scottish)</td></tr>
<tr><th>gv<th><td>Gaelic (Manx)</td></tr>
<tr><th>ka<th><td>Georgian</td></tr>
<tr><th>de<th><td>German</td></tr>
<tr><th>el<th><td>Greek</td></tr>
<tr><th>kl<th><td>Greenlandic</td></tr>
<tr><th>gn<th><td>Guarani</td></tr>
<tr><th>gu<th><td>Gujarati</td></tr>
<tr><th>ht<th><td>Haitian Creole</td></tr>
<tr><th>ha<th><td>Hausa</td></tr>
<tr><th>he<th><td>Hebrew</td></tr>
<tr><th>hi<th><td>Hindi</td></tr>
<tr><th>hu<th><td>Hungarian</td></tr>
<tr><th>is<th><td>Icelandic</td></tr>
<tr><th>io<th><td>Ido</td></tr>
<tr><th>id<th><td>Indonesian</td></tr>
<tr><th>ia<th><td>Interlingua</td></tr>
<tr><th>ie<th><td>Interlingue</td></tr>
<tr><th>iu<th><td>Inuktitut</td></tr>
<tr><th>ik<th><td>Inupiak</td></tr>
<tr><th>ga<th><td>Irish</td></tr>
<tr><th>it<th><td>Italian</td></tr>
<tr><th>ja<th><td>Japanese</td></tr>
<tr><th>jv<th><td>Javanese</td></tr>
<tr><th>kn<th><td>Kannada</td></tr>
<tr><th>ks<th><td>Kashmiri</td></tr>
<tr><th>kk<th><td>Kazakh</td></tr>
<tr><th>rw<th><td>Kinyarwanda (Ruanda)</td></tr>
<tr><th>ky<th><td>Kirghiz</td></tr>
<tr><th>rn<th><td>Kirundi (Rundi)</td></tr>
<tr><th>ko<th><td>Korean</td></tr>
<tr><th>ku<th><td>Kurdish</td></tr>
<tr><th>lo<th><td>Laothian</td></tr>
<tr><th>la<th><td>Latin</td></tr>
</table>
</td>
<td>
<table>
<tr><th>lv<th><td>Latvian (Lettish)</td></tr>
<tr><th>li<th><td>Limburgish ( Limburger)</td></tr>
<tr><th>ln<th><td>Lingala</td></tr>
<tr><th>lt<th><td>Lithuanian</td></tr>
<tr><th>mk<th><td>Macedonian</td></tr>
<tr><th>mg<th><td>Malagasy</td></tr>
<tr><th>ms<th><td>Malay</td></tr>
<tr><th>ml<th><td>Malayalam</td></tr>
<tr><th>mt<th><td>Maltese</td></tr>
<tr><th>mi<th><td>Maori</td></tr>
<tr><th>mr<th><td>Marathi</td></tr>
<tr><th>mo<th><td>Moldavian</td></tr>
<tr><th>mn<th><td>Mongolian</td></tr>
<tr><th>na<th><td>Nauru</td></tr>
<tr><th>ne<th><td>Nepali</td></tr>
<tr><th>no<th><td>Norwegian</td></tr>
<tr><th>oc<th><td>Occitan</td></tr>
<tr><th>or<th><td>Oriya</td></tr>
<tr><th>om<th><td>Oromo (Afan, Galla)</td></tr>
<tr><th>ps<th><td>Pashto (Pushto)</td></tr>
<tr><th>pl<th><td>Polish</td></tr>
<tr><th>pt<th><td>Portuguese</td></tr>
<tr><th>pa<th><td>Punjabi</td></tr>
<tr><th>qu<th><td>Quechua</td></tr>
<tr><th>rm<th><td>Rhaeto-Romance</td></tr>
<tr><th>ro<th><td>Romanian</td></tr>
<tr><th>ru<th><td>Russian</td></tr>
<tr><th>sm<th><td>Samoan</td></tr>
<tr><th>sg<th><td>Sangro</td></tr>
<tr><th>sa<th><td>Sanskrit</td></tr>
<tr><th>sr<th><td>Serbian</td></tr>
<tr><th>sh<th><td>Serbo-Croatian</td></tr>
<tr><th>st<th><td>Sesotho</td></tr>
<tr><th>tn<th><td>Setswana</td></tr>
<tr><th>sn<th><td>Shona</td></tr>
<tr><th>ii<th><td>Sichuan Yi</td></tr>
<tr><th>sd<th><td>Sindhi</td></tr>
</table>
</td>
<td>
<table>
<tr><th>si<th><td>Sinhalese</td></tr>
<tr><th>ss<th><td>Siswati</td></tr>
<tr><th>sk<th><td>Slovak</td></tr>
<tr><th>sl<th><td>Slovenian</td></tr>
<tr><th>so<th><td>Somali</td></tr>
<tr><th>es<th><td>Spanish</td></tr>
<tr><th>su<th><td>Sundanese</td></tr>
<tr><th>sw<th><td>Swahili (Kiswahili)</td></tr>
<tr><th>sv<th><td>Swedish</td></tr>
<tr><th>tl<th><td>Tagalog</td></tr>
<tr><th>tg<th><td>Tajik</td></tr>
<tr><th>ta<th><td>Tamil</td></tr>
<tr><th>tt<th><td>Tatar</td></tr>
<tr><th>te<th><td>Telugu</td></tr>
<tr><th>th<th><td>Thai</td></tr>
<tr><th>bo<th><td>Tibetan</td></tr>
<tr><th>ti<th><td>Tigrinya</td></tr>
<tr><th>to<th><td>Tonga</td></tr>
<tr><th>ts<th><td>Tsonga</td></tr>
<tr><th>tr<th><td>Turkish</td></tr>
<tr><th>tk<th><td>Turkmen</td></tr>
<tr><th>tw<th><td>Twi</td></tr>
<tr><th>ug<th><td>Uighur</td></tr>
<tr><th>uk<th><td>Ukrainian</td></tr>
<tr><th>ur<th><td>Urdu</td></tr>
<tr><th>uz<th><td>Uzbek</td></tr>
<tr><th>vi<th><td>Vietnamese</td></tr>
<tr><th>vo<th><td>Volapük</td></tr>
<tr><th>wa<th><td>Wallon</td></tr>
<tr><th>cy<th><td>Welsh</td></tr>
<tr><th>wo<th><td>Wolof</td></tr>
<tr><th>xh<th><td>Xhosa</td></tr>
<tr><th>yi<th><td>Yiddish</td></tr>
<tr><th>yo<th><td>Yoruba</td></tr>
<tr><th>zu<th><td>Zulu</td></tr>
</table>
</td>
</tr>
</table>
+1
Ver Arquivo
@@ -0,0 +1 @@
var vjsSourceList = [];
+2 -3
Ver Arquivo
@@ -1,6 +1,6 @@
/*!
Video.js - HTML5 Video Player
Version 3.0r2
Version GENERATED_AT_BUILD
LGPL v3 LICENSE INFO
This file is part of Video.js. Copyright 2011 Zencoder, Inc.
@@ -21,5 +21,4 @@ along with Video.js. If not, see <http://www.gnu.org/licenses/>.
// Self-executing function to prevent global vars and help with minification
;(function(window, undefined){
var document = window.document;
var document = window.document;
-3
Ver Arquivo
@@ -2,8 +2,5 @@
// Expose to global
window.VideoJS = window._V_ = VideoJS;
// Run Auto-load players
_V_.autoSetup();
// End self-executing function
})(window);
+83 -41
Ver Arquivo
@@ -45,18 +45,18 @@ _V_.Component = _V_.Class.extend({
init: function(player, options){
this.player = player;
if (options && options.el) {
// Allow for overridding default component options
options = this.options = _V_.merge(this.options || {}, options);
// Create element if one wasn't provided in options
if (options.el) {
this.el = options.el;
} else {
this.el = this.createElement();
}
// Array of sub-components
if (options && options.components) {
_V_.each.call(this, options.components, function(comp){
this.addComponent(comp);
});
}
// Add any components in options
this.initComponents();
},
destroy: function(){},
@@ -71,38 +71,59 @@ _V_.Component = _V_.Class.extend({
return "";
},
initComponents: function(){
var options = this.options;
if (options && options.components) {
// Loop through components and add them to the player
this.eachProp(options.components, function(name, opts){
// Allow waiting to add components until a specific event is called
var tempAdd = this.proxy(function(){
// Set property name on player. Could cause conflicts with other prop names, but it's worth making refs easy.
this[name] = this.addComponent(name, opts);
});
if (opts.loadEvent) {
this.one(opts.loadEvent, tempAdd)
} else {
tempAdd();
}
});
}
},
// Add child components to this component.
// Will generate a new child component and then append child component's element to this component's element.
// Takes either the name of the UI component class, or an object that contains a name, UI Class, and options.
addComponent: function(nameORobj){
var name, componentClass, options, component;
addComponent: function(name, options){
var component, componentClass;
if (typeof nameORobj == "string") {
name = nameORobj;
// If string, create new component with options
if (typeof name == "string") {
// Can also pass in object to define a different class than the name and add other options
} else {
name = nameORobj.name;
componentClass = nameORobj.componentClass;
options = nameORobj.options;
}
// Make sure options is at least an empty object to protect against errors
options = options || {};
if (!componentClass) {
// Assume name of set is a lowercased name of the UI Class (PlayButton, etc.)
componentClass = _V_.capitalize(name);
}
componentClass = options.componentClass || _V_.uc(name);
// Create a new object & element for this controls set
// If there's no .player, this is a player
component = new _V_[componentClass](this.player || this, options);
// Create a new object & element for this controls set
// If there's no .player, this is a player
component = new _V_[componentClass](this.player || this, options);
if (this.components === undefined) {
this.components = [];
} else {
component = name;
}
this.components.push(component);
// Add the UI object's element to the container div (box)
this.el.appendChild(component.el);
// Return so it can stored on parent object if desired.
return component;
},
removeComponent: function(component){
this.el.removeChild(component.el);
},
/* Display
@@ -114,6 +135,30 @@ _V_.Component = _V_.Class.extend({
hide: function(){
this.el.style.display = "none";
},
fadeIn: function(){
this.removeClass("vjs-fade-out");
this.addClass("vjs-fade-in");
},
fadeOut: function(){
this.removeClass("vjs-fade-in");
this.addClass("vjs-fade-out");
},
lockShowing: function(){
var style = this.el.style;
style.display = "block";
style.opacity = 1;
style.visiblity = "visible";
},
unlockShowing: function(){
var style = this.el.style;
style.display = "";
style.opacity = "";
style.visiblity = "";
},
addClass: function(classToAdd){
_V_.addClass(this.el, classToAdd);
@@ -125,7 +170,7 @@ _V_.Component = _V_.Class.extend({
/* Events
================================================================================ */
addEvent: function(type, fn){
addEvent: function(type, fn, uid){
return _V_.addEvent(this.el, type, _V_.proxy(this, fn));
},
removeEvent: function(type, fn){
@@ -134,6 +179,9 @@ _V_.Component = _V_.Class.extend({
triggerEvent: function(type, e){
return _V_.triggerEvent(this.el, type, e);
},
one: function(type, fn) {
_V_.one(this.el, type, _V_.proxy(this, fn));
},
/* Ready - Trigger functions when component is ready
================================================================================ */
@@ -162,27 +210,21 @@ _V_.Component = _V_.Class.extend({
// Reset Ready Queue
this.readyQueue = [];
// Allow for using event listeners also, in case you want to do something everytime a source is ready.
this.triggerEvent("ready");
}
},
/* Utility
================================================================================ */
each: function(arr, fn){
if (!arr || arr.length === 0) { return; }
for (var i=0,j=arr.length; i<j; i++) {
if (fn.call(this, arr[i], i)) { break; }
}
},
each: function(arr, fn){ _V_.each.call(this, arr, fn); },
extend: function(obj){
for (var attrname in obj) {
if (obj.hasOwnProperty(attrname)) { this[attrname]=obj[attrname]; }
}
},
eachProp: function(obj, fn){ _V_.eachProp.call(this, obj, fn); },
extend: function(obj){ _V_.merge(this, obj) },
// More easily attach 'this' to functions
proxy: function(fn){
return _V_.proxy(this, fn);
}
proxy: function(fn, uid){ return _V_.proxy(this, fn, uid); }
});
+137 -86
Ver Arquivo
@@ -8,6 +8,58 @@ _V_.Control = _V_.Component.extend({
});
/* Control Bar
================================================================================ */
_V_.ControlBar = _V_.Component.extend({
options: {
loadEvent: "play",
components: {
"playToggle": {},
"fullscreenToggle": {},
"currentTimeDisplay": {},
"timeDivider": {},
"durationDisplay": {},
"remainingTimeDisplay": {},
"progressControl": {},
"volumeControl": {},
"muteToggle": {}
}
},
init: function(player, options){
this._super(player, options);
player.one("play", this.proxy(function(){
this.fadeIn();
this.player.addEvent("mouseover", this.proxy(this.fadeIn));
this.player.addEvent("mouseout", this.proxy(this.fadeOut));
}));
},
createElement: function(){
return _V_.createElement("div", {
className: "vjs-controls"
});
},
fadeIn: function(){
this._super();
this.player.triggerEvent("controlsvisible");
},
fadeOut: function(){
this._super();
this.player.triggerEvent("controlshidden");
},
lockShowing: function(){
this.el.style.opacity = "1";
}
});
/* Button - Base class for all buttons
================================================================================ */
_V_.Button = _V_.Control.extend({
@@ -140,7 +192,7 @@ _V_.FullscreenToggle = _V_.Button.extend({
},
onClick: function(){
if (!this.player.videoIsFullScreen) {
if (!this.player.isFullScreen) {
this.player.requestFullScreen();
} else {
this.player.cancelFullScreen();
@@ -169,7 +221,7 @@ _V_.BigPlayButton = _V_.Button.extend({
onClick: function(){
// Go back to the beginning if big play button is showing at the end.
// Have to check for current time otherwise it might throw a 'not ready' error.
if(this.player.currentTime()) {
if(this.player.currentTime()) {
this.player.currentTime(0);
}
this.player.play();
@@ -188,7 +240,11 @@ _V_.LoadingSpinner = _V_.Component.extend({
player.addEvent("seeking", _V_.proxy(this, this.show));
player.addEvent("error", _V_.proxy(this, this.show));
player.addEvent("stalled", _V_.proxy(this, this.show));
// Not showing spinner on stalled any more. Browsers may stall and then not trigger any events that would remove the spinner.
// Checked in Chrome 16 and Safari 5.1.2. http://help.videojs.com/discussions/problems/883-why-is-the-download-progress-showing
// player.addEvent("stalled", _V_.proxy(this, this.show));
player.addEvent("waiting", _V_.proxy(this, this.show));
},
@@ -196,9 +252,9 @@ _V_.LoadingSpinner = _V_.Component.extend({
var classNameSpinner, innerHtmlSpinner;
if ( typeof this.player.el.style.WebkitBorderRadius == "string"
|| typeof this.player.el.style.MozBorderRadius == "string"
|| typeof this.player.el.style.KhtmlBorderRadius == "string"
if ( typeof this.player.el.style.WebkitBorderRadius == "string"
|| typeof this.player.el.style.MozBorderRadius == "string"
|| typeof this.player.el.style.KhtmlBorderRadius == "string"
|| typeof this.player.el.style.borderRadius == "string")
{
classNameSpinner = "vjs-loading-spinner";
@@ -215,34 +271,6 @@ _V_.LoadingSpinner = _V_.Component.extend({
}
});
/* Control Bar
================================================================================ */
_V_.ControlBar = _V_.Component.extend({
init: function(player, options){
this._super(player, options);
player.addEvent("play", this.proxy(this.show));
player.addEvent("mouseover", this.proxy(this.reveal));
player.addEvent("mouseout", this.proxy(this.conceal));
},
createElement: function(){
return _V_.createElement("div", {
className: "vjs-controls"
});
},
// Used for transitions (fading out)
reveal: function(){
this.el.style.opacity = 1;
},
conceal: function(){
this.el.style.opacity = 0;
}
});
/* Time
================================================================================ */
_V_.CurrentTimeDisplay = _V_.Component.extend({
@@ -354,24 +382,18 @@ _V_.Slider = _V_.Component.extend({
init: function(player, options){
this._super(player, options);
_V_.each.call(this, this.components, function(comp){
if (comp instanceof _V_[this.barClass]) {
this.bar = comp;
} else if (comp instanceof _V_[this.handleClass]) {
this.handle = comp;
}
});
player.addEvent(this.playerEvent, _V_.proxy(this, this.update));
this.addEvent("mousedown", this.onMouseDown);
this.addEvent("focus", this.onFocus);
this.addEvent("blur", this.onBlur);
// Update Display
// Need to wait for styles to be loaded.
// TODO - replace setTimeout with stylesReady function.
setTimeout(this.proxy(this.update), 0);
this.player.addEvent("controlsvisible", this.proxy(this.update));
// This is actually to fix the volume handle position. http://twitter.com/#!/gerritvanaaken/status/159046254519787520
// this.player.one("timeupdate", this.proxy(this.update));
this.update();
},
createElement: function(type, attrs) {
@@ -422,12 +444,15 @@ _V_.Slider = _V_.Component.extend({
// If there is a handle, we need to account for the handle in our calculation for progress bar
// so that it doesn't fall short of or extend past the handle.
if (handle) {
var box = this.el,
boxWidth = box.offsetWidth,
handleWidth = handle.el.offsetWidth,
// The width of the handle in percent of the containing box
// In IE, widths may not be ready yet causing NaN
handlePercent = (handle.el.offsetWidth) ? handle.el.offsetWidth / boxWidth : 0,
handlePercent = (handleWidth) ? handleWidth / boxWidth : 0,
// Get the adjusted size of the box, considering that the handle's center never touches the left or right side.
// There is a margin of half the handle's width on both sides.
@@ -491,6 +516,12 @@ _V_.Slider = _V_.Component.extend({
// Progress Control: Seek, Load Progress, and Play Progress
_V_.ProgressControl = _V_.Component.extend({
options: {
components: {
"seekBar": {}
}
},
createElement: function(){
return this._super("div", {
className: "vjs-progress-control vjs-control"
@@ -502,8 +533,16 @@ _V_.ProgressControl = _V_.Component.extend({
// Seek Bar and holder for the progress bars
_V_.SeekBar = _V_.Slider.extend({
barClass: "PlayProgressBar",
handleClass: "SeekHandle",
options: {
components: {
"loadProgressBar": {},
// Set property names to bar and handle to match with the parent Slider class is looking for
"bar": { componentClass: "PlayProgressBar" },
"handle": { componentClass: "SeekHandle" }
}
},
playerEvent: "timeupdate",
init: function(player, options){
@@ -610,6 +649,12 @@ _V_.SeekHandle = _V_.Component.extend({
// Progress Control: Seek, Load Progress, and Play Progress
_V_.VolumeControl = _V_.Component.extend({
options: {
components: {
"volumeBar": {}
}
},
createElement: function(){
return this._super("div", {
className: "vjs-volume-control vjs-control"
@@ -620,8 +665,13 @@ _V_.VolumeControl = _V_.Component.extend({
_V_.VolumeBar = _V_.Slider.extend({
barClass: "VolumeLevel",
handleClass: "VolumeHandle",
options: {
components: {
"bar": { componentClass: "VolumeLevel" },
"handle": { componentClass: "VolumeHandle" }
}
},
playerEvent: "volumechange",
createElement: function(){
@@ -714,7 +764,7 @@ _V_.MuteToggle = _V_.Button.extend({
/* Poster Image
================================================================================ */
_V_.Poster = _V_.Button.extend({
_V_.PosterImage = _V_.Button.extend({
init: function(player, options){
this._super(player, options);
@@ -726,8 +776,7 @@ _V_.Poster = _V_.Button.extend({
},
createElement: function(){
_V_.log(this.player.options.poster)
return this._super("img", {
return _V_.createElement("img", {
className: "vjs-poster",
src: this.player.options.poster,
@@ -741,55 +790,57 @@ _V_.Poster = _V_.Button.extend({
}
});
/* Text Track Displays
/* Menu
================================================================================ */
// Create a behavior type for each text track type (subtitlesDisplay, captionsDisplay, etc.).
// Then you can easily do something like.
// player.addBehavior(myDiv, "subtitlesDisplay");
// And the myDiv's content will be updated with the text change.
// Base class for all track displays. Should not be instantiated on its own.
_V_.TextTrackDisplay = _V_.Component.extend({
// The base for text track and settings menu buttons.
_V_.Menu = _V_.Component.extend({
init: function(player, options){
this._super(player, options);
},
player.addEvent(this.trackType + "update", _V_.proxy(this, this.update));
addItem: function(component){
this.addComponent(component);
component.addEvent("click", this.proxy(function(){
this.unlockShowing();
}));
},
createElement: function(){
return this._super("div", {
className: "vjs-" + this.trackType
return this._super("ul", {
className: "vjs-menu"
});
},
update: function(){
this.el.innerHTML = this.player.textTrackValue(this.trackType);
}
});
_V_.SubtitlesDisplay = _V_.TextTrackDisplay.extend({
_V_.MenuItem = _V_.Button.extend({
trackType: "subtitles"
init: function(player, options){
this._super(player, options);
});
if (options.selected) {
this.addClass("vjs-selected");
}
},
_V_.CaptionsDisplay = _V_.TextTrackDisplay.extend({
createElement: function(type, attrs){
return this._super("li", _V_.merge({
className: "vjs-menu-item",
innerHTML: this.options.label
}, attrs));
},
trackType: "captions"
onClick: function(){
this.selected(true);
},
});
_V_.ChaptersDisplay = _V_.TextTrackDisplay.extend({
trackType: "chapters"
});
_V_.DescriptionsDisplay = _V_.TextTrackDisplay.extend({
trackType: "descriptions"
selected: function(selected){
if (selected) {
this.addClass("vjs-selected");
} else {
this.removeClass("vjs-selected")
}
}
});
+57 -99
Ver Arquivo
@@ -38,7 +38,10 @@ var VideoJS = function(id, addOptions, ready){
},
// Shortcut
_V_ = VideoJS;
_V_ = VideoJS,
// CDN Version. Used to target right flash swf.
CDN_VERSION = "GENERATED_CDN_VSN";
VideoJS.players = {};
@@ -49,110 +52,65 @@ VideoJS.options = {
// techOrder: ["flash","html5"],
html5: {},
flash: {
swf: "http://vjs.zencdn.net/c/video-js.swf"
// swf: "https://s3.amazonaws.com/video-js/3.0b/video-js.swf"
// swf: "http://video-js.zencoder.com/3.0b/video-js.swf"
// swf: "http://video-js.com/test/video-js.swf"
// swf: "http://video-js.com/source/flash/video-js.swf"
// swf: "http://video-js.com/source/flash/video-js.swf"
// swf: "video-js.swf"
},
flash: { swf: "http://vjs.zencdn.net/c/video-js.swf" },
// Default of web browser is 300x150. Should rely on source width/height.
width: "auto",
height: "auto",
width: 300,
height: 150,
// defaultVolume: 0.85,
defaultVolume: 0.00, // The freakin seaguls are driving me crazy!
// Included control sets
components: [
"poster",
"loadingSpinner",
"bigPlayButton",
{ name: "controlBar", options: {
components: [
"playToggle",
"fullscreenToggle",
"currentTimeDisplay",
"timeDivider",
"durationDisplay",
"remainingTimeDisplay",
{ name: "progressControl", options: {
components: [
{ name: "seekBar", options: {
components: [
"loadProgressBar",
"playProgressBar",
"seekHandle"
]}
}
]}
},
{ name: "volumeControl", options: {
components: [
{ name: "volumeBar", options: {
components: [
"volumeLevel",
"volumeHandle"
]}
}
]}
},
"muteToggle"
]
}},
"subtitlesDisplay"/*, "replay"*/
]
};
// Automatically set up any tags that have a data-setup attribute
_V_.autoSetup = function(){
var options, vid, player,
vids = document.getElementsByTagName("video");
// Check if any media elements exist
if (vids && vids.length > 0) {
for (var i=0,j=vids.length; i<j; i++) {
vid = vids[i];
// Check if element exists, has getAttribute func.
// IE seems to consider typeof el.getAttribute == "object" instead of "function" like expected, at least when loading the player immediately.
if (vid && vid.getAttribute) {
// Make sure this player hasn't already been set up.
if (vid.player === undefined) {
options = vid.getAttribute("data-setup");
// Check if data-setup attr exists.
// We only auto-setup if they've added the data-setup attr.
if (options !== null) {
// Parse options JSON
// If empty string, make it a parsable json object.
options = JSON.parse(options || "{}");
// Create new video.js instance.
player = _V_(vid, options);
}
}
// If getAttribute isn't defined, we need to wait for the DOM.
} else {
_V_.autoSetupTimeout(1);
break;
}
}
// No videos were found, so keep looping unless page is finisehd loading.
} else if (!_V_.windowLoaded) {
_V_.autoSetupTimeout(1);
components: {
"posterImage": {},
"textTrackDisplay": {},
"loadingSpinner": {},
"bigPlayButton": {},
"controlBar": {}
}
// components: [
// "poster",
// "loadingSpinner",
// "bigPlayButton",
// { name: "controlBar", options: {
// components: [
// "playToggle",
// "fullscreenToggle",
// "currentTimeDisplay",
// "timeDivider",
// "durationDisplay",
// "remainingTimeDisplay",
// { name: "progressControl", options: {
// components: [
// { name: "seekBar", options: {
// components: [
// "loadProgressBar",
// "playProgressBar",
// "seekHandle"
// ]}
// }
// ]}
// },
// { name: "volumeControl", options: {
// components: [
// { name: "volumeBar", options: {
// components: [
// "volumeLevel",
// "volumeHandle"
// ]}
// }
// ]}
// },
// "muteToggle"
// ]
// }},
// "subtitlesDisplay"/*, "replay"*/
// ]
};
// Pause to let the DOM keep processing
_V_.autoSetupTimeout = function(wait){
setTimeout(_V_.autoSetup, wait);
};
// Set CDN Version of swf
if (CDN_VERSION != "GENERATED_CDN_VSN") {
_V_.options.flash.swf = "http://vjs.zencdn.net/"+CDN_VERSION+"/video-js.swf"
}
+7
Ver Arquivo
@@ -226,6 +226,13 @@ _V_.extend({
// }
// }
// }
},
one: function(elem, type, fn) {
_V_.addEvent(elem, type, function(){
_V_.removeEvent(elem, type, arguments.callee)
fn.apply(this, arguments);
});
}
});
+38 -9
Ver Arquivo
@@ -15,6 +15,7 @@ _V_.extend({
// Device Checks
isIE: function(){ return !+"\v1"; },
isFF: function(){ return !!_V_.ua.match("Firefox") },
isIPad: function(){ return navigator.userAgent.match(/iPad/i) !== null; },
isIPhone: function(){ return navigator.userAgent.match(/iPhone/i) !== null; },
isIOS: function(){ return VideoJS.isIPhone() || VideoJS.isIPad(); },
@@ -39,6 +40,15 @@ _V_.extend({
}
},
eachProp: function(obj, fn){
if (!obj) { return; }
for (var name in obj) {
if (obj.hasOwnProperty(name)) {
fn.call(this, name, obj[name]);
}
}
},
el: function(id){ return document.getElementById(id); },
createElement: function(tagName, attributes){
var el = document.createElement(tagName),
@@ -95,8 +105,8 @@ _V_.extend({
// Return seconds as H:MM:SS or M:SS
// Supplying a guide (in seconds) will include enough leading zeros to cover the length of the guide
formatTime: function(seconds, guide) {
var guide = guide || seconds, // Default to using seconds as guide
s = Math.floor(seconds % 60),
guide = guide || seconds; // Default to using seconds as guide
var s = Math.floor(seconds % 60),
m = Math.floor(seconds / 60 % 60),
h = Math.floor(seconds / 3600),
gm = Math.floor(guide / 60 % 60),
@@ -115,7 +125,7 @@ _V_.extend({
return h + m + s;
},
capitalize: function(string){
uc: function(string){
return string.charAt(0).toUpperCase() + string.slice(1);
},
@@ -189,17 +199,21 @@ _V_.extend({
/* Proxy (a.k.a Bind or Context). A simple method for changing the context of a function
It also stores a unique id on the function so it can be easily removed from events
================================================================================ */
proxy: function(context, fn) {
proxy: function(context, fn, uid) {
// Make sure the function has a unique ID
if (!fn.guid) { fn.guid = _V_.guid++; }
// Create the new function that changes the context
var ret = function() {
return fn.apply(context, arguments);
};
}
// Give the new function the same ID
// (so that they are equivalent and can be easily removed)
ret.guid = fn.guid;
// Allow for the ability to individualize this function
// Needed in the case where multiple objects might share the same prototype
// IF both items add an event listener with the same function, then you try to remove just one
// it will remove both because they both have the same guid.
// when using this, you need to use the proxy method when you remove the listener as well.
ret.guid = (uid) ? uid + "_" + fn.guid : fn.guid;
return ret;
},
@@ -256,7 +270,7 @@ _V_.extend({
================================================================================ */
setLocalStorage: function(key, value){
// IE was throwing errors referencing the var anywhere without this
var localStorage = localStorage || false;
var localStorage = window.localStorage || false;
if (!localStorage) { return; }
try {
localStorage[key] = value;
@@ -267,6 +281,21 @@ _V_.extend({
_V_.log("LocalStorage Error (VideoJS)", e);
}
}
},
// Get abosolute version of relative URL. Used to tell flash correct URL.
// http://stackoverflow.com/questions/470832/getting-an-absolute-url-from-a-relative-one-ie6-issue
getAbsoluteURL: function(url){
// Check if absolute URL
if (!url.match(/^https?:\/\//)) {
// Convert to absolute URL. Flash hosted off-site needs an absolute URL.
url = _V_.createElement('div', {
innerHTML: '<a href="'+url+'">x</a>'
}).firstChild.href;
}
return url;
}
});
+332 -198
Ver Arquivo
@@ -7,27 +7,34 @@ _V_.Player = _V_.Component.extend({
this.tag = tag; // Store the original tag used to set options
var el = this.el = _V_.createElement("div"), // Div to contain video and controls
options = this.options = {},
width = options.width = tag.width,
height = options.height = tag.height,
options = this.options = {};
// Browsers default to 300x150 if there's no width/height or video size data.
initWidth = width || 300,
initHeight = height || 150;
// Make player findable on elements
tag.player = el.player = this;
// Set Options
_V_.merge(options, _V_.options); // Copy Global Defaults
_V_.merge(options, this.getVideoTagSettings()); // Override with Video Tag Options
_V_.merge(options, addOptions); // Override/extend with options from setup call
// Add callback to ready queue
this.ready(ready);
// Store controls setting, and then remove immediately so native controls don't flash.
tag.removeAttribute("controls");
// Poster will be handled by a manual <img>
tag.removeAttribute("poster");
// Make player findable on elements
tag.player = el.player = this;
// Wrap video tag in div (el/box) container
tag.parentNode.insertBefore(el, tag);
el.appendChild(tag); // Breaks iPhone, fixed in HTML5 setup.
// Give video tag properties to box
el.id = this.id = tag.id; // ID will now reference box, not the video tag
// ID will now reference box, not the video tag
this.id = el.id = tag.id;
el.className = tag.className;
// Update tag id/class for use as HTML5 playback tech
tag.id += "_html5_api";
tag.className = "vjs-tech";
@@ -36,27 +43,18 @@ _V_.Player = _V_.Component.extend({
_V_.players[el.id] = this;
// Make box use width/height of tag, or default 300x150
el.setAttribute("width", initWidth);
el.setAttribute("height", initHeight);
el.setAttribute("width", options.width);
el.setAttribute("height", options.height);
// Enforce with CSS since width/height attrs don't work on divs
el.style.width = initWidth+"px";
el.style.height = initHeight+"px";
el.style.width = options.width+"px";
el.style.height = options.height+"px";
// Remove width/height attrs from tag so CSS can make it 100% width/height
tag.removeAttribute("width");
tag.removeAttribute("height");
// Set Options
_V_.merge(options, _V_.options); // Copy Global Defaults
_V_.merge(options, this.getVideoTagSettings()); // Override with Video Tag Options
_V_.merge(options, addOptions); // Override/extend with options from setup call
// Store controls setting, and then remove immediately so native controls don't flash.
tag.removeAttribute("controls");
// Poster will be handled by a manual <img>
tag.removeAttribute("poster");
// Empty video tag sources and tracks so the built in player doesn't use them also.
// Empty video tag sources and tracks so the built-in player doesn't use them also.
if (tag.hasChildNodes()) {
for (var i=0,j=tag.childNodes;i<j.length;i++) {
if (j[i].nodeName == "SOURCE" || j[i].nodeName == "TRACK") {
@@ -65,9 +63,6 @@ _V_.Player = _V_.Component.extend({
}
}
// Holder for playback tech components
this.techs = {};
// Cache for video property values.
this.values = {};
@@ -76,17 +71,22 @@ _V_.Player = _V_.Component.extend({
this.addEvent("ended", this.onEnded);
this.addEvent("play", this.onPlay);
this.addEvent("pause", this.onPause);
this.addEvent("progress", this.onProgress);
this.addEvent("error", this.onError);
// When the API is ready, loop through the components and add to the player.
if (options.controls) {
this.ready(function(){
this.each(this.options.components, function(set){
this.addComponent(set);
});
this.initComponents();
});
}
// Tracks defined in tracks.js
this.textTracks = [];
if (options.tracks && options.tracks.length > 0) {
this.addTextTracks(options.tracks);
}
// If there are no sources when the player is initialized,
// load the first supported playback technology.
if (!options.sources || options.sources.length == 0) {
@@ -101,8 +101,9 @@ _V_.Player = _V_.Component.extend({
}
}
} else {
// Loop through playback technologies (HTML5, Flash) and check for support
// Then load the best source.
// Loop through playback technologies (HTML5, Flash) and check for support. Then load the best source.
// A few assumptions here:
// All playback technologies respect preload false.
this.src(options.sources);
}
},
@@ -114,47 +115,51 @@ _V_.Player = _V_.Component.extend({
// Ensure that tracking progress and time progress will stop and plater deleted
this.stopTrackingProgress();
this.stopTrackingCurrentTime();
delete _V_.players[this.id]
_V_.players[this.id] = null;
delete _V_.players[this.id];
this.tech.destroy();
this.el.parentNode.removeChild(this.el);
},
createElement: function(type, options){
},
createElement: function(type, options){},
getVideoTagSettings: function(){
var options = {
sources: [],
tracks: []
};
},
tag = this.tag,
getAttribute = "getAttribute"; // For better minification
options.src = this.tag.src;
options.controls = this.tag.getAttribute("controls") !== null;
options.poster = this.tag.poster;
options.preload = this.tag.preload;
options.autoplay = this.tag.getAttribute("autoplay") !== null; // hasAttribute not IE <8 compatible
options.loop = this.tag.getAttribute("loop") !== null;
options.muted = this.tag.getAttribute("muted") !== null;
for (var c,i=0,j=this.tag.children;i<j.length;i++) {
c = j[i];
if (c.nodeName == "SOURCE") {
options.sources.push({
src: c.src,
type: c.type,
media: c.media,
title: c.title
});
}
if (c.nodeName == "TRACK") {
options.tracks.push(new _V_.Track({
src: c.getAttribute("src"),
kind: c.getAttribute("kind"),
srclang: c.getAttribute("srclang"),
label: c.getAttribute("label"),
'default': c.getAttribute("default") !== null,
title: c.getAttribute("title")
}, this));
options.src = tag[getAttribute]("src");
options.controls = tag[getAttribute]("controls") !== null;
options.poster = tag[getAttribute]("poster");
options.preload = tag[getAttribute]("preload");
options.autoplay = tag[getAttribute]("autoplay") !== null; // hasAttribute not IE <8 compatible
options.loop = tag[getAttribute]("loop") !== null;
options.muted = tag[getAttribute]("muted") !== null;
if (this.tag.hasChildNodes()) {
for (var c,i=0,j=this.tag.childNodes;i<j.length;i++) {
c = j[i];
if (c.nodeName == "SOURCE") {
options.sources.push({
src: c[getAttribute]('src'),
type: c[getAttribute]('type'),
media: c[getAttribute]('media'),
title: c[getAttribute]('title')
});
}
if (c.nodeName == "TRACK") {
options.tracks.push({
src: c[getAttribute]("src"),
kind: c[getAttribute]("kind"),
srclang: c[getAttribute]("srclang"),
label: c[getAttribute]("label"),
'default': c[getAttribute]("default") !== null,
title: c[getAttribute]("title")
});
}
}
}
return options;
@@ -227,15 +232,15 @@ _V_.Player = _V_.Component.extend({
// First is a plugin reload issue in Firefox that has been around for 11 years: https://bugzilla.mozilla.org/show_bug.cgi?id=90268
// Then with the new fullscreen API, Mozilla and webkit browsers will reload the flash object after going to fullscreen.
// To get around this, we're unloading the tech, caching source and currentTime values, and reloading the tech once the plugin is resized.
reloadTech: function(betweenFn){
_V_.log("unloadingTech")
this.unloadTech();
_V_.log("unloadedTech")
if (betweenFn) { betweenFn.call(); }
_V_.log("LoadingTech")
this.loadTech(this.techName, { src: this.values.src })
_V_.log("loadedTech")
},
// reloadTech: function(betweenFn){
// _V_.log("unloadingTech")
// this.unloadTech();
// _V_.log("unloadedTech")
// if (betweenFn) { betweenFn.call(); }
// _V_.log("LoadingTech")
// this.loadTech(this.techName, { src: this.values.src })
// _V_.log("loadedTech")
// },
/* Fallbacks for unsupported event types
================================================================================ */
@@ -345,6 +350,13 @@ _V_.Player = _V_.Component.extend({
_V_.addClass(this.el, "vjs-paused");
},
onProgress: function(){
// Add custom event for when source is finished downloading.
if (this.bufferedPercent() == 1) {
this.triggerEvent("loadedalldata");
}
},
onError: function(e) {
_V_.log("Video Error", e);
},
@@ -352,53 +364,127 @@ _V_.Player = _V_.Component.extend({
/* Player API
================================================================================ */
apiCall: function(method, arg){
if (this.isReady) {
return this.tech[method](arg);
// Pass values to the playback tech
techCall: function(method, arg){
// If it's not ready yet, call method when it is
if (!this.tech.isReady) {
this.tech.ready(function(){
this[method](arg);
});
// Otherwise call method now
} else {
_V_.log("The playback technology API is not ready yet. Use player.ready(myFunction)."+" ["+method+"]", arguments.callee.caller.arguments.callee.caller.arguments.callee.caller)
return false;
// throw new Error("The playback technology API is not ready yet. Use player.ready(myFunction)."+" ["+method+"]");
try {
this.tech[method](arg);
} catch(e) {
_V_.log(e);
}
}
},
play: function(){
this.apiCall("play"); return this;
},
pause: function(){
this.apiCall("pause"); return this;
},
paused: function(){
return this.apiCall("paused");
// Get calls can't wait for the tech, and sometimes don't need to.
techGet: function(method){
// Make sure tech is ready
if (this.tech.isReady) {
// Flash likes to die and reload when you hide or reposition it.
// In these cases the object methods go away and we get errors.
// When that happens we'll catch the errors and inform tech that it's not ready any more.
try {
return this.tech[method]();
} catch(e) {
// When building additional tech libs, an expected method may not be defined yet
if (this.tech[method] === undefined) {
_V_.log("Video.js: " + method + " method not defined for "+this.techName+" playback technology.", e);
} else {
// When a method isn't available on the object it throws a TypeError
if (e.name == "TypeError") {
_V_.log("Video.js: " + method + " unavailable on "+this.techName+" playback technology element.", e);
this.tech.isReady = false;
} else {
_V_.log(e);
}
}
}
}
return;
},
// Method for calling methods on the current playback technology
// techCall: function(method, arg){
//
// // if (this.isReady) {
// //
// // } else {
// // _V_.log("The playback technology API is not ready yet. Use player.ready(myFunction)."+" ["+method+"]", arguments.callee.caller.arguments.callee.caller.arguments.callee.caller)
// // return false;
// // // throw new Error("The playback technology API is not ready yet. Use player.ready(myFunction)."+" ["+method+"]");
// // }
// },
// http://dev.w3.org/html5/spec/video.html#dom-media-play
play: function(){
this.techCall("play");
return this;
},
// http://dev.w3.org/html5/spec/video.html#dom-media-pause
pause: function(){
this.techCall("pause");
return this;
},
// http://dev.w3.org/html5/spec/video.html#dom-media-paused
// The initial state of paused should be true (in Safari it's actually false)
paused: function(){
return (this.techGet("paused") === false) ? false : true;
},
// http://dev.w3.org/html5/spec/video.html#dom-media-currenttime
currentTime: function(seconds){
if (seconds !== undefined) {
// Cache the last set value for smoother scrubbing.
this.values.lastSetCurrentTime = seconds;
this.apiCall("setCurrentTime", seconds);
this.techCall("setCurrentTime", seconds);
// Improve the accuracy of manual timeupdates
if (this.manualTimeUpdates) { this.triggerEvent("timeupdate"); }
if (this.manualTimeUpdates) {
this.triggerEvent("timeupdate");
}
return this;
}
// Cache last currentTime and return
return this.values.currentTime = this.apiCall("currentTime");
// Default to 0 seconds
return this.values.currentTime = (this.techGet("currentTime") || 0);
},
// http://dev.w3.org/html5/spec/video.html#dom-media-duration
// Duration should return NaN if not available. ParseFloat will turn false-ish values to NaN.
duration: function(){
return this.apiCall("duration");
return parseFloat(this.techGet("duration"));
},
// Calculates how much time is left. Not in spec, but useful.
remainingTime: function(){
return this.duration() - this.currentTime();
},
// http://dev.w3.org/html5/spec/video.html#dom-media-buffered
// Buffered returns a timerange object. Kind of like an array of portions of the video that have been downloaded.
// So far no browsers return more than one range (portion)
buffered: function(){
var buffered = this.apiCall("buffered"),
start = 0, end = this.values.bufferEnd = this.values.bufferEnd || 0,
var buffered = this.techGet("buffered"),
start = 0,
end = this.values.bufferEnd = this.values.bufferEnd || 0, // Default end to 0 and store in values
timeRange;
if (buffered && buffered.length > 0 && buffered.end(0) !== end) {
@@ -410,34 +496,46 @@ _V_.Player = _V_.Component.extend({
return _V_.createTimeRange(start, end);
},
// Calculates amount of buffer is full
// Calculates amount of buffer is full. Not in spec but useful.
bufferedPercent: function(){
return (this.duration()) ? this.buffered().end(0) / this.duration() : 0;
},
// http://dev.w3.org/html5/spec/video.html#dom-media-volume
volume: function(percentAsDecimal){
var vol;
if (percentAsDecimal !== undefined) {
var vol = Math.max(0, Math.min(1, parseFloat(percentAsDecimal))); // Force value to between 0 and 1
vol = Math.max(0, Math.min(1, parseFloat(percentAsDecimal))); // Force value to between 0 and 1
this.values.volume = vol;
this.apiCall("setVolume", vol);
this.techCall("setVolume", vol);
_V_.setLocalStorage("volume", vol);
return this;
}
// if (this.values.volume) { return this.values.volume; }
return this.apiCall("volume");
},
muted: function(muted){
if (muted !== undefined) {
this.apiCall("setMuted", muted);
return this;
}
return this.apiCall("muted");
// Default to 1 when returning current volume.
vol = parseFloat(this.techGet("volume"));
return (isNaN(vol)) ? 1 : vol;
},
// http://dev.w3.org/html5/spec/video.html#attr-media-muted
muted: function(muted){
if (muted !== undefined) {
this.techCall("setMuted", muted);
return this;
}
return this.techGet("muted") || false; // Default to false
},
// http://dev.w3.org/html5/spec/dimension-attributes.html#attr-dim-height
// Video tag width/height only work in pixels. No percents.
// We could potentially allow percents but won't for now until we can do testing around it.
width: function(width, skipListeners){
if (width !== undefined) {
this.el.width = width;
this.el.style.width = width+"px";
// skipListeners allows us to avoid triggering the resize event when setting both width and height
if (!skipListeners) { this.triggerEvent("resize"); }
return this;
}
@@ -452,30 +550,43 @@ _V_.Player = _V_.Component.extend({
}
return parseInt(this.el.getAttribute("height"));
},
// Set both width and height at the same time.
size: function(width, height){
// Skip resize listeners on width for optimization
return this.width(width, true).height(height);
},
supportsFullScreen: function(){ return this.apiCall("supportsFullScreen"); },
// Check if current tech can support native fullscreen (e.g. with built in controls lik iOS, so not our flash swf)
supportsFullScreen: function(){ return this.techGet("supportsFullScreen") || false; },
// Turn on fullscreen (or window) mode
requestFullScreen: function(){
var requestFullScreen = _V_.support.requestFullScreen;
this.isFullScreen = true;
// Check for browser element fullscreen support
if (requestFullScreen) {
// Trigger fullscreenchange event after change
_V_.addEvent(document, requestFullScreen.eventName, this.proxy(function(){
this.isFullScreen = document[requestFullScreen.isFullScreen];
// If cancelling fullscreen, remove event listener.
if (this.isFullScreen == false) {
_V_.removeEvent(document, requestFullScreen.eventName, arguments.callee);
}
this.triggerEvent("fullscreenchange");
}));
// Flash and other plugins get reloaded when you take their parent to fullscreen.
// To fix that we'll remove the tech, and reload it after the resize has finished.
if (this.tech.support.fullscreenResize === false) {
if (this.tech.support.fullscreenResize === false && this.options.flash.iFrameMode != true) {
this.pause();
this.unloadTech();
_V_.addEvent(document, "keydown", _V_.proxy(this, function(e){
_V_.log("asdf", e)
}));
_V_.addEvent(document, requestFullScreen.eventName, this.proxy(function(){
_V_.removeEvent(document, requestFullScreen.eventName, arguments.callee);
this.loadTech(this.techName, { src: this.values.src });
@@ -488,34 +599,34 @@ _V_.Player = _V_.Component.extend({
}
} else if (this.tech.supportsFullScreen()) {
this.apiCall("enterFullScreen");
this.triggerEvent("fullscreenchange");
this.techCall("enterFullScreen");
} else {
this.triggerEvent("fullscreenchange");
this.enterFullWindow();
}
this.videoIsFullScreen = true;
this.triggerEvent("fullscreenchange");
return this;
},
cancelFullScreen: function(){
var requestFullScreen = _V_.support.requestFullScreen;
this.isFullScreen = false;
// Check for browser element fullscreen support
if (requestFullScreen) {
// Flash and other plugins get reloaded when you take their parent to fullscreen.
// To fix that we'll remove the tech, and reload it after the resize has finished.
if (this.tech.support.fullscreenResize === false) {
if (this.tech.support.fullscreenResize === false && this.options.flash.iFrameMode != true) {
this.pause();
this.unloadTech();
_V_.addEvent(document, requestFullScreen.eventName, this.proxy(function(){
_V_.removeEvent(document, requestFullScreen.eventName, arguments.callee);
_V_.log("document fullscreeneventchange")
this.loadTech(this.techName, { src: this.values.src })
}));
@@ -526,20 +637,20 @@ _V_.Player = _V_.Component.extend({
}
} else if (this.tech.supportsFullScreen()) {
this.apiCall("exitFullScreen");
this.techCall("exitFullScreen");
this.triggerEvent("fullscreenchange");
} else {
this.exitFullWindow();
this.triggerEvent("fullscreenchange");
}
this.videoIsFullScreen = false;
this.triggerEvent("fullscreenchange");
return this;
},
// When fullscreen isn't supported we can stretch the video container to as wide as the browser will let us.
enterFullWindow: function(){
this.videoIsFullScreen = true;
this.isFullWindow = true;
// Storing original doc overflow value to return to when fullscreen is off
this.docOrigOverflow = document.documentElement.style.overflow;
@@ -556,15 +667,18 @@ _V_.Player = _V_.Component.extend({
this.triggerEvent("enterFullWindow");
},
fullWindowOnEscKey: function(event){
if (event.keyCode == 27) {
this.cancelFullScreen();
if (this.isFullScreen == true) {
this.cancelFullScreen();
} else {
this.exitFullWindow();
}
}
},
exitFullWindow: function(){
this.videoIsFullScreen = false;
this.isFullWindow = false;
_V_.removeEvent(document, "keydown", this.fullWindowOnEscKey);
// Unhide scroll bars.
@@ -579,6 +693,34 @@ _V_.Player = _V_.Component.extend({
this.triggerEvent("exitFullWindow");
},
selectSource: function(sources){
// Loop through each playback technology in the options order
for (var i=0,j=this.options.techOrder;i<j.length;i++) {
var techName = j[i],
tech = _V_[techName];
// tech = _V_.tech[techName];
// Check if the browser supports this technology
if (tech.isSupported()) {
// Loop through each source object
for (var a=0,b=sources;a<b.length;a++) {
var source = b[a];
// Check if source can be played with this technology
if (tech.canPlaySource.call(this, source)) {
return { source: source, tech: techName };
}
}
}
}
return false;
},
// src is a pretty powerful function
// If you pass it an array of source objects, it will find the best source to play and use that object.src
// If the new source requires a new playback technology, it will switch to that.
@@ -588,48 +730,36 @@ _V_.Player = _V_.Component.extend({
// Case: Array of source objects to choose from and pick the best to play
if (source instanceof Array) {
var sources = source;
var sourceTech = this.selectSource(source),
source,
techName;
techLoop: // Named loop for breaking both loops
// Loop through each playback technology in the options order
for (var i=0,j=this.options.techOrder;i<j.length;i++) {
var techName = j[i],
tech = _V_[techName];
// tech = _V_.tech[techName];
if (sourceTech) {
source = sourceTech.source;
techName = sourceTech.tech;
// Check if the browser supports this technology
if (tech.isSupported()) {
// If this technology is already loaded, set source
if (techName == this.techName) {
this.src(source); // Passing the source object
// Loop through each source object
for (var a=0,b=sources;a<b.length;a++) {
var source = b[a];
// Check if source can be played with this technology
if (tech.canPlaySource.call(this, source)) {
// If this technology is already loaded, set source
if (techName == this.currentTechName) {
this.src(source); // Passing the source object
// Otherwise load this technology with chosen source
} else {
this.loadTech(techName, source);
}
break techLoop; // Break both loops
}
}
// Otherwise load this technology with chosen source
} else {
this.loadTech(techName, source);
}
} else {
_V_.log("No compatible source and playback technology were found.")
}
// Case: Source object { src: "", type: "" ... }
} else if (source instanceof Object) {
if (_V_[this.techName].canPlaySource(source)) {
this.src(source.src);
} else {
// Send through tech loop to check for a compatible technology.
this.src([source]);
}
// Case: URL String (http://myvideo...)
} else {
// Cache for getting last set source
@@ -640,7 +770,7 @@ _V_.Player = _V_.Component.extend({
this.src(source);
});
} else {
this.apiCall("src", source);
this.techCall("src", source);
if (this.options.preload == "auto") {
this.load();
}
@@ -653,78 +783,73 @@ _V_.Player = _V_.Component.extend({
},
// Begin loading the src data
// http://dev.w3.org/html5/spec/video.html#dom-media-load
load: function(){
this.apiCall("load");
this.techCall("load");
return this;
},
currentSrc: function(){
return this.apiCall("currentSrc");
},
textTrackValue: function(kind, value){
if (value !== undefined) {
this.values[kind] = value;
this.triggerEvent(kind+"update");
return this;
}
return this.values[kind];
// http://dev.w3.org/html5/spec/video.html#dom-media-currentsrc
currentSrc: function(){
return this.techGet("currentSrc") || this.values.src || "";
},
// Attributes/Options
preload: function(value){
if (value !== undefined) {
this.apiCall("setPreload", value);
this.techCall("setPreload", value);
this.options.preload = value;
return this;
}
return this.apiCall("preload", value);
return this.techGet("preload");
},
autoplay: function(value){
if (value !== undefined) {
this.apiCall("setAutoplay", value);
this.techCall("setAutoplay", value);
this.options.autoplay = value;
return this;
}
return this.apiCall("autoplay", value);
return this.techGet("autoplay", value);
},
loop: function(value){
if (value !== undefined) {
this.apiCall("setLoop", value);
this.techCall("setLoop", value);
this.options.loop = value;
return this;
}
return this.apiCall("loop", value);
return this.techGet("loop");
},
controls: function(){ return this.options.controls; },
textTracks: function(){ return this.options.tracks; },
poster: function(){ return this.apiCall("poster"); },
poster: function(){ return this.techGet("poster"); },
error: function(){ return this.techGet("error"); },
ended: function(){ return this.techGet("ended"); }
error: function(){ return this.apiCall("error"); },
networkState: function(){ return this.apiCall("networkState"); },
readyState: function(){ return this.apiCall("readyState"); },
seeking: function(){ return this.apiCall("seeking"); },
initialTime: function(){ return this.apiCall("initialTime"); },
startOffsetTime: function(){ return this.apiCall("startOffsetTime"); },
played: function(){ return this.apiCall("played"); },
seekable: function(){ return this.apiCall("seekable"); },
ended: function(){ return this.apiCall("ended"); },
videoTracks: function(){ return this.apiCall("videoTracks"); },
audioTracks: function(){ return this.apiCall("audioTracks"); },
videoWidth: function(){ return this.apiCall("videoWidth"); },
videoHeight: function(){ return this.apiCall("videoHeight"); },
defaultPlaybackRate: function(){ return this.apiCall("defaultPlaybackRate"); },
playbackRate: function(){ return this.apiCall("playbackRate"); },
// mediaGroup: function(){ return this.apiCall("mediaGroup"); },
// controller: function(){ return this.apiCall("controller"); },
controls: function(){ return this.apiCall("controls"); },
defaultMuted: function(){ return this.apiCall("defaultMuted"); }
// Methods to add support for
// networkState: function(){ return this.techCall("networkState"); },
// readyState: function(){ return this.techCall("readyState"); },
// seeking: function(){ return this.techCall("seeking"); },
// initialTime: function(){ return this.techCall("initialTime"); },
// startOffsetTime: function(){ return this.techCall("startOffsetTime"); },
// played: function(){ return this.techCall("played"); },
// seekable: function(){ return this.techCall("seekable"); },
// videoTracks: function(){ return this.techCall("videoTracks"); },
// audioTracks: function(){ return this.techCall("audioTracks"); },
// videoWidth: function(){ return this.techCall("videoWidth"); },
// videoHeight: function(){ return this.techCall("videoHeight"); },
// defaultPlaybackRate: function(){ return this.techCall("defaultPlaybackRate"); },
// playbackRate: function(){ return this.techCall("playbackRate"); },
// mediaGroup: function(){ return this.techCall("mediaGroup"); },
// controller: function(){ return this.techCall("controller"); },
// defaultMuted: function(){ return this.techCall("defaultMuted"); }
});
// RequestFullscreen API
(function(){
var requestFn,
cancelFn,
eventName,
isFullScreen,
playerProto = _V_.Player.prototype;
// Current W3C Spec
@@ -734,6 +859,7 @@ _V_.Player = _V_.Component.extend({
requestFn = "requestFullscreen";
cancelFn = "exitFullscreen";
eventName = "fullscreenchange";
isFullScreen = "fullScreen";
// Webkit (Chrome/Safari) and Mozilla (Firefox) have working implementaitons
// that use prefixes and vary slightly from the new W3C spec. Specifically, using 'exit' instead of 'cancel',
@@ -743,10 +869,17 @@ _V_.Player = _V_.Component.extend({
_V_.each(["moz", "webkit"], function(prefix){
if (document[prefix + "CancelFullScreen"] !== undefined) {
// https://github.com/zencoder/video-js/pull/128
if ((prefix != "moz" || document.mozFullScreenEnabled) && document[prefix + "CancelFullScreen"] !== undefined) {
requestFn = prefix + "RequestFullScreen";
cancelFn = prefix + "CancelFullScreen";
eventName = prefix + "fullscreenchange";
if (prefix == "webkit") {
isFullScreen = prefix + "IsFullScreen";
} else {
isFullScreen = prefix + "FullScreen";
}
}
});
@@ -757,7 +890,8 @@ _V_.Player = _V_.Component.extend({
_V_.support.requestFullScreen = {
requestFn: requestFn,
cancelFn: cancelFn,
eventName: eventName
eventName: eventName,
isFullScreen: isFullScreen
};
}
+53 -1
Ver Arquivo
@@ -1,4 +1,56 @@
// Automatically set up any tags that have a data-setup attribute
_V_.autoSetup = function(){
var options, vid, player,
vids = document.getElementsByTagName("video");
// Check if any media elements exist
if (vids && vids.length > 0) {
for (var i=0,j=vids.length; i<j; i++) {
vid = vids[i];
// Check if element exists, has getAttribute func.
// IE seems to consider typeof el.getAttribute == "object" instead of "function" like expected, at least when loading the player immediately.
if (vid && vid.getAttribute) {
// Make sure this player hasn't already been set up.
if (vid.player === undefined) {
options = vid.getAttribute("data-setup");
// Check if data-setup attr exists.
// We only auto-setup if they've added the data-setup attr.
if (options !== null) {
// Parse options JSON
// If empty string, make it a parsable json object.
options = JSON.parse(options || "{}");
// Create new video.js instance.
player = _V_(vid, options);
}
}
// If getAttribute isn't defined, we need to wait for the DOM.
} else {
_V_.autoSetupTimeout(1);
break;
}
}
// No videos were found, so keep looping unless page is finisehd loading.
} else if (!_V_.windowLoaded) {
_V_.autoSetupTimeout(1);
}
};
// Pause to let the DOM keep processing
_V_.autoSetupTimeout = function(wait){
setTimeout(_V_.autoSetup, wait);
};
_V_.addEvent(window, "load", function(){
_V_.windowLoaded = true;
});
_V_.autoSetupTimeout();
// Run Auto-load players
_V_.autoSetup();
+302 -59
Ver Arquivo
@@ -24,7 +24,7 @@ _V_.PlaybackTech = _V_.Component.extend({
_V_.apiMethods = "play,pause,paused,currentTime,setCurrentTime,duration,buffered,volume,setVolume,muted,setMuted,width,height,supportsFullScreen,enterFullScreen,src,load,currentSrc,preload,setPreload,autoplay,setAutoplay,loop,setLoop,error,networkState,readyState,seeking,initialTime,startOffsetTime,played,seekable,ended,videoTracks,audioTracks,videoWidth,videoHeight,textTracks,defaultPlaybackRate,playbackRate,mediaGroup,controller,controls,defaultMuted".split(",");
_V_.each(_V_.apiMethods, function(methodName){
_V_.PlaybackTech.prototype[methodName] = function(){
throw new Error("The '"+method+"' method is not available on the playback technology's API");
throw new Error("The '"+methodName+"' method is not available on the playback technology's API");
}
});
@@ -66,7 +66,7 @@ _V_.html5 = _V_.PlaybackTech.extend({
this.triggerReady();
},
destroy: function(){
this.player.tag = false;
this.removeTriggers();
@@ -87,7 +87,7 @@ _V_.html5 = _V_.PlaybackTech.extend({
if (!el || this.support.movingElementInDOM === false) {
// If the original tag is still there, remove it.
if (el) {
if (el) {
player.el.removeChild(el);
}
@@ -102,7 +102,9 @@ _V_.html5 = _V_.PlaybackTech.extend({
// Update tag settings, in case they were overridden
_V_.each(["autoplay","preload","loop","muted"], function(attr){ // ,"poster"
el[attr] = player.options[attr];
if (player.options[attr] !== null) {
el[attr] = player.options[attr];
}
}, this);
return el;
@@ -183,23 +185,23 @@ _V_.html5 = _V_.PlaybackTech.extend({
setLoop: function(val){ this.el.loop = val; },
error: function(){ return this.el.error; },
networkState: function(){ return this.el.networkState; },
readyState: function(){ return this.el.readyState; },
// networkState: function(){ return this.el.networkState; },
// readyState: function(){ return this.el.readyState; },
seeking: function(){ return this.el.seeking; },
initialTime: function(){ return this.el.initialTime; },
startOffsetTime: function(){ return this.el.startOffsetTime; },
played: function(){ return this.el.played; },
seekable: function(){ return this.el.seekable; },
// initialTime: function(){ return this.el.initialTime; },
// startOffsetTime: function(){ return this.el.startOffsetTime; },
// played: function(){ return this.el.played; },
// seekable: function(){ return this.el.seekable; },
ended: function(){ return this.el.ended; },
videoTracks: function(){ return this.el.videoTracks; },
audioTracks: function(){ return this.el.audioTracks; },
videoWidth: function(){ return this.el.videoWidth; },
videoHeight: function(){ return this.el.videoHeight; },
textTracks: function(){ return this.el.textTracks; },
defaultPlaybackRate: function(){ return this.el.defaultPlaybackRate; },
playbackRate: function(){ return this.el.playbackRate; },
mediaGroup: function(){ return this.el.mediaGroup; },
controller: function(){ return this.el.controller; },
// videoTracks: function(){ return this.el.videoTracks; },
// audioTracks: function(){ return this.el.audioTracks; },
// videoWidth: function(){ return this.el.videoWidth; },
// videoHeight: function(){ return this.el.videoHeight; },
// textTracks: function(){ return this.el.textTracks; },
// defaultPlaybackRate: function(){ return this.el.defaultPlaybackRate; },
// playbackRate: function(){ return this.el.playbackRate; },
// mediaGroup: function(){ return this.el.mediaGroup; },
// controller: function(){ return this.el.controller; },
controls: function(){ return this.player.options.controls; },
defaultMuted: function(){ return this.el.defaultMuted; }
});
@@ -223,7 +225,7 @@ _V_.html5.events = "loadstart,suspend,abort,error,emptied,stalled,loadedmetadata
/* HTML5 Device Fixes ---------------------------------------------------------- */
_V_.html5.prototype.support = {
// Support for tech specific full screen. (webkitEnterFullScreen, not requestFullscreen)
// http://developer.apple.com/library/safari/#documentation/AudioVideo/Reference/HTMLVideoElementClassReference/HTMLVideoElement/HTMLVideoElement.html
// Seems to be broken in Chromium/Chrome && Safari in Leopard
@@ -246,7 +248,7 @@ if (_V_.isAndroid()) {
}
/* VideoJS-SWF - Custom Flash Player with HTML5-ish API
/* VideoJS-SWF - Custom Flash Player with HTML5-ish API - https://github.com/zencoder/video-js-swf
================================================================================ */
_V_.flash = _V_.PlaybackTech.extend({
@@ -254,17 +256,26 @@ _V_.flash = _V_.PlaybackTech.extend({
this.player = player;
var source = options.source,
parentEl = options.parentEl,
placeHolder = this.el = _V_.createElement("div", { id: parentEl.id + "_temp_flash" }),
objId = player.el.id+"_flash_api",
playerOptions = player.options;
// Which element to embed in
parentEl = options.parentEl,
// Create a temporary element to be replaced by swf object
placeHolder = this.el = _V_.createElement("div", { id: parentEl.id + "_temp_flash" }),
// Generate ID for swf object
objId = player.el.id+"_flash_api",
// Store player options in local var for optimization
playerOptions = player.options,
// Merge default flashvars with ones passed in to init
flashVars = _V_.merge({
// SWF Callback Functions
readyFunction: "_V_.flash.onSWFReady",
eventProxyFunction: "_V_.flash.onSWFEvent",
errorEventProxyFunction: "_V_.flash.onSWFErrorEvent",
readyFunction: "_V_.flash.onReady",
eventProxyFunction: "_V_.flash.onEvent",
errorEventProxyFunction: "_V_.flash.onError",
// Player Settings
autoplay: playerOptions.autoplay,
@@ -274,32 +285,30 @@ _V_.flash = _V_.PlaybackTech.extend({
}, options.flashVars),
// Merge default parames with ones passed in
params = _V_.merge({
allowScriptAccess: "always",
wmode: "opaque",
bgcolor: "#000000"
wmode: "opaque", // Opaque is needed to overlay controls, but can affect playback performance
bgcolor: "#000000" // Using bgcolor prevents a white flash when the object is loading
}, options.params),
// Merge default attributes with ones passed in
attributes = _V_.merge({
id: objId,
name: objId,
name: objId, // Both ID and Name needed or swf to identifty itself
'class': 'vjs-tech'
}, options.attributes);
// EDIT: Trying to just us a manual <img> for poster.
// if (playerOptions.poster) {
// flashVars.poster = playerOptions.poster;
// }
}, options.attributes)
;
// If source was supplied pass as a flash var.
if (source) {
flashVars.src = source.src;
flashVars.src = encodeURIComponent(_V_.getAbsoluteURL(source.src));
}
// Add to box.
// Add placeholder to player div
_V_.insertFirst(placeHolder, parentEl);
// Having issues with Flash reloading on certain page actions (hide/resize/fullscreen) in certain browsers
// This allows resetting the playhead when we catch the reload
if (options.startTime) {
this.ready(function(){
this.load();
@@ -308,23 +317,158 @@ _V_.flash = _V_.PlaybackTech.extend({
});
}
swfobject.embedSWF(options.swf, placeHolder.id, "480", "270", "9.0.124", "", flashVars, params, attributes);
// Flash iFrame Mode
// In web browsers there are multiple instances where changing the parent element or visibility of a plugin causes the plugin to reload.
// - Firefox just about always. https://bugzilla.mozilla.org/show_bug.cgi?id=90268 (might be fixed by version 13)
// - Webkit when hiding the plugin
// - Webkit and Firefox when using requestFullScreen on a parent element
// Loading the flash plugin into a dynamically generated iFrame gets around most of these issues.
// Issues that remain include hiding the element and requestFullScreen in Firefox specifically
// There's on particularly annoying issue with this method which is that Firefox throws a security error on an offsite Flash object loaded into a dynamically created iFrame.
// Even though the iframe was inserted into a page on the web, Firefox + Flash considers it a local app trying to access an internet file.
// I tried mulitple ways of setting the iframe src attribute but couldn't find a src that worked well. Tried a real/fake source, in/out of domain.
// Also tried a method from stackoverflow that caused a security error in all browsers. http://stackoverflow.com/questions/2486901/how-to-set-document-domain-for-a-dynamically-generated-iframe
// In the end the solution I found to work was setting the iframe window.location.href right before doing a document.write of the Flash object.
// The only downside of this it seems to trigger another http request to the original page (no matter what's put in the href). Not sure why that is.
// NOTE (2012-01-29): Cannot get Firefox to load the remote hosted SWF into a dynamically created iFrame
// Firefox 9 throws a security error, unleess you call location.href right before doc.write.
// Not sure why that even works, but it causes the browser to look like it's continuously trying to load the page.
// Firefox 3.6 keeps calling the iframe onload function anytime I write to it, causing an endless loop.
if (options.iFrameMode == true && !_V_.isFF) {
// Create iFrame with vjs-tech class so it's 100% width/height
var iFrm = _V_.createElement("iframe", {
id: objId + "_iframe",
name: objId + "_iframe",
className: "vjs-tech",
scrolling: "no",
marginWidth: 0,
marginHeight: 0,
frameBorder: 0
});
// Update ready function names in flash vars for iframe window
flashVars.readyFunction = "ready";
flashVars.eventProxyFunction = "events";
flashVars.errorEventProxyFunction = "errors";
// Tried multiple methods to get this to work in all browsers
// Tried embedding the flash object in the page first, and then adding a place holder to the iframe, then replacing the placeholder with the page object.
// The goal here was to try to load the swf URL in the parent page first and hope that got around the firefox security error
// var newObj = _V_.flash.embed(options.swf, placeHolder, flashVars, params, attributes);
// (in onload)
// var temp = _V_.createElement("a", { id:"asdf", innerHTML: "asdf" } );
// iDoc.body.appendChild(temp);
// Tried embedding the flash object through javascript in the iframe source.
// This works in webkit but still triggers the firefox security error
// iFrm.src = "javascript: document.write('"+_V_.flash.getEmbedCode(options.swf, flashVars, params, attributes)+"');";
// Tried an actual local iframe just to make sure that works, but it kills the easiness of the CDN version if you require the user to host an iframe
// We should add an option to host the iframe locally though, because it could help a lot of issues.
// iFrm.src = "iframe.html";
// Wait until iFrame has loaded to write into it.
_V_.addEvent(iFrm, "load", _V_.proxy(this, function(){
var iDoc, objTag, swfLoc,
iWin = iFrm.contentWindow,
varString = "";
// The one working method I found was to use the iframe's document.write() to create the swf object
// This got around the security issue in all browsers except firefox.
// I did find a hack where if I call the iframe's window.location.href="", it would get around the security error
// However, the main page would look like it was loading indefinitely (URL bar loading spinner would never stop)
// Plus Firefox 3.6 didn't work no matter what I tried.
// if (_V_.ua.match("Firefox")) {
// iWin.location.href = "";
// }
// Get the iFrame's document depending on what the browser supports
iDoc = iFrm.contentDocument ? iFrm.contentDocument : iFrm.contentWindow.document;
// Tried ensuring both document domains were the same, but they already were, so that wasn't the issue.
// Even tried adding /. that was mentioned in a browser security writeup
// document.domain = document.domain+"/.";
// iDoc.domain = document.domain+"/.";
// Tried adding the object to the iframe doc's innerHTML. Security error in all browsers.
// iDoc.body.innerHTML = swfObjectHTML;
// Tried appending the object to the iframe doc's body. Security error in all browsers.
// iDoc.body.appendChild(swfObject);
// Using document.write actually got around the security error that browsers were throwing.
// Again, it's a dynamically generated (same domain) iframe, loading an external Flash swf.
// Not sure why that's a security issue, but apparently it is.
iDoc.write(_V_.flash.getEmbedCode(options.swf, flashVars, params, attributes));
// Setting variables on the window needs to come after the doc write because otherwise they can get reset in some browsers
// So far no issues with swf ready event being called before it's set on the window.
iWin.player = this.player;
// Create swf ready function for iFrame window
iWin.ready = _V_.proxy(this.player, function(currSwf){
var el = iDoc.getElementById(currSwf),
player = this,
tech = player.tech;
// Update reference to playback technology element
tech.el = el;
// Now that the element is ready, make a click on the swf play the video
_V_.addEvent(el, "click", tech.proxy(tech.onClick));
// Make sure swf is actually ready. Sometimes the API isn't actually yet.
_V_.flash.checkReady(tech);
});
// Create event listener for all swf events
iWin.events = _V_.proxy(this.player, function(swfID, eventName, other){
var player = this;
if (player && player.techName == "flash") {
player.triggerEvent(eventName);
}
});
// Create error listener for all swf errors
iWin.errors = _V_.proxy(this.player, function(swfID, eventName){
_V_.log("Flash Error", eventName);
});
}));
// Replace placeholder with iFrame (it will load now)
placeHolder.parentNode.replaceChild(iFrm, placeHolder);
// If not using iFrame mode, embed as normal object
} else {
_V_.flash.embed(options.swf, placeHolder, flashVars, params, attributes);
}
},
destroy: function(){
this.el.parentNode.removeChild(this.el);
},
// setupTriggers: function(){}, // Using global onSWFEvent func to distribute events
// setupTriggers: function(){}, // Using global onEvent func to distribute events
play: function(){ this.el.vjs_play(); },
pause: function(){ this.el.vjs_pause(); },
src: function(src){
// Make sure source URL is abosolute.
src = _V_.getAbsoluteURL(src);
this.el.vjs_src(src);
// Currently the SWF doesn't autoplay if you load a source later.
// e.g. Load player w/ no source, wait 2s, set src.
if (this.player.autoplay) {
if (this.player.autoplay()) {
var tech = this;
setTimeout(function(){ tech.play(); }, 0);
}
@@ -346,6 +490,7 @@ _V_.flash = _V_.PlaybackTech.extend({
// Create setters and getters for attributes
(function(){
var api = _V_.flash.prototype,
readWrite = "preload,currentTime,defaultPlaybackRate,playbackRate,autoplay,loop,mediaGroup,controller,controls,volume,muted,defaultMuted".split(","),
readOnly = "error,currentSrc,networkState,readyState,seeking,initialTime,duration,startOffsetTime,paused,played,seekable,ended,videoTracks,audioTracks,videoWidth,videoHeight,textTracks".split(","),
@@ -359,7 +504,8 @@ _V_.flash = _V_.PlaybackTech.extend({
createGetter = function(attr){
api[attr] = function(){ return this.el.vjs_getProperty(attr); };
};
}
;
// Create getter and setters for all read/write attributes
_V_.each(readWrite, function(attr){
@@ -371,12 +517,14 @@ _V_.flash = _V_.PlaybackTech.extend({
_V_.each(readOnly, function(attr){
createGetter(attr);
});
})();
/* Flash Support Testing -------------------------------------------------------- */
_V_.flash.isSupported = function(){
return swfobject.hasFlashPlayerVersion("10");
return _V_.flash.version()[0] >= 10;
// return swfobject.hasFlashPlayerVersion("10");
};
_V_.flash.canPlaySource = function(srcObj){
@@ -395,14 +543,14 @@ _V_.flash.prototype.support = {
progressEvent: false,
timeupdateEvent: false,
// Resizing plugins using request fullscreen reloads the plugin
// Resizing plugins using request fullscreen reloads the plugin
fullscreenResize: false,
// Resizing plugins in Firefox always reloads the plugin (e.g. full window mode)
parentResize: !(_V_.ua.match("Firefox"))
};
_V_.flash.onSWFReady = function(currSwf){
_V_.flash.onReady = function(currSwf){
var el = _V_.el(currSwf);
@@ -426,26 +574,121 @@ _V_.flash.onSWFReady = function(currSwf){
// The SWF isn't alwasy ready when it says it is. Sometimes the API functions still need to be added to the object.
// If it's not ready, we set a timeout to check again shortly.
_V_.flash.checkReady = function(tech){
// Check if API property exists
if (tech.el.vjs_getProperty) {
// If so, tell tech it's ready
tech.triggerReady();
// Otherwise wait longer.
} else {
setTimeout(function(){
_V_.flash.checkReady(tech);
}, 50);
}
};
_V_.flash.onSWFEvent = function(swfID, eventName, other){
// Trigger events from the swf on the player
_V_.flash.onEvent = function(swfID, eventName){
var player = _V_.el(swfID).player;
player.triggerEvent(eventName);
};
// Log errors from the swf
_V_.flash.onError = function(swfID, err){
var player = _V_.el(swfID).player;
player.triggerEvent("error");
_V_.log("Flash Error", err, swfID);
};
// Flash Version Check
_V_.flash.version = function(){
var version = '0,0,0'
// IE
try {
var player = _V_.el(swfID).player;
if (player && player.techName == "flash") {
player.triggerEvent(eventName);
}
} catch(err) {
_V_.log(err);
version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version').replace(/\D+/g, ',').match(/^,?(.+),?$/)[1];
// other browsers
} catch(e) {
try {
if (navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin){
version = (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]).description.replace(/\D+/g, ",").match(/^,?(.+),?$/)[1];
}
} catch(e) {}
}
return version.split(",");
}
// Flash embedding method. Only used in non-iframe mode
_V_.flash.embed = function(swf, placeHolder, flashVars, params, attributes){
var code = _V_.flash.getEmbedCode(swf, flashVars, params, attributes),
// Get element by embedding code and retrieving created element
obj = _V_.createElement("div", { innerHTML: code }).childNodes[0],
par = placeHolder.parentNode
;
placeHolder.parentNode.replaceChild(obj, placeHolder);
// IE6 seems to have an issue where it won't initialize the swf object after injecting it.
// This is a dumb temporary fix
if (_V_.isIE()) {
var newObj = par.childNodes[0];
setTimeout(function(){
newObj.style.display = "block";
}, 1000);
}
return obj;
};
_V_.flash.onSWFErrorEvent = function(swfID, eventName){
_V_.log("Flash Error", eventName);
};
_V_.flash.getEmbedCode = function(swf, flashVars, params, attributes){
var objTag = '<object type="application/x-shockwave-flash"',
flashVarsString = '',
paramsString = ''
attrsString = '';
// Convert flash vars to string
if (flashVars) {
_V_.eachProp(flashVars, function(key, val){
flashVarsString += (key + "=" + val + "&amp;");
});
}
// Add swf, flashVars, and other default params
params = _V_.merge({
movie: swf,
flashvars: flashVarsString,
allowScriptAccess: "always", // Required to talk to swf
allowNetworking: "all" // All should be default, but having security issues.
}, params);
// Create param tags string
_V_.eachProp(params, function(key, val){
paramsString += '<param name="'+key+'" value="'+val+'" />';
});
attributes = _V_.merge({
// Add swf to attributes (need both for IE and Others to work)
data: swf,
// Default to 100% width/height
width: "100%",
height: "100%"
}, attributes);
// Create Attributes string
_V_.eachProp(attributes, function(key, val){
attrsString += (key + '="' + val + '" ');
});
return objTag + attrsString + '>' + paramsString + '</object>';
}
+728 -80
Ver Arquivo
@@ -1,132 +1,780 @@
_V_.Track = function(attributes, player){
// Store reference to the parent player
this.player = player;
// TEXT TRACKS
// Text tracks are tracks of timed text events.
// Captions - text displayed over the video for the hearing impared
// Subtitles - text displayed over the video for those who don't understand langauge in the video
// Chapters - text displayed in a menu allowing the user to jump to particular points (chapters) in the video
// Descriptions (not supported yet) - audio descriptions that are read back to the user by a screen reading device
this.src = attributes.src;
this.kind = attributes.kind;
this.srclang = attributes.srclang;
this.label = attributes.label;
this["default"] = attributes["default"]; // 'default' is reserved-ish
this.title = attributes.title;
// Player Track Functions - Functions add to the player object for easier access to tracks
_V_.merge(_V_.Player.prototype, {
this.cues = [];
this.currentCue = false;
this.lastCueIndex = 0;
// Add an array of text tracks. captions, subtitles, chapters, descriptions
// Track objects will be stored in the player.textTracks array
addTextTracks: function(trackObjects){
var tracks = this.textTracks = (this.textTracks) ? this.textTracks : [],
i = 0, j = trackObjects.length, track, Kind;
// Update current cue on timeupdate
player.addEvent("timeupdate", _V_.proxy(this, this.update));
for (;i<j;i++) {
// HTML5 Spec says default to subtitles.
// Uppercase (uc) first letter to match class names
Kind = _V_.uc(trackObjects[i].kind || "subtitles");
// Reset cue time on media end
player.addEvent("ended", _V_.proxy(this, function() { this.lastCueIndex = 0; }));
// Create correct texttrack class. CaptionsTrack, etc.
track = new _V_[Kind + "Track"](this, trackObjects[i]);
// Load Track File
_V_.get(attributes.src, _V_.proxy(this, this.parseCues));
};
tracks.push(track);
_V_.Track.prototype = {
// If track.default is set, start showing immediately
// TODO: Add a process to deterime the best track to show for the specific kind
// Incase there are mulitple defaulted tracks of the same kind
// Or the user has a set preference of a specific language that should override the default
if (track['default']) {
this.ready(_V_.proxy(track, track.show));
}
}
// Return the track so it can be appended to the display component
return this;
},
// Show a text track
// disableSameKind: disable all other tracks of the same kind. Value should be a track kind (captions, etc.)
showTextTrack: function(id, disableSameKind){
var tracks = this.textTracks,
i = 0,
j = tracks.length,
track, showTrack, kind;
// Find Track with same ID
for (;i<j;i++) {
track = tracks[i];
if (track.id === id) {
track.show();
showTrack = track;
// Disable tracks of the same kind
} else if (disableSameKind && track.kind == disableSameKind && track.mode > 0) {
track.disable();
}
}
// Get track kind from shown track or disableSameKind
kind = (showTrack) ? showTrack.kind : ((disableSameKind) ? disableSameKind : false);
// Trigger trackchange event, captionstrackchange, subtitlestrackchange, etc.
if (kind) {
this.triggerEvent(kind+"trackchange");
}
return this;
}
});
// Track Class
// Contains track methods for loading, showing, parsing cues of tracks
_V_.Track = _V_.Component.extend({
init: function(player, options){
this._super(player, options);
// Apply track info to track object
// Options will often be a track element
_V_.merge(this, {
// Build ID if one doesn't exist
id: options.id || ("vjs_" + options.kind + "_" + options.language + "_" + _V_.guid++),
src: options.src,
// If default is used, subtitles/captions to start showing
"default": options["default"], // 'default' is reserved-ish
title: options.title,
// Language - two letter string to represent track language, e.g. "en" for English
// readonly attribute DOMString language;
language: options.srclang,
// Track label e.g. "English"
// readonly attribute DOMString label;
label: options.label,
// All cues of the track. Cues have a startTime, endTime, text, and other properties.
// readonly attribute TextTrackCueList cues;
cues: [],
// ActiveCues is all cues that are currently showing
// readonly attribute TextTrackCueList activeCues;
activeCues: [],
// ReadyState describes if the text file has been loaded
// const unsigned short NONE = 0;
// const unsigned short LOADING = 1;
// const unsigned short LOADED = 2;
// const unsigned short ERROR = 3;
// readonly attribute unsigned short readyState;
readyState: 0,
// Mode describes if the track is showing, hidden, or disabled
// const unsigned short OFF = 0;
// const unsigned short HIDDEN = 1; (still triggering cuechange events, but not visible)
// const unsigned short SHOWING = 2;
// attribute unsigned short mode;
mode: 0
});
},
// Create basic div to hold cue text
createElement: function(){
return this._super("div", {
className: "vjs-" + this.kind + " vjs-text-track"
});
},
// Show: Mode Showing (2)
// Indicates that the text track is active. If no attempt has yet been made to obtain the track's cues, the user agent will perform such an attempt momentarily.
// The user agent is maintaining a list of which cues are active, and events are being fired accordingly.
// In addition, for text tracks whose kind is subtitles or captions, the cues are being displayed over the video as appropriate;
// for text tracks whose kind is descriptions, the user agent is making the cues available to the user in a non-visual fashion;
// and for text tracks whose kind is chapters, the user agent is making available to the user a mechanism by which the user can navigate to any point in the media resource by selecting a cue.
// The showing by default state is used in conjunction with the default attribute on track elements to indicate that the text track was enabled due to that attribute.
// This allows the user agent to override the state if a later track is discovered that is more appropriate per the user's preferences.
show: function(){
this.activate();
this.mode = 2;
// Show element.
this._super();
},
// Hide: Mode Hidden (1)
// Indicates that the text track is active, but that the user agent is not actively displaying the cues.
// If no attempt has yet been made to obtain the track's cues, the user agent will perform such an attempt momentarily.
// The user agent is maintaining a list of which cues are active, and events are being fired accordingly.
hide: function(){
// When hidden, cues are still triggered. Disable to stop triggering.
this.activate();
this.mode = 1;
// Hide element.
this._super();
},
// Disable: Mode Off/Disable (0)
// Indicates that the text track is not active. Other than for the purposes of exposing the track in the DOM, the user agent is ignoring the text track.
// No cues are active, no events are fired, and the user agent will not attempt to obtain the track's cues.
disable: function(){
// If showing, hide.
if (this.mode == 2) { this.hide(); }
// Stop triggering cues
this.deactivate();
// Switch Mode to Off
this.mode = 0;
},
// Turn on cue tracking. Tracks that are showing OR hidden are active.
activate: function(){
// Load text file if it hasn't been yet.
if (this.readyState == 0) { this.load(); }
// Only activate if not already active.
if (this.mode == 0) {
// Update current cue on timeupdate
// Using unique ID for proxy function so other tracks don't remove listener
this.player.addEvent("timeupdate", this.proxy(this.update, this.id));
// Reset cue time on media end
this.player.addEvent("ended", this.proxy(this.reset, this.id));
// Add to display
if (this.kind == "captions" || this.kind == "subtitles") {
this.player.textTrackDisplay.addComponent(this);
}
}
},
// Turn off cue tracking.
deactivate: function(){
// Using unique ID for proxy function so other tracks don't remove listener
this.player.removeEvent("timeupdate", this.proxy(this.update, this.id));
this.player.removeEvent("ended", this.proxy(this.reset, this.id));
this.reset(); // Reset
// Remove from display
this.player.textTrackDisplay.removeComponent(this);
},
// A readiness state
// One of the following:
//
// Not loaded
// Indicates that the text track is known to exist (e.g. it has been declared with a track element), but its cues have not been obtained.
//
// Loading
// Indicates that the text track is loading and there have been no fatal errors encountered so far. Further cues might still be added to the track.
//
// Loaded
// Indicates that the text track has been loaded with no fatal errors. No new cues will be added to the track except if the text track corresponds to a MutableTextTrack object.
//
// Failed to load
// Indicates that the text track was enabled, but when the user agent attempted to obtain it, this failed in some way (e.g. URL could not be resolved, network error, unknown text track format). Some or all of the cues are likely missing and will not be obtained.
load: function(){
// Only load if not loaded yet.
if (this.readyState == 0) {
this.readyState = 1;
_V_.get(this.src, this.proxy(this.parseCues), this.proxy(this.onError));
}
},
onError: function(err){
this.error = err;
this.readyState = 3;
this.triggerEvent("error");
},
// Parse the WebVTT text format for cue times.
// TODO: Separate parser into own class so alternative timed text formats can be used. (TTML, DFXP)
parseCues: function(srcContent) {
var cue, time, text,
lines = srcContent.split("\n"),
line = "";
line = "", id;
for (var i=1, j=lines.length; i<j; i++) {
// Line 0 should be 'WEBVTT', so skipping i=0
for (var i=0; i<lines.length; i++) {
line = _V_.trim(lines[i]); // Trim whitespace and linebreaks
if (line) { // Loop until a line with content
// First line could be an optional cue ID
// Check if line has the time separator
if (line.indexOf("-->") == -1) {
id = line;
// Advance to next line for timing.
line = _V_.trim(lines[++i]);
} else {
id = this.cues.length;
}
// First line - Number
cue = {
id: line, // Cue Number
id: id, // Cue Number
index: this.cues.length // Position in Array
};
// Second line - Time
line = _V_.trim(lines[++i]);
// Timing line
time = line.split(" --> ");
cue.startTime = this.parseCueTime(time[0]);
cue.endTime = this.parseCueTime(time[1]);
// Additional lines - Cue Text
text = [];
for (var j=i; j<lines.length; j++) { // Loop until a blank line or end of lines
line = _V_.trim(lines[++i]);
if (!line) { break; }
// Loop until a blank line or end of lines
// Assumeing trim("") returns false for blank lines
while (lines[++i] && (line = _V_.trim(lines[i]))) {
text.push(line);
}
cue.text = text.join('<br/>');
// Add this cue
this.cues.push(cue);
}
}
this.readyState = 2;
this.triggerEvent("loaded");
},
parseCueTime: function(timeText) {
var parts = timeText.split(':'),
time = 0;
// hours => seconds
time += parseFloat(parts[0])*60*60;
// minutes => seconds
time += parseFloat(parts[1])*60;
// get seconds
var seconds = parts[2].split(/\.|,/); // Either . or ,
time += parseFloat(seconds[0]);
// add miliseconds
time = 0,
hours, minutes, other, seconds, ms, flags;
// Check if optional hours place is included
// 00:00:00.000 vs. 00:00.000
if (parts.length == 3) {
hours = parts[0];
minutes = parts[1];
other = parts[2];
} else {
hours = 0;
minutes = parts[0];
other = parts[1];
}
// Break other (seconds, milliseconds, and flags) by spaces
// TODO: Make additional cue layout settings work with flags
other = other.split(/\s+/)
// Remove seconds. Seconds is the first part before any spaces.
seconds = other.splice(0,1)[0];
// Could use either . or , for decimal
seconds = seconds.split(/\.|,/);
// Get milliseconds
ms = parseFloat(seconds[1]);
seconds = seconds[0];
// hours => seconds
time += parseFloat(hours) * 3600;
// minutes => seconds
time += parseFloat(minutes) * 60;
// Add seconds
time += parseFloat(seconds);
// Add milliseconds
if (ms) { time += ms/1000; }
return time;
},
// Update active cues whenever timeupdate events are triggered on the player.
update: function(){
// Assuming all cues are in order by time, and do not overlap
if (this.cues && this.cues.length > 0) {
if (this.cues.length > 0) {
// Get curent player time
var time = this.player.currentTime();
// If current cue should stay showing, don't do anything. Otherwise, find new cue.
if (!this.currentCue || this.currentCue.startTime >= time || this.currentCue.endTime < time) {
var newSubIndex = false,
// Loop in reverse if lastCue is after current time (optimization)
// Meaning the user is scrubbing in reverse or rewinding
reverse = (this.cues[this.lastCueIndex].startTime > time),
// If reverse, step back 1 becase we know it's not the lastCue
i = this.lastCueIndex - (reverse ? 1 : 0);
while (true) { // Loop until broken
if (reverse) { // Looping in reverse
// Stop if no more, or this cue ends before the current time (no earlier cues should apply)
if (i < 0 || this.cues[i].endTime < time) { break; }
// End is greater than time, so if start is less, show this cue
if (this.cues[i].startTime < time) {
newSubIndex = i;
break;
}
i--;
} else { // Looping forward
// Stop if no more, or this cue starts after time (no later cues should apply)
if (i >= this.cues.length || this.cues[i].startTime > time) { break; }
// Start is less than time, so if end is later, show this cue
if (this.cues[i].endTime > time) {
newSubIndex = i;
break;
}
i++;
}
// Check if the new time is outside the time box created by the the last update.
if (this.prevChange === undefined || time < this.prevChange || this.nextChange <= time) {
var cues = this.cues,
// Create a new time box for this state.
newNextChange = this.player.duration(), // Start at beginning of the timeline
newPrevChange = 0, // Start at end
reverse = false, // Set the direction of the loop through the cues. Optimized the cue check.
newCues = [], // Store new active cues.
// Store where in the loop the current active cues are, to provide a smart starting point for the next loop.
firstActiveIndex, lastActiveIndex,
html = "", // Create cue text HTML to add to the display
cue, i, j; // Loop vars
// Check if time is going forwards or backwards (scrubbing/rewinding)
// If we know the direction we can optimize the starting position and direction of the loop through the cues array.
if (time >= this.nextChange || this.nextChange === undefined) { // NextChange should happen
// Forwards, so start at the index of the first active cue and loop forward
i = (this.firstActiveIndex !== undefined) ? this.firstActiveIndex : 0;
} else {
// Backwards, so start at the index of the last active cue and loop backward
reverse = true;
i = (this.lastActiveIndex !== undefined) ? this.lastActiveIndex : cues.length - 1;
}
// Set or clear current cue
if (newSubIndex !== false) {
this.currentCue = this.cues[newSubIndex];
this.lastCueIndex = newSubIndex;
this.updatePlayer(this.currentCue.text);
} else if (this.currentCue) {
this.currentCue = false;
this.updatePlayer("");
while (true) { // Loop until broken
cue = cues[i];
// Cue ended at this point
if (cue.endTime <= time) {
newPrevChange = Math.max(newPrevChange, cue.endTime);
if (cue.active) {
cue.active = false;
}
// No earlier cues should have an active start time.
// Nevermind. Assume first cue could have a duration the same as the video.
// In that case we need to loop all the way back to the beginning.
// if (reverse && cue.startTime) { break; }
// Cue hasn't started
} else if (time < cue.startTime) {
newNextChange = Math.min(newNextChange, cue.startTime);
if (cue.active) {
cue.active = false;
}
// No later cues should have an active start time.
if (!reverse) { break; }
// Cue is current
} else {
if (reverse) {
// Add cue to front of array to keep in time order
newCues.splice(0,0,cue);
// If in reverse, the first current cue is our lastActiveCue
if (lastActiveIndex === undefined) { lastActiveIndex = i; }
firstActiveIndex = i;
} else {
// Add cue to end of array
newCues.push(cue);
// If forward, the first current cue is our firstActiveIndex
if (firstActiveIndex === undefined) { firstActiveIndex = i; }
lastActiveIndex = i;
}
newNextChange = Math.min(newNextChange, cue.endTime);
newPrevChange = Math.max(newPrevChange, cue.startTime);
cue.active = true;
}
if (reverse) {
// Reverse down the array of cues, break if at first
if (i === 0) { break; } else { i--; }
} else {
// Walk up the array fo cues, break if at last
if (i === cues.length - 1) { break; } else { i++; }
}
}
this.activeCues = newCues;
this.nextChange = newNextChange;
this.prevChange = newPrevChange;
this.firstActiveIndex = firstActiveIndex;
this.lastActiveIndex = lastActiveIndex;
this.updateDisplay();
this.triggerEvent("cuechange");
}
}
},
// Update the stored value for the current track kind
// and trigger an event to update all text track displays.
updatePlayer: function(text){
this.player.textTrackValue(this.kind, text);
// Add cue HTML to display
updateDisplay: function(){
var cues = this.activeCues,
html = "",
i=0,j=cues.length;
for (;i<j;i++) {
html += "<span class='vjs-tt-cue'>"+cues[i].text+"</span>";
}
this.el.innerHTML = html;
},
// Set all loop helper values back
reset: function(){
this.nextChange = 0;
this.prevChange = this.player.duration();
this.firstActiveIndex = 0;
this.lastActiveIndex = 0;
}
};
});
// Create specific track types
_V_.CaptionsTrack = _V_.Track.extend({
kind: "captions"
});
_V_.SubtitlesTrack = _V_.Track.extend({
kind: "subtitles"
});
_V_.ChaptersTrack = _V_.Track.extend({
kind: "chapters"
});
/* Text Track Display
================================================================================ */
// Global container for both subtitle and captions text. Simple div container.
_V_.TextTrackDisplay = _V_.Component.extend({
createElement: function(){
return this._super("div", {
className: "vjs-text-track-display"
});
}
});
/* Text Track Menu Items
================================================================================ */
_V_.TextTrackMenuItem = _V_.MenuItem.extend({
init: function(player, options){
var track = this.track = options.track;
// Modify options for parent MenuItem class's init.
options.label = track.label;
options.selected = track["default"];
this._super(player, options);
this.player.addEvent(track.kind + "trackchange", _V_.proxy(this, this.update));
},
onClick: function(){
this._super();
this.player.showTextTrack(this.track.id, this.track.kind);
},
update: function(){
if (this.track.mode == 2) {
this.selected(true);
} else {
this.selected(false);
}
}
});
_V_.OffTextTrackMenuItem = _V_.TextTrackMenuItem.extend({
init: function(player, options){
// Create pseudo track info
// Requires options.kind
options.track = { kind: options.kind, player: player, label: "Off" }
this._super(player, options);
},
onClick: function(){
this._super();
this.player.showTextTrack(this.track.id, this.track.kind);
},
update: function(){
var tracks = this.player.textTracks,
i=0, j=tracks.length, track,
off = true;
for (;i<j;i++) {
track = tracks[i];
if (track.kind == this.track.kind && track.mode == 2) {
off = false;
}
}
if (off) {
this.selected(true);
} else {
this.selected(false);
}
}
});
/* Captions Button
================================================================================ */
_V_.TextTrackButton = _V_.Button.extend({
init: function(player, options){
this._super(player, options);
this.menu = this.createMenu();
if (this.items.length === 0) {
this.hide();
}
},
createMenu: function(){
var menu = new _V_.Menu(this.player);
// Add a title list item to the top
menu.el.appendChild(_V_.createElement("li", {
className: "vjs-menu-title",
innerHTML: _V_.uc(this.kind)
}));
// Add an OFF menu item to turn all tracks off
menu.addItem(new _V_.OffTextTrackMenuItem(this.player, { kind: this.kind }))
this.items = this.createItems();
// Add menu items to the menu
this.each(this.items, function(item){
menu.addItem(item);
});
// Add list to element
this.addComponent(menu);
return menu;
},
// Create a menu item for each text track
createItems: function(){
var items = [];
this.each(this.player.textTracks, function(track){
if (track.kind === this.kind) {
items.push(new _V_.TextTrackMenuItem(this.player, {
track: track
}));
}
});
return items;
},
buildCSSClass: function(){
return this.className + " vjs-menu-button " + this._super();
},
// Focus - Add keyboard functionality to element
onFocus: function(){
// Show the menu, and keep showing when the menu items are in focus
this.menu.lockShowing();
// this.menu.el.style.display = "block";
// When tabbing through, the menu should hide when focus goes from the last menu item to the next tabbed element.
_V_.one(this.menu.el.childNodes[this.menu.el.childNodes.length - 1], "blur", this.proxy(function(){
this.menu.unlockShowing();
}));
},
// Can't turn off list display that we turned on with focus, because list would go away.
onBlur: function(){},
onClick: function(){
// When you click the button it adds focus, which will show the menu indefinitely.
// So we'll remove focus when the mouse leaves the button.
// Focus is needed for tab navigation.
this.one("mouseout", this.proxy(function(){
this.menu.unlockShowing();
this.el.blur();
}));
}
});
_V_.CaptionsButton = _V_.TextTrackButton.extend({
kind: "captions",
buttonText: "Captions",
className: "vjs-captions-button"
});
_V_.SubtitlesButton = _V_.TextTrackButton.extend({
kind: "subtitles",
buttonText: "Subtitles",
className: "vjs-subtitles-button"
});
// Chapters act much differently than other text tracks
// Cues are navigation vs. other tracks of alternative languages
_V_.ChaptersButton = _V_.TextTrackButton.extend({
kind: "chapters",
buttonText: "Chapters",
className: "vjs-chapters-button",
// Create a menu item for each text track
createItems: function(chaptersTrack){
var items = [];
this.each(this.player.textTracks, function(track){
if (track.kind === this.kind) {
items.push(new _V_.TextTrackMenuItem(this.player, {
track: track
}));
}
});
return items;
},
createMenu: function(){
var tracks = this.player.textTracks,
i = 0,
j = tracks.length,
track, chaptersTrack,
items = this.items = [];
for (;i<j;i++) {
track = tracks[i];
if (track.kind == this.kind && track["default"]) {
if (track.readyState < 2) {
this.chaptersTrack = track;
track.addEvent("loaded", this.proxy(this.createMenu));
return;
} else {
chaptersTrack = track;
break;
}
}
}
var menu = this.menu = new _V_.Menu(this.player);
menu.el.appendChild(_V_.createElement("li", {
className: "vjs-menu-title",
innerHTML: _V_.uc(this.kind)
}));
if (chaptersTrack) {
var cues = chaptersTrack.cues,
i = 0, j = cues.length, cue, mi;
for (;i<j;i++) {
cue = cues[i];
mi = new _V_.ChaptersTrackMenuItem(this.player, {
track: chaptersTrack,
cue: cue
});
items.push(mi);
menu.addComponent(mi);
}
}
// Add list to element
this.addComponent(menu);
if (this.items.length > 0) {
this.show();
}
return menu;
}
});
_V_.ChaptersTrackMenuItem = _V_.MenuItem.extend({
init: function(player, options){
var track = this.track = options.track,
cue = this.cue = options.cue,
currentTime = player.currentTime();
// Modify options for parent MenuItem class's init.
options.label = cue.text;
options.selected = (cue.startTime <= currentTime && currentTime < cue.endTime);
this._super(player, options);
track.addEvent("cuechange", _V_.proxy(this, this.update));
},
onClick: function(){
this._super();
this.player.currentTime(this.cue.startTime);
this.update(this.cue.startTime);
},
update: function(time){
var cue = this.cue,
currentTime = this.player.currentTime();
// _V_.log(currentTime, cue.startTime);
if (cue.startTime <= currentTime && currentTime < cue.endTime) {
this.selected(true);
} else {
this.selected(false);
}
}
});
// Add Buttons to controlBar
_V_.merge(_V_.ControlBar.prototype.options.components, {
"subtitlesButton": {},
"captionsButton": {},
"chaptersButton": {}
});
// _V_.Cue = _V_.Component.extend({
// init: function(player, options){
// this._super(player, options);
// }
// });
+21 -29
Ver Arquivo
@@ -5,41 +5,31 @@
<title>QUnit Test Suite</title>
<link rel="stylesheet" href="../design/video-js.css" type="text/css">
<!--[if lt IE 9]>
<script>
document.createElement("video"); // HTML5 Shiv. Must be in <head>.
</script>
<![endif]-->
<!--[if IE]>
<script src="https://getfirebug.com/firebug-lite.js" type="text/javascript" charset="utf-8"></script>
<![endif]-->
<script src="../src/core.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/lib.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/ecma.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/json.js" type="text/javascript" charset="utf-8"></script>
<!-- Video.js Source Files -->
<script src='../src/core.js'></script>
<script src='../src/lib.js'></script>
<script src='../src/component.js'></script>
<script src='../src/controls.js'></script>
<script src='../src/ecma.js'></script>
<script src='../src/events.js'></script>
<script src='../src/json.js'></script>
<script src='../src/player.js'></script>
<script src='../src/tech.js'></script>
<script src='../src/tracks.js'></script>
<script src='../src/setup.js'></script>
<!-- END Video.js Source Files -->
<script src="../src/api.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/events.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/tracks.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/behaviors/behaviors.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/behaviors/seekBar.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/behaviors/volume.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/behaviors/texttrackdisplays.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/controls/bigPlay.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/controls/bar.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/controls/subtitlesBox.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/tech/html5.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/tech/flowplayer.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/autoload.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/log.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" charset="utf-8">
// Easy access to test Flash over HTML5. Add ?flash to URL
if (window.location.href.indexOf("?flash") !== -1) {
_V_.options.techOrder = ["flash"]
}
</script>
<link rel="stylesheet" href="qunit/qunit/qunit.css" type="text/css" media="screen">
<script type="text/javascript" src="qunit/qunit/qunit.js"></script>
@@ -52,5 +42,7 @@
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<div id="qunit-fixture">test markup</div>
<div id="player_box"></div>
</body>
</html>
+248 -98
Ver Arquivo
@@ -1,27 +1,71 @@
var tagCode = '<video id="vid1" class="video-js vjs-default-skin" preload="none" width="640" height="264" data-setup=\'{}\' poster="http://video-js.zencoder.com/oceans-clip.png">';
tagCode+= '<source src="http://video-js.zencoder.com/oceans-clip.mp4" type="video/mp4">';
tagCode+= '<source src="http://video-js.zencoder.com/oceans-clip.webm" type="video/webm">';
tagCode+= '<source src="http://video-js.zencoder.com/oceans-clip.ogv" type="video/ogg; codecs=\'theora, vorbis\'">';
tagCode+= '<track kind="subtitles" src="http://videojs.com/subtitles/demo-subtitles.srt" srclang="en-US" label="English"></track>';
tagCode+= '</video>';
// Potential Future automation
// https://github.com/mcrmfc/qunit_sauce_runner
// http://saucelabs.com/blog/index.php/2011/06/javascript-unit-testing-with-jellyfish-and-ondemand/
// https://github.com/admc/jellyfish/blob/master/test/fun/jfqunit.js
function createVideoTag(id){
var tagCode, tag, attrs;
tagCode = '<video id="vid1" controls class="video-js vjs-default-skin" preload="none" width="640" height="264" data-setup=\'{}\' poster="http://video-js.zencoder.com/oceans-clip.png">';
tagCode+= '<source src="http://video-js.zencoder.com/oceans-clip.mp4" type="video/mp4">';
tagCode+= '<source src="http://video-js.zencoder.com/oceans-clip.webm" type="video/webm">';
tagCode+= '<source src="http://video-js.zencoder.com/oceans-clip.ogv" type="video/ogg; codecs=\'theora, vorbis\'">';
tagCode+= '</video>';
tag = document.createElement("video");
tag.id = "vid1";
tag.controls = true;
tag.className = "video-js vjs-default-skin";
tag.preload = "auto";
tag.width = "640";
tag.height = "264";
tag.poster = "http://video-js.zencoder.com/oceans-clip.png";
source1 = document.createElement("source");
source1.src = "http://video-js.zencoder.com/oceans-clip.mp4";
source1.type = "video/mp4";
tag.appendChild(source1);
source2 = document.createElement("source");
source2.src = "http://video-js.zencoder.com/oceans-clip.webm";
source2.type = "video/webm";
tag.appendChild(source2);
source3 = document.createElement("source");
source3.src = "http://video-js.zencoder.com/oceans-clip.ogv";
source3.type = "video/ogg";
tag.appendChild(source3);
return tag;
}
function playerSetup(){
document.body.innerHTML += tagCode;
_V_.el("player_box").appendChild(createVideoTag())
var vid = document.getElementById("vid1");
this.player = _V_(vid);
stop();
this.player.ready(_V_.proxy(this, function(){
start();
}));
}
function playerTeardown(){
document.body.removeChild(document.getElementById("vid1"));
ok(!document.getElementById("vid1"), "torndown");
stop();
_V_("vid1").destroy();
// document.body.removeChild(document.getElementById("vid1"));
delete this.player;
setTimeout(function(){
start();
}, 500);
}
module("video.js setup", {
module("Video.js setup", {
setup: playerSetup,
teardown: playerTeardown
});
@@ -30,55 +74,6 @@ test("Player Set Up", function() {
ok(this.player);
});
/* Events
================================================================================ */
module("API Events", {
setup: playerSetup,
teardown: playerTeardown
});
// Play Event
test("play", function() {
this.player.addEvent("play", _V_.proxy(this, function(){
start();
ok(true);
}));
this.player.play();
});
// Playing Event
test("playing", function() {
this.player.addEvent("playing", _V_.proxy(this, function(){
start();
ok(true, "playing");
}));
this.player.play();
});
// Pause Event
test("pause", function() {
this.player.addEvent("pause", _V_.proxy(this, function(){
start();
ok(true);
}));
this.player.addEvent("playing", _V_.proxy(this, function(){
this.player.pause();
}));
this.player.play();
});
// Pause Event
test("timeupdate", function() {
this.player.addEvent("timeupdate", _V_.proxy(this, function(){
start();
ok(true);
}));
this.player.addEvent("playing", _V_.proxy(this, function(){
this.player.pause();
}));
this.player.play();
});
/* Methods
================================================================================ */
module("API Methods", {
@@ -86,69 +81,224 @@ module("API Methods", {
teardown: playerTeardown
});
// Play Method
test("play()", function() {
this.player.addEvent("playing", _V_.proxy(this, function(){
function failOnEnded() {
this.player.one("ended", _V_.proxy(this, function(){
start();
ok(true);
}));
}
// Play Method
test("Play", 1, function() {
stop();
this.player.one("playing", _V_.proxy(this, function(){
ok(true);
start();
}));
this.player.play();
failOnEnded.call(this);
});
// Pause Method
test("pause()", function() {
this.player.addEvent("pause", _V_.proxy(this, function(){
start();
ok(true);
}));
this.player.addEvent("playing", _V_.proxy(this, function(){
test("Pause", 1, function() {
stop();
// Flash doesn't currently like calling pause immediately after 'playing'.
this.player.one("timeupdate", _V_.proxy(this, function(){
this.player.pause();
}));
this.player.addEvent("pause", _V_.proxy(this, function(){
ok(true);
start();
}));
this.player.play();
});
test("currentTime()", function() {
// Paused Method
test("Paused", 2, function() {
stop();
// Need video loaded before we can call current time
this.player.addEvent("loadstart", _V_.proxy(this, function(){
this.player.one("timeupdate", _V_.proxy(this, function(){
equal(this.player.paused(), false);
this.player.pause();
}));
this.player.addEvent("pause", _V_.proxy(this, function(){
equal(this.player.paused(), true);
start();
ok(true, "vid loading");
}));
this.player.play();
});
test("currentTime()", 1, function() {
stop();
// Try for 3 time updates, sometimes it updates at 0 seconds.
// var tries = 0;
// Can't rely on just time update because it's faked for Flash.
this.player.one("loadeddata", _V_.proxy(this, function(){
// Watch for timeudpate
this.player.addEvent("timeupdate", _V_.proxy(this, function(){
start();
equal(this.player.currentTime(), 0, "time is 0");
this.player.removeEvent("timeupdate", arguments.callee);
// Test again for later time
this.player.addEvent("timeupdate", _V_.proxy(this, function(){
if (this.player.currentTime() > 0) {
ok(true, "Time is greater than 0.");
start();
notEqual(this.player.currentTime(), 0, "time is not 0");
this.player.removeEvent("timeupdate", arguments.callee);
}));
// Stop and trigger time
stop();
this.player.currentTime(10);
} else {
// tries++;
}
// if (tries >= 3) {
// start();
// }
}));
}));
this.player.play();
});
test("currentTime(seconds)", 2, function() {
stop();
// var afterPlayback = _V_.proxy(this, function(){
// this.player.currentTime(this.player.duration() / 2);
//
// this.player.addEvent("timeupdate", _V_.proxy(this, function(){
// ok(this.player.currentTime() > 0, "Time is greater than 0.");
//
// this.player.pause();
//
// this.player.addEvent("timeupdate", _V_.proxy(this, function(){
// ok(this.player.currentTime() == 0, "Time is 0.");
// start();
// }));
//
// this.player.currentTime(0);
// }));
// });
// Wait for Source to be ready.
this.player.one("loadeddata", _V_.proxy(this, function(){
_V_.log("loadeddata", this.player);
this.player.currentTime(this.player.duration() - 1);
}));
this.player.one("seeked", _V_.proxy(this, function(){
_V_.log("seeked", this.player.currentTime())
ok(this.player.currentTime() > 1, "Time is greater than 1.");
this.player.one("seeked", _V_.proxy(this, function(){
_V_.log("seeked2", this.player.currentTime())
ok(this.player.currentTime() <= 1, "Time is less than 1.");
start();
}));
// Stop and trigger time
stop();
this.player.currentTime(0);
}));
stop();
this.player.load();
this.player.play();
// this.player.one("timeupdate", _V_.proxy(this, function(){
//
// this.player.currentTime(this.player.duration() / 2);
//
// this.player.one("timeupdate", _V_.proxy(this, function(){
// ok(this.player.currentTime() > 0, "Time is greater than 0.");
//
// this.player.pause();
// this.player.currentTime(0);
//
// this.player.one("timeupdate", _V_.proxy(this, function(){
//
// ok(this.player.currentTime() == 0, "Time is 0.");
// start();
//
// }));
//
// }));
//
//
// }));
// Watch for timeudpate
this.player.addEvent("timeupdate", _V_.proxy(this, function(){
start();
notEqual(this.player.currentTime(), 0, "time is not 0");
this.player.removeEvent("timeupdate", arguments.callee);
}));
// Stop and trigger time
stop();
this.player.load();
});
/* Events
================================================================================ */
module("API Events", {
setup: playerSetup,
teardown: playerTeardown
});
var playEventList = []
// Test all playback events
test("Initial Events", 12, function() {
stop(); // Give 30 seconds to run then fail.
var events = [
// "loadstart" // Called during setup
"play",
"playing",
"durationchange",
"loadedmetadata",
"loadeddata",
"loadedalldata",
"progress",
"timeupdate",
"canplay",
"canplaythrough",
"pause",
"ended"
];
// Add an event listener for each event type.
for (var i=0, l=events.length; i<l; i++) {
var evt = events[i];
// Bind player and event name to function so event name value doesn't get overwritten.
this.player.one(evt, _V_.proxy({ player: this.player, evt: evt }, function(){
ok(true, this.evt);
// Once we reach canplaythrough, pause the video and wait for 'paused'.
if (this.evt == "loadedalldata") {
this.player.pause();
// After we've paused, go to the end of the video and wait for 'ended'.
} else if (this.evt == "pause") {
this.player.currentTime(this.player.duration() - 1);
// Flash has an issue calling play too quickly after currentTime. Hopefully we'll fix this.
setTimeout(this.player.proxy(function(){
this.play();
}), 250);
// When we reach ended, we're done. Continue with the test suite.
} else if (this.evt == "ended") {
start();
}
}));
}
this.player.play();
});