jquery.anythingslider.video.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. /*
  2. * AnythingSlider Video Controller 1.3 beta for AnythingSlider v1.6+
  3. * By Rob Garrison (aka Mottie & Fudgey)
  4. * Dual licensed under the MIT and GPL licenses.
  5. */
  6. (function($) {
  7. $.fn.anythingSliderVideo = function(options){
  8. //Set the default values, use comma to separate the settings, example:
  9. var defaults = {
  10. videoID : 'asvideo' // id prefix
  11. };
  12. return this.each(function(){
  13. // make sure a AnythingSlider is attached
  14. var video, tmp, service, sel, base = $(this).data('AnythingSlider');
  15. if (!base) { return; }
  16. video = base.video = {};
  17. // Next update, I may just force users to call the video extension instead of it auto-running on window load
  18. // then they can change the video options in that call instead of the base defaults, and maybe prevent the
  19. // videos being initialized twice on startup (once as a regular video and second time with the API string)
  20. video.options = $.extend({}, defaults, options);
  21. // check if SWFObject is loaded
  22. video.hasSwfo = (typeof(swfobject) !== 'undefined' && swfobject.hasOwnProperty('embedSWF') && typeof(swfobject.embedSWF) === 'function') ? true : false;
  23. video.list = {};
  24. video.hasVid = false;
  25. video.hasEmbed = false;
  26. video.services = $.fn.anythingSliderVideo.services;
  27. video.len = 0; // used to add a unique ID to videos "asvideo#"
  28. video.hasEmbedCount = 0;
  29. video.hasiframeCount = 0;
  30. video.$items = base.$items.filter(':not(.cloned)');
  31. // find and save all known videos
  32. for (service in video.services) {
  33. if (typeof(service) === 'string') {
  34. sel = video.services[service].selector;
  35. video.$items.find(sel).each(function(){
  36. tmp = $(this);
  37. // save panel and video selector in the list
  38. tmp.attr('id', video.options.videoID + video.len);
  39. video.list[video.len] = {
  40. id : video.options.videoID + video.len++,
  41. panel : tmp.closest('.panel')[0],
  42. service : service,
  43. selector : sel,
  44. status : -1 // YouTube uses -1 to mean the video is unstarted
  45. };
  46. video.hasVid = true;
  47. if (sel.match('embed|object')) {
  48. video.hasEmbed = true;
  49. video.hasEmbedCount++;
  50. } else if (sel.match('iframe')) {
  51. video.hasiframeCount++;
  52. }
  53. });
  54. }
  55. }
  56. // Initialize each video, as needed
  57. $.each(video.list, function(i,s){
  58. // s.id = ID, s.panel = slider panel (DOM), s.selector = 'jQuery selector'
  59. var tmp, $tar, vidsrc, opts,
  60. $vid = $(s.panel).find(s.selector),
  61. service = video.services[s.service],
  62. api = service.initAPI || '';
  63. // Initialize embeded video javascript api using SWFObject, if loaded
  64. if (video.hasEmbed && video.hasSwfo && s.selector.match('embed|object')) {
  65. $vid.each(function(){
  66. // Older IE doesn't have an object - just make sure we are wrapping the correct element
  67. $tar = ($(this).parent()[0].tagName === 'OBJECT') ? $(this).parent() : $(this);
  68. vidsrc = ($tar[0].tagName === 'EMBED') ? $tar.attr('src') : $tar.find('embed').attr('src') || $tar.children().filter('[name=movie]').attr('value');
  69. opts = $.extend(true, {}, {
  70. flashvars : null,
  71. params : { allowScriptAccess: 'always', wmode : base.options.addWmodeToObject, allowfullscreen : true },
  72. attr : { 'class' : $tar.attr('class'), 'style' : $tar.attr('style'), 'data-url' : vidsrc }
  73. }, service.embedOpts);
  74. $tar.wrap('<div id="' + s.id + '"></div>');
  75. // use SWFObject if it exists, it replaces the wrapper with the object/embed
  76. swfobject.embedSWF(vidsrc + (api === '' ? '': api + s.id), s.id,
  77. $tar.attr('width'), $tar.attr('height'), '10', null,
  78. opts.flashvars, opts.params, opts.attr, function(){
  79. // run init code if it exists
  80. if (service.hasOwnProperty('init')) {
  81. video.list[i].player = service.init(base, s.id, i);
  82. }
  83. if (i >= video.hasEmbedCount) {
  84. base.$el.trigger('swf_completed', base); // swf callback
  85. }
  86. }
  87. );
  88. });
  89. } else if (s.selector.match('iframe')) {
  90. $vid.each(function(i,v){
  91. vidsrc = $(this).attr('src');
  92. tmp = (vidsrc.match(/\?/g) ? '' : '?') + '&wmode=' + base.options.addWmodeToObject; // string connector & wmode
  93. $(this).attr('src', function(i,r){ return r + tmp + (api === '' ? '': api + s.id); });
  94. });
  95. }
  96. });
  97. // Returns URL parameter; url: http://www.somesite.com?name=hello&id=11111
  98. // Original code from Netlobo.com (http://www.netlobo.com/url_query_string_javascript.html)
  99. video.gup = function(n,s){
  100. n = n.replace(/[\[]/,"\\[").replace(/[\]]/,"\\]");
  101. var p = (new RegExp("[\\?&]"+n+"=([^&#]*)")).exec(s || window.location.href);
  102. return (p===null) ? "" : p[1];
  103. };
  104. // postMessage to iframe - http://benalman.com/projects/jquery-postmessage-plugin/ (FOR IE7)
  105. video.postMsg = function(data, vid){
  106. var $vid = $('#' + vid);
  107. if ($vid.length){
  108. $vid[0].contentWindow.postMessage(data, $vid.attr('src').split('?')[0]);
  109. }
  110. };
  111. // receive message from iframe
  112. // no way to figure out which iframe since the message is from the window
  113. video.message = function(e){
  114. if (e.data) {
  115. if (/infoDelivery/g.test(e.data)) { return; } // ignore youtube video loading spam
  116. var data = $.parseJSON(e.data);
  117. $.each(video.list, function(i,s){
  118. if (video.services[video.list[i].service].hasOwnProperty('message')) {
  119. video.services[video.list[i].service].message(base, data);
  120. }
  121. });
  122. }
  123. };
  124. // toDO = 'cont', 'pause' or 'isPlaying'
  125. video.control = function(toDo){
  126. var i,
  127. s = video.list,
  128. slide = (toDo === 'pause') ? base.$lastPage[0] : base.$currentPage[0],
  129. isPlaying = false;
  130. for (i=0; i < video.len; i++){
  131. if (s[i].panel === slide && video.services[s[i].service].hasOwnProperty(toDo)){
  132. isPlaying = video.services[s[i].service][toDo](base, s[i].id, i);
  133. }
  134. }
  135. return isPlaying;
  136. };
  137. // iframe event listener
  138. if (video.hasiframeCount){
  139. if (window.addEventListener){
  140. window.addEventListener('message', video.message, false);
  141. } else { // IE
  142. window.attachEvent('onmessage', video.message, false);
  143. }
  144. }
  145. // bind to events
  146. base.$el
  147. .bind('slide_init', function(){
  148. video.control('pause');
  149. })
  150. .bind('slide_complete', function(){
  151. video.control('cont');
  152. });
  153. base.options.isVideoPlaying = function(){ return video.control('isPlaying'); };
  154. });
  155. };
  156. /* Each video service is set up as follows
  157. * service-name : {
  158. * // initialization
  159. * selector : 'object[data-url*=service], embed[src*=service]', // required: jQuery selector used to find the video ('video' or 'iframe[src*=service]' are other examples)
  160. * initAPI : 'string added to the URL to initialize the API', // optional: the string must end with a parameter pointing to the video id (e.g. "&player_id=")
  161. * embedOpts : { flashvars: {}, params: {}, attr: {} }, // optional: add any required flashvars, parameters or attributes to initialize the API
  162. * // video startup functions
  163. * init : function(base, vid, index){ }, // optional: include any additional initialization code here; function called AFTER the embeded video is added using SWFObject
  164. * // required functions
  165. * cont : function(base, vid, index){ }, // required: continue play if video was previously played
  166. * pause : function(base, vid, index){ }, // required: pause ALL videos
  167. * message : function(base, data){ }, // required for iframe: process data received from iframe and update the video status for the "isPlaying" function
  168. * isPlaying : function(base, vid, index){ } // required: return true if video is playing and return false if not playing (paused or ended)
  169. * }
  170. *
  171. * Function variables
  172. * base (object) = plugin base, all video values/functions are stored in base.video
  173. * vid (string) is the ID of the video: vid = "asvideo1"; so jQuery needs a "#" in front... "#" + videoID option default ("asvideo") + index (e.g. "1"); each video matching a service will have a unquie vid
  174. * index (number) is the unique video number from the vid (starts from zero)
  175. *
  176. * var list = base.video.list[index]; list will contain:
  177. * list.id = vid
  178. * list.service = service name (e.g. 'video', 'vimeo1', 'vimeo2', etc)
  179. * list.selector = 'jQuery selector' (e.g. 'video', 'object[data-url*=vimeo]', 'iframe[src*=vimeo]', etc)
  180. * list.panel = AnythingSlider panel DOM object. So you can target the video using $(list[index].panel).find(list[index].service) or $('#' + vid)
  181. * list.status = video status, updated by the iframe event listeners added in the video service "ready" function; see examples below
  182. */
  183. $.fn.anythingSliderVideo.services = {
  184. // *** HTML5 video ***
  185. video : {
  186. selector : 'video',
  187. cont : function(base, vid, index){
  188. var $vid = $('#' + vid);
  189. if ($vid.length && $vid[0].paused && $vid[0].currentTime > 0 && !$vid[0].ended) {
  190. $vid[0].play();
  191. }
  192. },
  193. pause : function(base, vid){
  194. // pause ALL videos on the page
  195. $('video').each(function(){
  196. if (typeof(this.pause) !== 'undefined') { this.pause(); } // throws an error in older ie without this
  197. });
  198. },
  199. isPlaying : function(base, vid, index){
  200. var $vid = $('#' + vid);
  201. // media.paused seems to be the only way to determine if a video is playing
  202. return ($vid.length && typeof($vid[0].pause) !== 'undefined' && !$vid[0].paused && !$vid[0].ended) ? true : false;
  203. }
  204. },
  205. // *** Vimeo iframe *** isolated demo: http://jsfiddle.net/Mottie/GxwEX/
  206. vimeo1 : {
  207. selector : 'iframe[src*=vimeo]',
  208. initAPI : '&api=1&player_id=', // video ID added to the end
  209. cont : function(base, vid, index){
  210. if (base.options.resumeOnVisible && base.video.list[index].status === 'pause'){
  211. // Commands sent to the iframe originally had "JSON.stringify" applied to them,
  212. // but not all browsers support this, so it's just as easy to wrap it in quotes.
  213. base.video.postMsg('{"method":"play"}', vid);
  214. }
  215. },
  216. pause : function(base, vid){
  217. // pause ALL videos on the page
  218. $('iframe[src*=vimeo]').each(function(){
  219. base.video.postMsg('{"method":"pause"}', this.id);
  220. });
  221. },
  222. message : function(base, data){
  223. // *** VIMEO *** iframe uses data.player_id
  224. var index, vid = data.player_id || ''; // vid = data.player_id (unique to vimeo)
  225. if (vid !== ''){
  226. index = vid.replace(base.video.options.videoID, '');
  227. if (data.event === 'ready') {
  228. // Vimeo ready, add additional event listeners for video status
  229. base.video.postMsg('{"method":"addEventListener","value":"play"}', vid);
  230. base.video.postMsg('{"method":"addEventListener","value":"pause"}', vid);
  231. base.video.postMsg('{"method":"addEventListener","value":"finish"}', vid);
  232. }
  233. // update current status - vimeo puts it in data.event
  234. if (base.video.list[index]) { base.video.list[index].status = data.event; }
  235. }
  236. },
  237. isPlaying : function(base, vid, index){
  238. return (base.video.list[index].status === 'play') ? true : false;
  239. }
  240. },
  241. // *** Embeded Vimeo ***
  242. // SWFObject adds the url to the object data
  243. // using param as a selector, the script above looks for the parent if it sees "param"
  244. vimeo2 : {
  245. selector : 'object[data-url*=vimeo], embed[src*=vimeo]',
  246. embedOpts : { flashvars : { api : 1 } },
  247. cont : function(base, vid, index) {
  248. if (base.options.resumeOnVisible) {
  249. var $vid = $('#' + vid);
  250. // continue video if previously played & not finished (api_finish doesn't seem to exist) - duration can be a decimal number, so subtract it and look at the difference (2 seconds here)
  251. if (typeof($vid[0].api_play) === 'function' && $vid[0].api_paused() && $vid[0].api_getCurrentTime() !== 0 && ($vid[0].api_getDuration() - $vid[0].api_getCurrentTime()) > 2) {
  252. $vid[0].api_play();
  253. }
  254. }
  255. },
  256. pause : function(base, vid){
  257. // find ALL videos and pause them, just in case
  258. $('object[data-url*=vimeo], embed[src*=vimeo]').each(function(){
  259. var el = (this.tagName === 'EMBED') ? $(this).parent()[0] : this;
  260. if (typeof(el.api_pause) === 'function') {
  261. el.api_pause();
  262. }
  263. });
  264. },
  265. isPlaying : function(base, vid, index){
  266. var $vid = $('#' + vid);
  267. return (typeof($vid[0].api_paused) === 'function' && !$vid[0].api_paused()) ? true : false;
  268. }
  269. },
  270. // *** iframe YouTube *** isolated demo: http://jsfiddle.net/Mottie/qk5MY/
  271. youtube1 : {
  272. selector : 'iframe[src*=youtube]',
  273. // "iv_load_policy=3" should turn off annotations on init, but doesn't seem to
  274. initAPI : '&iv_load_policy=3&enablejsapi=1&playerapiid=',
  275. cont : function(base, vid, index){
  276. if (base.options.resumeOnVisible && base.video.list[index].status === 2){
  277. base.video.postMsg('{"event":"command","func":"playVideo"}', vid);
  278. }
  279. },
  280. pause : function(base, vid, index){
  281. // pause ALL videos on the page - in IE, pausing a video means it will continue when next seen =(
  282. $('iframe[src*=youtube]').each(function(){
  283. // if (this.id !== vid || (this.id === vid && base.video.list[index].status >= 0)) { // trying to fix the continue video problem; this only breaks it
  284. base.video.postMsg('{"event":"command","func":"pauseVideo"}', vid);
  285. // }
  286. });
  287. },
  288. message : function(base, data){
  289. if (data.event === 'infoDelivery') { return; } // ignore youtube video loading spam
  290. // *** YouTube *** iframe returns an embeded url (data.info.videoUrl) but no video id...
  291. if (data.info && data.info.videoUrl) {
  292. // figure out vid for youtube
  293. // data.info.videoURL = http://www.youtube.com/watch?v=###########&feature=player_embedded
  294. var url = base.video.gup('v', data.info.videoUrl), // end up with ###########, now find it
  295. v = $('iframe[src*=' + url + ']'), vid, index;
  296. // iframe src changes when watching related videos; so there is no way to tell which video has an update
  297. if (v.length) {
  298. vid = v[0].id;
  299. index = vid.replace(base.video.options.videoID, '');
  300. // YouTube ready, add additional event listeners for video status. BUT this never fires off =(
  301. // Fixing this may solve the continue problem
  302. if (data.event === 'onReady') {
  303. base.video.postMsg('{"event":"listening","func":"onStateChange"}', vid);
  304. }
  305. // Update status, so the "isPlaying" function can access it
  306. if (data.event === 'onStateChange' && base.video.list[index]) {
  307. // update list with current status; data.info.playerState = YouTube
  308. base.video.list[index].status = data.info.playerState;
  309. }
  310. }
  311. }
  312. },
  313. isPlaying : function(base, vid, index){
  314. var status = base.video.list[index].status;
  315. // state: unstarted (-1), ended (0), playing (1), paused (2), buffering (3), video cued (5).
  316. return (status === 1 || status > 2) ? true : false;
  317. }
  318. },
  319. // *** Embeded YouTube ***
  320. // include embed for IE; SWFObject adds the url to the object data attribute
  321. youtube2 : {
  322. selector : 'object[data-url*=youtube], embed[src*=youtube]',
  323. initAPI : '&iv_load_policy=3&enablejsapi=1&version=3&playerapiid=', // video ID added to the end
  324. // YouTube - player states: unstarted (-1), ended (0), playing (1), paused (2), buffering (3), video cued (5).
  325. cont : function(base, vid, index) {
  326. if (base.options.resumeOnVisible) {
  327. var $vid = $('#' + vid);
  328. // continue video if previously played and not cued
  329. if ($vid.length && typeof($vid[0].getPlayerState) === 'function' && $vid[0].getPlayerState() > 0) {
  330. $vid[0].playVideo();
  331. }
  332. }
  333. },
  334. pause : function(base, vid){
  335. // find ALL videos and pause them, just in case
  336. $('object[data-url*=youtube], embed[src*=youtube]').each(function(){
  337. var el = (this.tagName === 'EMBED') ? $(this).parent()[0] : this;
  338. // player states: unstarted (-1), ended (0), playing (1), paused (2), buffering (3), video cued (5).
  339. if (typeof(el.getPlayerState) === 'function' && el.getPlayerState() > 0) {
  340. // pause video if not autoplaying (if already initialized)
  341. el.pauseVideo();
  342. }
  343. });
  344. },
  345. isPlaying : function(base, vid){
  346. var $vid = $('#' + vid);
  347. return (typeof($vid[0].getPlayerState) === 'function' && ($vid[0].getPlayerState() === 1 || $vid[0].getPlayerState() > 2)) ? true : false;
  348. }
  349. }
  350. };
  351. })(jQuery);
  352. // Initialize video extension automatically
  353. jQuery(window).load(function(){
  354. jQuery('.anythingBase').anythingSliderVideo();
  355. });