mediaelementplayer.js 115 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213
  1. /*!
  2. *
  3. * MediaElementPlayer
  4. * http://mediaelementjs.com/
  5. *
  6. * Creates a controller bar for HTML5 <video> add <audio> tags
  7. * using jQuery and MediaElement.js (HTML5 Flash/Silverlight wrapper)
  8. *
  9. * Copyright 2010-2013, John Dyer (http://j.hn/)
  10. * License: MIT
  11. *
  12. */
  13. if (typeof jQuery != 'undefined') {
  14. mejs.$ = jQuery;
  15. } else if (typeof Zepto != 'undefined') {
  16. mejs.$ = Zepto;
  17. // define `outerWidth` method which has not been realized in Zepto
  18. Zepto.fn.outerWidth = function(includeMargin) {
  19. var width = $(this).width();
  20. if (includeMargin) {
  21. width += parseInt($(this).css('margin-right'), 10);
  22. width += parseInt($(this).css('margin-left'), 10);
  23. }
  24. return width
  25. }
  26. } else if (typeof ender != 'undefined') {
  27. mejs.$ = ender;
  28. }
  29. (function ($) {
  30. // default player values
  31. mejs.MepDefaults = {
  32. // url to poster (to fix iOS 3.x)
  33. poster: '',
  34. // When the video is ended, we can show the poster.
  35. showPosterWhenEnded: false,
  36. // default if the <video width> is not specified
  37. defaultVideoWidth: 480,
  38. // default if the <video height> is not specified
  39. defaultVideoHeight: 270,
  40. // if set, overrides <video width>
  41. videoWidth: -1,
  42. // if set, overrides <video height>
  43. videoHeight: -1,
  44. // default if the user doesn't specify
  45. defaultAudioWidth: 400,
  46. // default if the user doesn't specify
  47. defaultAudioHeight: 30,
  48. // default amount to move back when back key is pressed
  49. defaultSeekBackwardInterval: function(media) {
  50. return (media.duration * 0.05);
  51. },
  52. // default amount to move forward when forward key is pressed
  53. defaultSeekForwardInterval: function(media) {
  54. return (media.duration * 0.05);
  55. },
  56. // set dimensions via JS instead of CSS
  57. setDimensions: true,
  58. // width of audio player
  59. audioWidth: -1,
  60. // height of audio player
  61. audioHeight: -1,
  62. // initial volume when the player starts (overrided by user cookie)
  63. startVolume: 0.8,
  64. // useful for <audio> player loops
  65. loop: false,
  66. // rewind to beginning when media ends
  67. autoRewind: true,
  68. // resize to media dimensions
  69. enableAutosize: true,
  70. /*
  71. * Time format to use. Default: 'mm:ss'
  72. * Supported units:
  73. * h: hour
  74. * m: minute
  75. * s: second
  76. * f: frame count
  77. * When using 'hh', 'mm', 'ss' or 'ff' we always display 2 digits.
  78. * If you use 'h', 'm', 's' or 'f' we display 1 digit if possible.
  79. *
  80. * Example to display 75 seconds:
  81. * Format 'mm:ss': 01:15
  82. * Format 'm:ss': 1:15
  83. * Format 'm:s': 1:15
  84. */
  85. timeFormat: '',
  86. // forces the hour marker (##:00:00)
  87. alwaysShowHours: false,
  88. // show framecount in timecode (##:00:00:00)
  89. showTimecodeFrameCount: false,
  90. // used when showTimecodeFrameCount is set to true
  91. framesPerSecond: 25,
  92. // automatically calculate the width of the progress bar based on the sizes of other elements
  93. autosizeProgress : true,
  94. // Hide controls when playing and mouse is not over the video
  95. alwaysShowControls: false,
  96. // Display the video control
  97. hideVideoControlsOnLoad: false,
  98. // Enable click video element to toggle play/pause
  99. clickToPlayPause: true,
  100. // Time in ms to hide controls
  101. controlsTimeoutDefault: 1500,
  102. // Time in ms to trigger the timer when mouse moves
  103. controlsTimeoutMouseEnter: 2500,
  104. // Time in ms to trigger the timer when mouse leaves
  105. controlsTimeoutMouseLeave: 1000,
  106. // force iPad's native controls
  107. iPadUseNativeControls: false,
  108. // force iPhone's native controls
  109. iPhoneUseNativeControls: false,
  110. // force Android's native controls
  111. AndroidUseNativeControls: false,
  112. // features to show
  113. features: ['playpause','current','progress','duration','tracks','volume','fullscreen'],
  114. // only for dynamic
  115. isVideo: true,
  116. // stretching modes (auto, fill, responsive, none)
  117. stretching: 'auto',
  118. // turns keyboard support on and off for this instance
  119. enableKeyboard: true,
  120. // when this player starts, it will pause other players
  121. pauseOtherPlayers: true,
  122. // array of keyboard actions such as play pause
  123. keyActions: [
  124. {
  125. keys: [
  126. 32, // SPACE
  127. 179 // GOOGLE play/pause button
  128. ],
  129. action: function(player, media, key, event) {
  130. if (!mejs.MediaFeatures.isFirefox) {
  131. if (media.paused || media.ended) {
  132. media.play();
  133. } else {
  134. media.pause();
  135. }
  136. }
  137. }
  138. },
  139. {
  140. keys: [38], // UP
  141. action: function(player, media, key, event) {
  142. player.container.find('.mejs-volume-slider').css('display','block');
  143. if (player.isVideo) {
  144. player.showControls();
  145. player.startControlsTimer();
  146. }
  147. var newVolume = Math.min(media.volume + 0.1, 1);
  148. media.setVolume(newVolume);
  149. }
  150. },
  151. {
  152. keys: [40], // DOWN
  153. action: function(player, media, key, event) {
  154. player.container.find('.mejs-volume-slider').css('display','block');
  155. if (player.isVideo) {
  156. player.showControls();
  157. player.startControlsTimer();
  158. }
  159. var newVolume = Math.max(media.volume - 0.1, 0);
  160. media.setVolume(newVolume);
  161. }
  162. },
  163. {
  164. keys: [
  165. 37, // LEFT
  166. 227 // Google TV rewind
  167. ],
  168. action: function(player, media, key, event) {
  169. if (!isNaN(media.duration) && media.duration > 0) {
  170. if (player.isVideo) {
  171. player.showControls();
  172. player.startControlsTimer();
  173. }
  174. // 5%
  175. var newTime = Math.max(media.currentTime - player.options.defaultSeekBackwardInterval(media), 0);
  176. media.setCurrentTime(newTime);
  177. }
  178. }
  179. },
  180. {
  181. keys: [
  182. 39, // RIGHT
  183. 228 // Google TV forward
  184. ],
  185. action: function(player, media, key, event) {
  186. if (!isNaN(media.duration) && media.duration > 0) {
  187. if (player.isVideo) {
  188. player.showControls();
  189. player.startControlsTimer();
  190. }
  191. // 5%
  192. var newTime = Math.min(media.currentTime + player.options.defaultSeekForwardInterval(media), media.duration);
  193. media.setCurrentTime(newTime);
  194. }
  195. }
  196. },
  197. {
  198. keys: [70], // F
  199. action: function(player, media, key, event) {
  200. if (typeof player.enterFullScreen != 'undefined') {
  201. if (player.isFullScreen) {
  202. player.exitFullScreen();
  203. } else {
  204. player.enterFullScreen();
  205. }
  206. }
  207. }
  208. },
  209. {
  210. keys: [77], // M
  211. action: function(player, media, key, event) {
  212. player.container.find('.mejs-volume-slider').css('display','block');
  213. if (player.isVideo) {
  214. player.showControls();
  215. player.startControlsTimer();
  216. }
  217. if (player.media.muted) {
  218. player.setMuted(false);
  219. } else {
  220. player.setMuted(true);
  221. }
  222. }
  223. }
  224. ]
  225. };
  226. mejs.mepIndex = 0;
  227. mejs.players = {};
  228. // wraps a MediaElement object in player controls
  229. mejs.MediaElementPlayer = function(node, o) {
  230. // enforce object, even without "new" (via John Resig)
  231. if ( !(this instanceof mejs.MediaElementPlayer) ) {
  232. return new mejs.MediaElementPlayer(node, o);
  233. }
  234. var t = this;
  235. // these will be reset after the MediaElement.success fires
  236. t.$media = t.$node = $(node);
  237. t.node = t.media = t.$media[0];
  238. if(!t.node) {
  239. return;
  240. }
  241. // check for existing player
  242. if (typeof t.node.player != 'undefined') {
  243. return t.node.player;
  244. }
  245. // try to get options from data-mejsoptions
  246. if (typeof o == 'undefined') {
  247. o = t.$node.data('mejsoptions');
  248. }
  249. // extend default options
  250. t.options = $.extend({},mejs.MepDefaults,o);
  251. if (!t.options.timeFormat) {
  252. // Generate the time format according to options
  253. t.options.timeFormat = 'mm:ss';
  254. if (t.options.alwaysShowHours) {
  255. t.options.timeFormat = 'hh:mm:ss';
  256. }
  257. if (t.options.showTimecodeFrameCount) {
  258. t.options.timeFormat += ':ff';
  259. }
  260. }
  261. mejs.Utility.calculateTimeFormat(0, t.options, t.options.framesPerSecond || 25);
  262. // unique ID
  263. t.id = 'mep_' + mejs.mepIndex++;
  264. // add to player array (for focus events)
  265. mejs.players[t.id] = t;
  266. // start up
  267. t.init();
  268. return t;
  269. };
  270. // actual player
  271. mejs.MediaElementPlayer.prototype = {
  272. hasFocus: false,
  273. controlsAreVisible: true,
  274. init: function() {
  275. var
  276. t = this,
  277. mf = mejs.MediaFeatures,
  278. // options for MediaElement (shim)
  279. meOptions = $.extend(true, {}, t.options, {
  280. success: function(media, domNode) { t.meReady(media, domNode); },
  281. error: function(e) { t.handleError(e);}
  282. }),
  283. tagName = t.media.tagName.toLowerCase();
  284. t.isDynamic = (tagName !== 'audio' && tagName !== 'video');
  285. if (t.isDynamic) {
  286. // get video from src or href?
  287. t.isVideo = t.options.isVideo;
  288. } else {
  289. t.isVideo = (tagName !== 'audio' && t.options.isVideo);
  290. }
  291. // use native controls in iPad, iPhone, and Android
  292. if ((mf.isiPad && t.options.iPadUseNativeControls) || (mf.isiPhone && t.options.iPhoneUseNativeControls)) {
  293. // add controls and stop
  294. t.$media.attr('controls', 'controls');
  295. // attempt to fix iOS 3 bug
  296. //t.$media.removeAttr('poster');
  297. // no Issue found on iOS3 -ttroxell
  298. // override Apple's autoplay override for iPads
  299. if (mf.isiPad && t.media.getAttribute('autoplay') !== null) {
  300. t.play();
  301. }
  302. } else if (mf.isAndroid && t.options.AndroidUseNativeControls) {
  303. // leave default player
  304. } else if (t.isVideo || (!t.isVideo && t.options.features.length)) {
  305. // DESKTOP: use MediaElementPlayer controls
  306. // remove native controls
  307. t.$media.removeAttr('controls');
  308. var videoPlayerTitle = t.isVideo ?
  309. mejs.i18n.t('mejs.video-player') : mejs.i18n.t('mejs.audio-player');
  310. // insert description for screen readers
  311. $('<span class="mejs-offscreen">' + videoPlayerTitle + '</span>').insertBefore(t.$media);
  312. // build container
  313. t.container =
  314. $('<div id="' + t.id + '" class="mejs-container ' + (mejs.MediaFeatures.svgAsImg ? 'svg' : 'no-svg') +
  315. '" tabindex="0" role="application" aria-label="' + videoPlayerTitle + '">'+
  316. '<div class="mejs-inner">'+
  317. '<div class="mejs-mediaelement"></div>'+
  318. '<div class="mejs-layers"></div>'+
  319. '<div class="mejs-controls"></div>'+
  320. '<div class="mejs-clear"></div>'+
  321. '</div>' +
  322. '</div>')
  323. .addClass(t.$media[0].className)
  324. .insertBefore(t.$media)
  325. .focus(function ( e ) {
  326. if( !t.controlsAreVisible && !t.hasFocus && t.controlsEnabled) {
  327. t.showControls(true);
  328. // In versions older than IE11, the focus causes the playbar to be displayed
  329. // if user clicks on the Play/Pause button in the control bar once it attempts
  330. // to hide it
  331. if (!t.hasMsNativeFullScreen) {
  332. // If e.relatedTarget appears before container, send focus to play button,
  333. // else send focus to last control button.
  334. var btnSelector = '.mejs-playpause-button > button';
  335. if (mejs.Utility.isNodeAfter(e.relatedTarget, t.container[0])) {
  336. btnSelector = '.mejs-controls .mejs-button:last-child > button';
  337. }
  338. var button = t.container.find(btnSelector);
  339. button.focus();
  340. }
  341. }
  342. });
  343. // When no elements in controls, hide bar completely
  344. if (!t.options.features.length) {
  345. t.container.css('background', 'transparent').find('.mejs-controls').hide();
  346. }
  347. if (t.isVideo && t.options.stretching === 'fill' && !t.container.parent('mejs-fill-container').length) {
  348. // outer container
  349. t.outerContainer = t.$media.parent();
  350. t.container.wrap('<div class="mejs-fill-container"/>');
  351. }
  352. // add classes for user and content
  353. t.container.addClass(
  354. (mf.isAndroid ? 'mejs-android ' : '') +
  355. (mf.isiOS ? 'mejs-ios ' : '') +
  356. (mf.isiPad ? 'mejs-ipad ' : '') +
  357. (mf.isiPhone ? 'mejs-iphone ' : '') +
  358. (t.isVideo ? 'mejs-video ' : 'mejs-audio ')
  359. );
  360. // move the <video/video> tag into the right spot
  361. t.container.find('.mejs-mediaelement').append(t.$media);
  362. // needs to be assigned here, after iOS remap
  363. t.node.player = t;
  364. // find parts
  365. t.controls = t.container.find('.mejs-controls');
  366. t.layers = t.container.find('.mejs-layers');
  367. // determine the size
  368. /* size priority:
  369. (1) videoWidth (forced),
  370. (2) style="width;height;"
  371. (3) width attribute,
  372. (4) defaultVideoWidth (for unspecified cases)
  373. */
  374. var tagType = (t.isVideo ? 'video' : 'audio'),
  375. capsTagName = tagType.substring(0,1).toUpperCase() + tagType.substring(1);
  376. if (t.options[tagType + 'Width'] > 0 || t.options[tagType + 'Width'].toString().indexOf('%') > -1) {
  377. t.width = t.options[tagType + 'Width'];
  378. } else if (t.media.style.width !== '' && t.media.style.width !== null) {
  379. t.width = t.media.style.width;
  380. } else if (t.media.getAttribute('width') !== null) {
  381. t.width = t.$media.attr('width');
  382. } else {
  383. t.width = t.options['default' + capsTagName + 'Width'];
  384. }
  385. if (t.options[tagType + 'Height'] > 0 || t.options[tagType + 'Height'].toString().indexOf('%') > -1) {
  386. t.height = t.options[tagType + 'Height'];
  387. } else if (t.media.style.height !== '' && t.media.style.height !== null) {
  388. t.height = t.media.style.height;
  389. } else if (t.$media[0].getAttribute('height') !== null) {
  390. t.height = t.$media.attr('height');
  391. } else {
  392. t.height = t.options['default' + capsTagName + 'Height'];
  393. }
  394. // set the size, while we wait for the plugins to load below
  395. t.setPlayerSize(t.width, t.height);
  396. // create MediaElementShim
  397. meOptions.pluginWidth = t.width;
  398. meOptions.pluginHeight = t.height;
  399. }
  400. // Hide media completely for audio that doesn't have any features
  401. else if (!t.isVideo && !t.options.features.length) {
  402. t.$media.hide();
  403. }
  404. // create MediaElement shim
  405. mejs.MediaElement(t.$media[0], meOptions);
  406. if (typeof(t.container) !== 'undefined' && t.options.features.length && t.controlsAreVisible) {
  407. // controls are shown when loaded
  408. t.container.trigger('controlsshown');
  409. }
  410. },
  411. showControls: function(doAnimation) {
  412. var t = this;
  413. doAnimation = typeof doAnimation == 'undefined' || doAnimation;
  414. if (t.controlsAreVisible)
  415. return;
  416. if (doAnimation) {
  417. t.controls
  418. .removeClass('mejs-offscreen')
  419. .stop(true, true).fadeIn(200, function() {
  420. t.controlsAreVisible = true;
  421. t.container.trigger('controlsshown');
  422. });
  423. // any additional controls people might add and want to hide
  424. t.container.find('.mejs-control')
  425. .removeClass('mejs-offscreen')
  426. .stop(true, true).fadeIn(200, function() {t.controlsAreVisible = true;});
  427. } else {
  428. t.controls
  429. .removeClass('mejs-offscreen')
  430. .css('display','block');
  431. // any additional controls people might add and want to hide
  432. t.container.find('.mejs-control')
  433. .removeClass('mejs-offscreen')
  434. .css('display','block');
  435. t.controlsAreVisible = true;
  436. t.container.trigger('controlsshown');
  437. }
  438. t.setControlsSize();
  439. },
  440. hideControls: function(doAnimation) {
  441. var t = this;
  442. doAnimation = typeof doAnimation == 'undefined' || doAnimation;
  443. if (!t.controlsAreVisible || t.options.alwaysShowControls || t.keyboardAction || t.media.paused || t.media.ended)
  444. return;
  445. if (doAnimation) {
  446. // fade out main controls
  447. t.controls.stop(true, true).fadeOut(200, function() {
  448. $(this)
  449. .addClass('mejs-offscreen')
  450. .css('display','block');
  451. t.controlsAreVisible = false;
  452. t.container.trigger('controlshidden');
  453. });
  454. // any additional controls people might add and want to hide
  455. t.container.find('.mejs-control').stop(true, true).fadeOut(200, function() {
  456. $(this)
  457. .addClass('mejs-offscreen')
  458. .css('display','block');
  459. });
  460. } else {
  461. // hide main controls
  462. t.controls
  463. .addClass('mejs-offscreen')
  464. .css('display','block');
  465. // hide others
  466. t.container.find('.mejs-control')
  467. .addClass('mejs-offscreen')
  468. .css('display','block');
  469. t.controlsAreVisible = false;
  470. t.container.trigger('controlshidden');
  471. }
  472. },
  473. controlsTimer: null,
  474. startControlsTimer: function(timeout) {
  475. var t = this;
  476. timeout = typeof timeout != 'undefined' ? timeout : t.options.controlsTimeoutDefault;
  477. t.killControlsTimer('start');
  478. t.controlsTimer = setTimeout(function() {
  479. //
  480. t.hideControls();
  481. t.killControlsTimer('hide');
  482. }, timeout);
  483. },
  484. killControlsTimer: function(src) {
  485. var t = this;
  486. if (t.controlsTimer !== null) {
  487. clearTimeout(t.controlsTimer);
  488. delete t.controlsTimer;
  489. t.controlsTimer = null;
  490. }
  491. },
  492. controlsEnabled: true,
  493. disableControls: function() {
  494. var t= this;
  495. t.killControlsTimer();
  496. t.hideControls(false);
  497. this.controlsEnabled = false;
  498. },
  499. enableControls: function() {
  500. var t= this;
  501. t.showControls(false);
  502. t.controlsEnabled = true;
  503. },
  504. // Sets up all controls and events
  505. meReady: function(media, domNode) {
  506. var
  507. t = this,
  508. mf = mejs.MediaFeatures,
  509. autoplayAttr = domNode.getAttribute('autoplay'),
  510. autoplay = !(typeof autoplayAttr == 'undefined' || autoplayAttr === null || autoplayAttr === 'false'),
  511. featureIndex,
  512. feature;
  513. // make sure it can't create itself again if a plugin reloads
  514. if (t.created) {
  515. return;
  516. } else {
  517. t.created = true;
  518. }
  519. t.media = media;
  520. t.domNode = domNode;
  521. if (!(mf.isAndroid && t.options.AndroidUseNativeControls) && !(mf.isiPad && t.options.iPadUseNativeControls) && !(mf.isiPhone && t.options.iPhoneUseNativeControls)) {
  522. // In the event that no features are specified for audio,
  523. // create only MediaElement instance rather than
  524. // doing all the work to create a full player
  525. if (!t.isVideo && !t.options.features.length) {
  526. // force autoplay for HTML5
  527. if (autoplay && media.pluginType == 'native') {
  528. t.play();
  529. }
  530. if (t.options.success) {
  531. if (typeof t.options.success == 'string') {
  532. window[t.options.success](t.media, t.domNode, t);
  533. } else {
  534. t.options.success(t.media, t.domNode, t);
  535. }
  536. }
  537. return;
  538. }
  539. // two built in features
  540. t.buildposter(t, t.controls, t.layers, t.media);
  541. t.buildkeyboard(t, t.controls, t.layers, t.media);
  542. t.buildoverlays(t, t.controls, t.layers, t.media);
  543. // grab for use by features
  544. t.findTracks();
  545. // add user-defined features/controls
  546. for (featureIndex in t.options.features) {
  547. feature = t.options.features[featureIndex];
  548. if (t['build' + feature]) {
  549. try {
  550. t['build' + feature](t, t.controls, t.layers, t.media);
  551. } catch (e) {
  552. // TODO: report control error
  553. //throw e;
  554. }
  555. }
  556. }
  557. t.container.trigger('controlsready');
  558. // reset all layers and controls
  559. t.setPlayerSize(t.width, t.height);
  560. t.setControlsSize();
  561. // controls fade
  562. if (t.isVideo) {
  563. if (mejs.MediaFeatures.hasTouch && !t.options.alwaysShowControls) {
  564. // for touch devices (iOS, Android)
  565. // show/hide without animation on touch
  566. t.$media.bind('touchstart', function() {
  567. // toggle controls
  568. if (t.controlsAreVisible) {
  569. t.hideControls(false);
  570. } else {
  571. if (t.controlsEnabled) {
  572. t.showControls(false);
  573. }
  574. }
  575. });
  576. } else {
  577. // create callback here since it needs access to current
  578. // MediaElement object
  579. t.clickToPlayPauseCallback = function() {
  580. //
  581. if (t.options.clickToPlayPause) {
  582. if (t.media.paused) {
  583. t.play();
  584. } else {
  585. t.pause();
  586. }
  587. var button = t.$media.closest('.mejs-container').find('.mejs-overlay-button'),
  588. pressed = button.attr('aria-pressed');
  589. button.attr('aria-pressed', !pressed);
  590. }
  591. };
  592. // click to play/pause
  593. t.media.addEventListener('click', t.clickToPlayPauseCallback, false);
  594. // show/hide controls
  595. t.container
  596. .bind('mouseenter', function () {
  597. if (t.controlsEnabled) {
  598. if (!t.options.alwaysShowControls ) {
  599. t.killControlsTimer('enter');
  600. t.showControls();
  601. t.startControlsTimer(t.options.controlsTimeoutMouseEnter);
  602. }
  603. }
  604. })
  605. .bind('mousemove', function() {
  606. if (t.controlsEnabled) {
  607. if (!t.controlsAreVisible) {
  608. t.showControls();
  609. }
  610. if (!t.options.alwaysShowControls) {
  611. t.startControlsTimer(t.options.controlsTimeoutMouseEnter);
  612. }
  613. }
  614. })
  615. .bind('mouseleave', function () {
  616. if (t.controlsEnabled) {
  617. if (!t.media.paused && !t.options.alwaysShowControls) {
  618. t.startControlsTimer(t.options.controlsTimeoutMouseLeave);
  619. }
  620. }
  621. });
  622. }
  623. if(t.options.hideVideoControlsOnLoad) {
  624. t.hideControls(false);
  625. }
  626. // check for autoplay
  627. if (autoplay && !t.options.alwaysShowControls) {
  628. t.hideControls();
  629. }
  630. // resizer
  631. if (t.options.enableAutosize) {
  632. t.media.addEventListener('loadedmetadata', function(e) {
  633. // if the <video height> was not set and the options.videoHeight was not set
  634. // then resize to the real dimensions
  635. if (t.options.videoHeight <= 0 && t.domNode.getAttribute('height') === null && !isNaN(e.target.videoHeight)) {
  636. t.setPlayerSize(e.target.videoWidth, e.target.videoHeight);
  637. t.setControlsSize();
  638. t.media.setVideoSize(e.target.videoWidth, e.target.videoHeight);
  639. }
  640. }, false);
  641. }
  642. }
  643. // EVENTS
  644. // FOCUS: when a video starts playing, it takes focus from other players (possibly pausing them)
  645. t.media.addEventListener('play', function() {
  646. var playerIndex;
  647. // go through all other players
  648. for (playerIndex in mejs.players) {
  649. var p = mejs.players[playerIndex];
  650. if (p.id != t.id && t.options.pauseOtherPlayers && !p.paused && !p.ended) {
  651. p.pause();
  652. }
  653. p.hasFocus = false;
  654. }
  655. t.hasFocus = true;
  656. },false);
  657. // ended for all
  658. t.media.addEventListener('ended', function (e) {
  659. if(t.options.autoRewind) {
  660. try{
  661. t.media.setCurrentTime(0);
  662. // Fixing an Android stock browser bug, where "seeked" isn't fired correctly after ending the video and jumping to the beginning
  663. window.setTimeout(function(){
  664. $(t.container).find('.mejs-overlay-loading').parent().hide();
  665. }, 20);
  666. } catch (exp) {
  667. }
  668. }
  669. if (t.media.pluginType === 'youtube') {
  670. t.media.stop();
  671. } else {
  672. t.media.pause();
  673. }
  674. if (t.setProgressRail) {
  675. t.setProgressRail();
  676. }
  677. if (t.setCurrentRail) {
  678. t.setCurrentRail();
  679. }
  680. if (t.options.loop) {
  681. t.play();
  682. } else if (!t.options.alwaysShowControls && t.controlsEnabled) {
  683. t.showControls();
  684. }
  685. }, false);
  686. // resize on the first play
  687. t.media.addEventListener('loadedmetadata', function() {
  688. mejs.Utility.calculateTimeFormat(t.duration, t.options, t.options.framesPerSecond || 25);
  689. if (t.updateDuration) {
  690. t.updateDuration();
  691. }
  692. if (t.updateCurrent) {
  693. t.updateCurrent();
  694. }
  695. if (!t.isFullScreen) {
  696. t.setPlayerSize(t.width, t.height);
  697. t.setControlsSize();
  698. }
  699. }, false);
  700. // Only change the time format when necessary
  701. var duration = null;
  702. t.media.addEventListener('timeupdate',function() {
  703. if (duration !== this.duration) {
  704. duration = this.duration;
  705. mejs.Utility.calculateTimeFormat(duration, t.options, t.options.framesPerSecond || 25);
  706. // make sure to fill in and resize the controls (e.g., 00:00 => 01:13:15
  707. if (t.updateDuration) {
  708. t.updateDuration();
  709. }
  710. if (t.updateCurrent) {
  711. t.updateCurrent();
  712. }
  713. t.setControlsSize();
  714. }
  715. }, false);
  716. t.container.focusout(function (e) {
  717. if( e.relatedTarget ) { //FF is working on supporting focusout https://bugzilla.mozilla.org/show_bug.cgi?id=687787
  718. var $target = $(e.relatedTarget);
  719. if (t.keyboardAction && $target.parents('.mejs-container').length === 0) {
  720. t.keyboardAction = false;
  721. if (t.isVideo && !t.options.alwaysShowControls) {
  722. t.hideControls(true);
  723. }
  724. }
  725. }
  726. });
  727. // webkit has trouble doing this without a delay
  728. setTimeout(function () {
  729. t.setPlayerSize(t.width, t.height);
  730. t.setControlsSize();
  731. }, 50);
  732. // adjust controls whenever window sizes (used to be in fullscreen only)
  733. t.globalBind('resize', function() {
  734. // don't resize for fullscreen mode
  735. if ( !(t.isFullScreen || (mejs.MediaFeatures.hasTrueNativeFullScreen && document.webkitIsFullScreen)) ) {
  736. t.setPlayerSize(t.width, t.height);
  737. }
  738. // always adjust controls
  739. t.setControlsSize();
  740. });
  741. // This is a work-around for a bug in the YouTube iFrame player, which means
  742. // we can't use the play() API for the initial playback on iOS or Android;
  743. // user has to start playback directly by tapping on the iFrame.
  744. if (t.media.pluginType == 'youtube' && ( mf.isiOS || mf.isAndroid ) ) {
  745. t.container.find('.mejs-overlay-play').hide();
  746. t.container.find('.mejs-poster').hide();
  747. }
  748. }
  749. // force autoplay for HTML5
  750. if (autoplay && media.pluginType == 'native') {
  751. t.play();
  752. }
  753. if (t.options.success) {
  754. if (typeof t.options.success == 'string') {
  755. window[t.options.success](t.media, t.domNode, t);
  756. } else {
  757. t.options.success(t.media, t.domNode, t);
  758. }
  759. }
  760. },
  761. handleError: function(e) {
  762. var t = this;
  763. if (t.controls) {
  764. t.controls.hide();
  765. }
  766. // Tell user that the file cannot be played
  767. if (t.options.error) {
  768. t.options.error(e);
  769. }
  770. },
  771. setPlayerSize: function(width,height) {
  772. var t = this;
  773. if( !t.options.setDimensions ) {
  774. return false;
  775. }
  776. if (typeof width != 'undefined') {
  777. t.width = width;
  778. }
  779. if (typeof height != 'undefined') {
  780. t.height = height;
  781. }
  782. // check stretching modes
  783. switch (t.options.stretching) {
  784. case 'fill':
  785. // The 'fill' effect only makes sense on video; for audio we will set the dimensions
  786. if (t.isVideo) {
  787. this.setFillMode();
  788. } else {
  789. this.setDimensions(t.width, t.height);
  790. }
  791. break;
  792. case 'responsive':
  793. this.setResponsiveMode();
  794. break;
  795. case 'none':
  796. this.setDimensions(t.width, t.height);
  797. break;
  798. // This is the 'auto' mode
  799. default:
  800. if (this.hasFluidMode() === true) {
  801. this.setResponsiveMode();
  802. } else {
  803. this.setDimensions(t.width, t.height);
  804. }
  805. break;
  806. }
  807. },
  808. hasFluidMode: function() {
  809. var t = this;
  810. // detect 100% mode - use currentStyle for IE since css() doesn't return percentages
  811. return (t.height.toString().indexOf('%') > 0 || (t.$node.css('max-width') !== 'none' && t.$node.css('max-width') !== 't.width') || (t.$node[0].currentStyle && t.$node[0].currentStyle.maxWidth === '100%'));
  812. },
  813. setResponsiveMode: function() {
  814. var t = this;
  815. // do we have the native dimensions yet?
  816. var nativeWidth = (function() {
  817. if (t.isVideo) {
  818. if (t.media.videoWidth && t.media.videoWidth > 0) {
  819. return t.media.videoWidth;
  820. } else if (t.media.getAttribute('width') !== null) {
  821. return t.media.getAttribute('width');
  822. } else {
  823. return t.options.defaultVideoWidth;
  824. }
  825. } else {
  826. return t.options.defaultAudioWidth;
  827. }
  828. })();
  829. var nativeHeight = (function() {
  830. if (t.isVideo) {
  831. if (t.media.videoHeight && t.media.videoHeight > 0) {
  832. return t.media.videoHeight;
  833. } else if (t.media.getAttribute('height') !== null) {
  834. return t.media.getAttribute('height');
  835. } else {
  836. return t.options.defaultVideoHeight;
  837. }
  838. } else {
  839. return t.options.defaultAudioHeight;
  840. }
  841. })();
  842. var parentWidth = t.container.parent().closest(':visible').width(),
  843. parentHeight = t.container.parent().closest(':visible').height(),
  844. newHeight = t.isVideo || !t.options.autosizeProgress ? parseInt(parentWidth * nativeHeight/nativeWidth, 10) : nativeHeight;
  845. // When we use percent, the newHeight can't be calculated so we get the container height
  846. if (isNaN(newHeight) || ( parentHeight !== 0 && newHeight > parentHeight && parentHeight > nativeHeight)) {
  847. newHeight = parentHeight;
  848. }
  849. if (t.container.parent().length > 0 && t.container.parent()[0].tagName.toLowerCase() === 'body') { // && t.container.siblings().count == 0) {
  850. parentWidth = $(window).width();
  851. newHeight = $(window).height();
  852. }
  853. if ( newHeight && parentWidth ) {
  854. // set outer container size
  855. t.container
  856. .width(parentWidth)
  857. .height(newHeight);
  858. // set native <video> or <audio> and shims
  859. t.$media.add(t.container.find('.mejs-shim'))
  860. .width('100%')
  861. .height('100%');
  862. // if shim is ready, send the size to the embeded plugin
  863. if (t.isVideo) {
  864. if (t.media.setVideoSize) {
  865. t.media.setVideoSize(parentWidth, newHeight);
  866. }
  867. }
  868. // set the layers
  869. t.layers.children('.mejs-layer')
  870. .width('100%')
  871. .height('100%');
  872. }
  873. },
  874. setFillMode: function() {
  875. var t = this,
  876. parent = t.outerContainer;
  877. if (!parent.width()) {
  878. parent.height(t.$media.width());
  879. }
  880. if (!parent.height()) {
  881. parent.height(t.$media.height());
  882. }
  883. var parentWidth = parent.width(),
  884. parentHeight = parent.height();
  885. t.setDimensions('100%', '100%');
  886. // This prevents an issue when displaying poster
  887. t.container.find('.mejs-poster img').css('display', 'block');
  888. targetElement = t.container.find('object, embed, iframe, video');
  889. // calculate new width and height
  890. var initHeight = t.height,
  891. initWidth = t.width,
  892. // scale to the target width
  893. scaleX1 = parentWidth,
  894. scaleY1 = (initHeight * parentWidth) / initWidth,
  895. // scale to the target height
  896. scaleX2 = (initWidth * parentHeight) / initHeight,
  897. scaleY2 = parentHeight,
  898. // now figure out which one we should use
  899. bScaleOnWidth = !(scaleX2 > parentWidth),
  900. finalWidth = bScaleOnWidth ? Math.floor(scaleX1) : Math.floor(scaleX2),
  901. finalHeight = bScaleOnWidth ? Math.floor(scaleY1) : Math.floor(scaleY2);
  902. if (bScaleOnWidth) {
  903. targetElement.height(finalHeight).width(parentWidth);
  904. if (t.media.setVideoSize) {
  905. t.media.setVideoSize(parentWidth, finalHeight);
  906. }
  907. } else {
  908. targetElement.height(parentHeight).width(finalWidth);
  909. if (t.media.setVideoSize) {
  910. t.media.setVideoSize(finalWidth, parentHeight);
  911. }
  912. }
  913. targetElement.css({
  914. 'margin-left': Math.floor((parentWidth - finalWidth) / 2),
  915. 'margin-top': 0
  916. });
  917. },
  918. setDimensions: function(width, height) {
  919. var t = this;
  920. t.container
  921. .width(width)
  922. .height(height);
  923. t.layers.children('.mejs-layer')
  924. .width(width)
  925. .height(height);
  926. },
  927. setControlsSize: function() {
  928. var t = this,
  929. usedWidth = 0,
  930. railWidth = 0,
  931. rail = t.controls.find('.mejs-time-rail'),
  932. total = t.controls.find('.mejs-time-total'),
  933. others = rail.siblings(),
  934. lastControl = others.last(),
  935. lastControlPosition = null,
  936. avoidAutosizeProgress = t.options && !t.options.autosizeProgress;
  937. // skip calculation if hidden
  938. if (!t.container.is(':visible') || !rail.length || !rail.is(':visible')) {
  939. return;
  940. }
  941. // allow the size to come from custom CSS
  942. if (avoidAutosizeProgress) {
  943. // Also, frontends devs can be more flexible
  944. // due the opportunity of absolute positioning.
  945. railWidth = parseInt(rail.css('width'), 10);
  946. }
  947. // attempt to autosize
  948. if (railWidth === 0 || !railWidth) {
  949. // find the size of all the other controls besides the rail
  950. others.each(function() {
  951. var $this = $(this);
  952. if ($this.css('position') != 'absolute' && $this.is(':visible')) {
  953. usedWidth += $(this).outerWidth(true);
  954. }
  955. });
  956. // fit the rail into the remaining space
  957. railWidth = t.controls.width() - usedWidth - (rail.outerWidth(true) - rail.width());
  958. }
  959. // resize the rail,
  960. // but then check if the last control (say, the fullscreen button) got pushed down
  961. // this often happens when zoomed
  962. do {
  963. // outer area
  964. // we only want to set an inline style with the width of the rail
  965. // if we're trying to autosize.
  966. if (!avoidAutosizeProgress) {
  967. rail.width(railWidth);
  968. }
  969. // dark space
  970. total.width(railWidth - (total.outerWidth(true) - total.width()));
  971. if (lastControl.css('position') != 'absolute') {
  972. lastControlPosition = lastControl.length ? lastControl.position() : null;
  973. railWidth--;
  974. }
  975. } while (lastControlPosition !== null && lastControlPosition.top.toFixed(2) > 0 && railWidth > 0);
  976. t.container.trigger('controlsresize');
  977. },
  978. buildposter: function(player, controls, layers, media) {
  979. var t = this,
  980. poster =
  981. $('<div class="mejs-poster mejs-layer">' +
  982. '</div>')
  983. .appendTo(layers),
  984. posterUrl = player.$media.attr('poster');
  985. // prioriy goes to option (this is useful if you need to support iOS 3.x (iOS completely fails with poster)
  986. if (player.options.poster !== '') {
  987. posterUrl = player.options.poster;
  988. }
  989. // second, try the real poster
  990. if ( posterUrl ) {
  991. t.setPoster(posterUrl);
  992. } else {
  993. poster.hide();
  994. }
  995. media.addEventListener('play',function() {
  996. poster.hide();
  997. }, false);
  998. if(player.options.showPosterWhenEnded && player.options.autoRewind){
  999. media.addEventListener('ended',function() {
  1000. poster.show();
  1001. }, false);
  1002. }
  1003. },
  1004. setPoster: function(url) {
  1005. var t = this,
  1006. posterDiv = t.container.find('.mejs-poster'),
  1007. posterImg = posterDiv.find('img');
  1008. if (posterImg.length === 0) {
  1009. posterImg = $('<img width="100%" height="100%" alt="" />').appendTo(posterDiv);
  1010. }
  1011. posterImg.attr('src', url);
  1012. posterDiv.css({'background-image' : 'url(' + url + ')'});
  1013. },
  1014. buildoverlays: function(player, controls, layers, media) {
  1015. var t = this;
  1016. if (!player.isVideo)
  1017. return;
  1018. var
  1019. loading =
  1020. $('<div class="mejs-overlay mejs-layer">'+
  1021. '<div class="mejs-overlay-loading"><span></span></div>'+
  1022. '</div>')
  1023. .hide() // start out hidden
  1024. .appendTo(layers),
  1025. error =
  1026. $('<div class="mejs-overlay mejs-layer">'+
  1027. '<div class="mejs-overlay-error"></div>'+
  1028. '</div>')
  1029. .hide() // start out hidden
  1030. .appendTo(layers),
  1031. // this needs to come last so it's on top
  1032. bigPlay =
  1033. $('<div class="mejs-overlay mejs-layer mejs-overlay-play">'+
  1034. '<div class="mejs-overlay-button" role="button" aria-label="' + mejs.i18n.t('mejs.play') + '" aria-pressed="false"></div>'+
  1035. '</div>')
  1036. .appendTo(layers)
  1037. .bind('click', function() { // Removed 'touchstart' due issues on Samsung Android devices where a tap on bigPlay started and immediately stopped the video
  1038. if (t.options.clickToPlayPause) {
  1039. if (media.paused) {
  1040. media.play();
  1041. }
  1042. var button = $(this).find('.mejs-overlay-button'),
  1043. pressed = button.attr('aria-pressed');
  1044. button.attr('aria-pressed', !!pressed);
  1045. }
  1046. });
  1047. /*
  1048. if (mejs.MediaFeatures.isiOS || mejs.MediaFeatures.isAndroid) {
  1049. bigPlay.remove();
  1050. loading.remove();
  1051. }
  1052. */
  1053. // show/hide big play button
  1054. media.addEventListener('play',function() {
  1055. bigPlay.hide();
  1056. loading.hide();
  1057. controls.find('.mejs-time-buffering').hide();
  1058. error.hide();
  1059. }, false);
  1060. media.addEventListener('playing', function() {
  1061. bigPlay.hide();
  1062. loading.hide();
  1063. controls.find('.mejs-time-buffering').hide();
  1064. error.hide();
  1065. }, false);
  1066. media.addEventListener('seeking', function() {
  1067. loading.show();
  1068. controls.find('.mejs-time-buffering').show();
  1069. }, false);
  1070. media.addEventListener('seeked', function() {
  1071. loading.hide();
  1072. controls.find('.mejs-time-buffering').hide();
  1073. }, false);
  1074. media.addEventListener('pause',function() {
  1075. if (!mejs.MediaFeatures.isiPhone) {
  1076. bigPlay.show();
  1077. }
  1078. }, false);
  1079. media.addEventListener('waiting', function() {
  1080. loading.show();
  1081. controls.find('.mejs-time-buffering').show();
  1082. }, false);
  1083. // show/hide loading
  1084. media.addEventListener('loadeddata',function() {
  1085. // for some reason Chrome is firing this event
  1086. //if (mejs.MediaFeatures.isChrome && media.getAttribute && media.getAttribute('preload') === 'none')
  1087. // return;
  1088. loading.show();
  1089. controls.find('.mejs-time-buffering').show();
  1090. // Firing the 'canplay' event after a timeout which isn't getting fired on some Android 4.1 devices (https://github.com/johndyer/mediaelement/issues/1305)
  1091. if (mejs.MediaFeatures.isAndroid) {
  1092. media.canplayTimeout = window.setTimeout(
  1093. function() {
  1094. if (document.createEvent) {
  1095. var evt = document.createEvent('HTMLEvents');
  1096. evt.initEvent('canplay', true, true);
  1097. return media.dispatchEvent(evt);
  1098. }
  1099. }, 300
  1100. );
  1101. }
  1102. }, false);
  1103. media.addEventListener('canplay',function() {
  1104. loading.hide();
  1105. controls.find('.mejs-time-buffering').hide();
  1106. clearTimeout(media.canplayTimeout); // Clear timeout inside 'loadeddata' to prevent 'canplay' to fire twice
  1107. }, false);
  1108. // error handling
  1109. media.addEventListener('error',function(e) {
  1110. t.handleError(e);
  1111. loading.hide();
  1112. bigPlay.hide();
  1113. error.show();
  1114. error.find('.mejs-overlay-error').html("Error loading this resource");
  1115. }, false);
  1116. media.addEventListener('keydown', function(e) {
  1117. t.onkeydown(player, media, e);
  1118. }, false);
  1119. },
  1120. buildkeyboard: function(player, controls, layers, media) {
  1121. var t = this;
  1122. t.container.keydown(function () {
  1123. t.keyboardAction = true;
  1124. });
  1125. // listen for key presses
  1126. t.globalBind('keydown', function(event) {
  1127. player.hasFocus = $(event.target).closest('.mejs-container').length !== 0
  1128. && $(event.target).closest('.mejs-container').attr('id') === player.$media.closest('.mejs-container').attr('id');
  1129. return t.onkeydown(player, media, event);
  1130. });
  1131. // check if someone clicked outside a player region, then kill its focus
  1132. t.globalBind('click', function(event) {
  1133. player.hasFocus = $(event.target).closest('.mejs-container').length !== 0;
  1134. });
  1135. },
  1136. onkeydown: function(player, media, e) {
  1137. if (player.hasFocus && player.options.enableKeyboard) {
  1138. // find a matching key
  1139. for (var i = 0, il = player.options.keyActions.length; i < il; i++) {
  1140. var keyAction = player.options.keyActions[i];
  1141. for (var j = 0, jl = keyAction.keys.length; j < jl; j++) {
  1142. if (e.keyCode == keyAction.keys[j]) {
  1143. if (typeof(e.preventDefault) == "function") e.preventDefault();
  1144. keyAction.action(player, media, e.keyCode, e);
  1145. return false;
  1146. }
  1147. }
  1148. }
  1149. }
  1150. return true;
  1151. },
  1152. findTracks: function() {
  1153. var t = this,
  1154. tracktags = t.$media.find('track');
  1155. // store for use by plugins
  1156. t.tracks = [];
  1157. tracktags.each(function(index, track) {
  1158. track = $(track);
  1159. t.tracks.push({
  1160. srclang: (track.attr('srclang')) ? track.attr('srclang').toLowerCase() : '',
  1161. src: track.attr('src'),
  1162. kind: track.attr('kind'),
  1163. label: track.attr('label') || '',
  1164. entries: [],
  1165. isLoaded: false
  1166. });
  1167. });
  1168. },
  1169. changeSkin: function(className) {
  1170. this.container[0].className = 'mejs-container ' + className;
  1171. this.setPlayerSize(this.width, this.height);
  1172. this.setControlsSize();
  1173. },
  1174. play: function() {
  1175. this.load();
  1176. this.media.play();
  1177. },
  1178. pause: function() {
  1179. try {
  1180. this.media.pause();
  1181. } catch (e) {}
  1182. },
  1183. load: function() {
  1184. if (!this.isLoaded) {
  1185. this.media.load();
  1186. }
  1187. this.isLoaded = true;
  1188. },
  1189. setMuted: function(muted) {
  1190. this.media.setMuted(muted);
  1191. },
  1192. setCurrentTime: function(time) {
  1193. this.media.setCurrentTime(time);
  1194. },
  1195. getCurrentTime: function() {
  1196. return this.media.currentTime;
  1197. },
  1198. setVolume: function(volume) {
  1199. this.media.setVolume(volume);
  1200. },
  1201. getVolume: function() {
  1202. return this.media.volume;
  1203. },
  1204. setSrc: function(src) {
  1205. var
  1206. t = this;
  1207. // If using YouTube, its API is different to load a specific source
  1208. if (t.media.pluginType === 'youtube') {
  1209. var videoId;
  1210. if (typeof src !== 'string') {
  1211. var i, media;
  1212. for (i=0; i<src.length; i++) {
  1213. media = src[i];
  1214. if (this.canPlayType(media.type)) {
  1215. src = media.src;
  1216. break;
  1217. }
  1218. }
  1219. }
  1220. // youtu.be url from share button
  1221. if (src.lastIndexOf('youtu.be') !== -1) {
  1222. videoId = src.substr(src.lastIndexOf('/') + 1);
  1223. if (videoId.indexOf('?') !== -1) {
  1224. videoId = videoId.substr(0, videoId.indexOf('?'));
  1225. }
  1226. } else {
  1227. // https://www.youtube.com/watch?v=
  1228. var videoIdMatch = src.match(/[?&]v=([^&#]+)|&|#|$/);
  1229. if (videoIdMatch) {
  1230. videoId = videoIdMatch[1];
  1231. }
  1232. }
  1233. if (t.media.getAttribute('autoplay') !== null) {
  1234. t.media.pluginApi.loadVideoById(videoId);
  1235. } else {
  1236. t.media.pluginApi.cueVideoById(videoId);
  1237. }
  1238. }
  1239. else {
  1240. t.media.setSrc(src);
  1241. }
  1242. },
  1243. remove: function() {
  1244. var t = this, featureIndex, feature;
  1245. t.container.prev('.mejs-offscreen').remove();
  1246. // invoke features cleanup
  1247. for (featureIndex in t.options.features) {
  1248. feature = t.options.features[featureIndex];
  1249. if (t['clean' + feature]) {
  1250. try {
  1251. t['clean' + feature](t);
  1252. } catch (e) {
  1253. // TODO: report control error
  1254. //throw e;
  1255. //
  1256. //
  1257. }
  1258. }
  1259. }
  1260. // grab video and put it back in place
  1261. if (!t.isDynamic) {
  1262. t.$media.prop('controls', true);
  1263. // detach events from the video
  1264. // TODO: detach event listeners better than this;
  1265. // also detach ONLY the events attached by this plugin!
  1266. t.$node.clone().insertBefore(t.container).show();
  1267. t.$node.remove();
  1268. } else {
  1269. t.$node.insertBefore(t.container);
  1270. }
  1271. if (t.media.pluginType !== 'native') {
  1272. t.media.remove();
  1273. }
  1274. // Remove the player from the mejs.players object so that pauseOtherPlayers doesn't blow up when trying to pause a non existance flash api.
  1275. delete mejs.players[t.id];
  1276. if (typeof t.container == 'object') {
  1277. t.container.remove();
  1278. }
  1279. t.globalUnbind();
  1280. delete t.node.player;
  1281. },
  1282. rebuildtracks: function(){
  1283. var t = this;
  1284. t.findTracks();
  1285. t.buildtracks(t, t.controls, t.layers, t.media);
  1286. },
  1287. resetSize: function(){
  1288. var t = this;
  1289. // webkit has trouble doing this without a delay
  1290. setTimeout(function () {
  1291. //
  1292. t.setPlayerSize(t.width, t.height);
  1293. t.setControlsSize();
  1294. }, 50);
  1295. }
  1296. };
  1297. (function(){
  1298. var rwindow = /^((after|before)print|(before)?unload|hashchange|message|o(ff|n)line|page(hide|show)|popstate|resize|storage)\b/;
  1299. function splitEvents(events, id) {
  1300. // add player ID as an event namespace so it's easier to unbind them all later
  1301. var ret = {d: [], w: []};
  1302. $.each((events || '').split(' '), function(k, v){
  1303. var eventname = v + '.' + id;
  1304. if (eventname.indexOf('.') === 0) {
  1305. ret.d.push(eventname);
  1306. ret.w.push(eventname);
  1307. }
  1308. else {
  1309. ret[rwindow.test(v) ? 'w' : 'd'].push(eventname);
  1310. }
  1311. });
  1312. ret.d = ret.d.join(' ');
  1313. ret.w = ret.w.join(' ');
  1314. return ret;
  1315. }
  1316. mejs.MediaElementPlayer.prototype.globalBind = function(events, data, callback) {
  1317. var t = this;
  1318. var doc = t.node ? t.node.ownerDocument : document;
  1319. events = splitEvents(events, t.id);
  1320. if (events.d) $(doc).bind(events.d, data, callback);
  1321. if (events.w) $(window).bind(events.w, data, callback);
  1322. };
  1323. mejs.MediaElementPlayer.prototype.globalUnbind = function(events, callback) {
  1324. var t = this;
  1325. var doc = t.node ? t.node.ownerDocument : document;
  1326. events = splitEvents(events, t.id);
  1327. if (events.d) $(doc).unbind(events.d, callback);
  1328. if (events.w) $(window).unbind(events.w, callback);
  1329. };
  1330. })();
  1331. // turn into jQuery plugin
  1332. if (typeof $ != 'undefined') {
  1333. $.fn.mediaelementplayer = function (options) {
  1334. if (options === false) {
  1335. this.each(function () {
  1336. var player = $(this).data('mediaelementplayer');
  1337. if (player) {
  1338. player.remove();
  1339. }
  1340. $(this).removeData('mediaelementplayer');
  1341. });
  1342. }
  1343. else {
  1344. this.each(function () {
  1345. $(this).data('mediaelementplayer', new mejs.MediaElementPlayer(this, options));
  1346. });
  1347. }
  1348. return this;
  1349. };
  1350. $(document).ready(function() {
  1351. // auto enable using JSON attribute
  1352. $('.mejs-player').mediaelementplayer();
  1353. });
  1354. }
  1355. // push out to window
  1356. window.MediaElementPlayer = mejs.MediaElementPlayer;
  1357. })(mejs.$);
  1358. (function($) {
  1359. $.extend(mejs.MepDefaults, {
  1360. playText: '',
  1361. pauseText: ''
  1362. });
  1363. // PLAY/pause BUTTON
  1364. $.extend(MediaElementPlayer.prototype, {
  1365. buildplaypause: function(player, controls, layers, media) {
  1366. var
  1367. t = this,
  1368. op = t.options,
  1369. playTitle = op.playText ? op.playText : mejs.i18n.t('mejs.play'),
  1370. pauseTitle = op.pauseText ? op.pauseText : mejs.i18n.t('mejs.pause'),
  1371. play =
  1372. $('<div class="mejs-button mejs-playpause-button mejs-play" >' +
  1373. '<button type="button" aria-controls="' + t.id + '" title="' + playTitle + '" aria-label="' + pauseTitle + '"></button>' +
  1374. '</div>')
  1375. .appendTo(controls)
  1376. .click(function(e) {
  1377. e.preventDefault();
  1378. if (media.paused) {
  1379. media.play();
  1380. } else {
  1381. media.pause();
  1382. }
  1383. return false;
  1384. }),
  1385. play_btn = play.find('button');
  1386. function togglePlayPause(which) {
  1387. if ('play' === which) {
  1388. play.removeClass('mejs-play').addClass('mejs-pause');
  1389. play_btn.attr({
  1390. 'title': pauseTitle,
  1391. 'aria-label': pauseTitle
  1392. });
  1393. } else {
  1394. play.removeClass('mejs-pause').addClass('mejs-play');
  1395. play_btn.attr({
  1396. 'title': playTitle,
  1397. 'aria-label': playTitle
  1398. });
  1399. }
  1400. };
  1401. togglePlayPause('pse');
  1402. media.addEventListener('play',function() {
  1403. togglePlayPause('play');
  1404. }, false);
  1405. media.addEventListener('playing',function() {
  1406. togglePlayPause('play');
  1407. }, false);
  1408. media.addEventListener('pause',function() {
  1409. togglePlayPause('pse');
  1410. }, false);
  1411. media.addEventListener('paused',function() {
  1412. togglePlayPause('pse');
  1413. }, false);
  1414. }
  1415. });
  1416. })(mejs.$);
  1417. (function($) {
  1418. $.extend(mejs.MepDefaults, {
  1419. stopText: 'Stop'
  1420. });
  1421. // STOP BUTTON
  1422. $.extend(MediaElementPlayer.prototype, {
  1423. buildstop: function(player, controls, layers, media) {
  1424. var t = this;
  1425. $('<div class="mejs-button mejs-stop-button mejs-stop">' +
  1426. '<button type="button" aria-controls="' + t.id + '" title="' + t.options.stopText + '" aria-label="' + t.options.stopText + '"></button>' +
  1427. '</div>')
  1428. .appendTo(controls)
  1429. .click(function() {
  1430. if (!media.paused) {
  1431. media.pause();
  1432. }
  1433. if (media.currentTime > 0) {
  1434. media.setCurrentTime(0);
  1435. media.pause();
  1436. controls.find('.mejs-time-current').width('0px');
  1437. controls.find('.mejs-time-handle').css('left', '0px');
  1438. controls.find('.mejs-time-float-current').html( mejs.Utility.secondsToTimeCode(0, player.options));
  1439. controls.find('.mejs-currenttime').html( mejs.Utility.secondsToTimeCode(0, player.options));
  1440. layers.find('.mejs-poster').show();
  1441. }
  1442. });
  1443. }
  1444. });
  1445. })(mejs.$);
  1446. (function($) {
  1447. $.extend(mejs.MepDefaults, {
  1448. // Enable tooltip that shows time in progress bar
  1449. enableProgressTooltip: true,
  1450. progressHelpText: ''
  1451. });
  1452. // progress/loaded bar
  1453. $.extend(MediaElementPlayer.prototype, {
  1454. buildprogress: function(player, controls, layers, media) {
  1455. var
  1456. t = this,
  1457. mouseIsDown = false,
  1458. mouseIsOver = false,
  1459. lastKeyPressTime = 0,
  1460. startedPaused = false,
  1461. autoRewindInitial = player.options.autoRewind,
  1462. progressTitle = t.options.progressHelpText ? t.options.progressHelpText : mejs.i18n.t('mejs.time-help-text'),
  1463. tooltip = player.options.enableProgressTooltip ? '<span class="mejs-time-float">' +
  1464. '<span class="mejs-time-float-current">00:00</span>' +
  1465. '<span class="mejs-time-float-corner"></span>' +
  1466. '</span>' : "";
  1467. $('<div class="mejs-time-rail">' +
  1468. '<span class="mejs-time-total mejs-time-slider">' +
  1469. //'<span class="mejs-offscreen">' + progressTitle + '</span>' +
  1470. '<span class="mejs-time-buffering"></span>' +
  1471. '<span class="mejs-time-loaded"></span>' +
  1472. '<span class="mejs-time-current"></span>' +
  1473. '<span class="mejs-time-handle"></span>' +
  1474. tooltip +
  1475. '</span>' +
  1476. '</div>')
  1477. .appendTo(controls);
  1478. controls.find('.mejs-time-buffering').hide();
  1479. t.total = controls.find('.mejs-time-total');
  1480. t.loaded = controls.find('.mejs-time-loaded');
  1481. t.current = controls.find('.mejs-time-current');
  1482. t.handle = controls.find('.mejs-time-handle');
  1483. t.timefloat = controls.find('.mejs-time-float');
  1484. t.timefloatcurrent = controls.find('.mejs-time-float-current');
  1485. t.slider = controls.find('.mejs-time-slider');
  1486. var handleMouseMove = function (e) {
  1487. var offset = t.total.offset(),
  1488. width = t.total.width(),
  1489. percentage = 0,
  1490. newTime = 0,
  1491. pos = 0,
  1492. x;
  1493. // mouse or touch position relative to the object
  1494. if (e.originalEvent && e.originalEvent.changedTouches) {
  1495. x = e.originalEvent.changedTouches[0].pageX;
  1496. } else if (e.changedTouches) { // for Zepto
  1497. x = e.changedTouches[0].pageX;
  1498. } else {
  1499. x = e.pageX;
  1500. }
  1501. if (media.duration) {
  1502. if (x < offset.left) {
  1503. x = offset.left;
  1504. } else if (x > width + offset.left) {
  1505. x = width + offset.left;
  1506. }
  1507. pos = x - offset.left;
  1508. percentage = (pos / width);
  1509. newTime = (percentage <= 0.02) ? 0 : percentage * media.duration;
  1510. // seek to where the mouse is
  1511. if (mouseIsDown && newTime !== media.currentTime) {
  1512. media.setCurrentTime(newTime);
  1513. }
  1514. // position floating time box
  1515. if (!mejs.MediaFeatures.hasTouch) {
  1516. t.timefloat.css('left', pos);
  1517. t.timefloatcurrent.html( mejs.Utility.secondsToTimeCode(newTime, player.options) );
  1518. t.timefloat.show();
  1519. }
  1520. }
  1521. },
  1522. // Accessibility for slider
  1523. updateSlider = function (e) {
  1524. var seconds = media.currentTime,
  1525. timeSliderText = mejs.i18n.t('mejs.time-slider'),
  1526. time = mejs.Utility.secondsToTimeCode(seconds, player.options),
  1527. duration = media.duration;
  1528. t.slider.attr({
  1529. 'aria-label': timeSliderText,
  1530. 'aria-valuemin': 0,
  1531. 'aria-valuemax': duration,
  1532. 'aria-valuenow': seconds,
  1533. 'aria-valuetext': time,
  1534. 'role': 'slider',
  1535. 'tabindex': 0
  1536. });
  1537. },
  1538. restartPlayer = function () {
  1539. var now = new Date();
  1540. if (now - lastKeyPressTime >= 1000) {
  1541. media.play();
  1542. }
  1543. };
  1544. t.slider.bind('focus', function (e) {
  1545. player.options.autoRewind = false;
  1546. });
  1547. t.slider.bind('blur', function (e) {
  1548. player.options.autoRewind = autoRewindInitial;
  1549. });
  1550. t.slider.bind('keydown', function (e) {
  1551. if ((new Date() - lastKeyPressTime) >= 1000) {
  1552. startedPaused = media.paused;
  1553. }
  1554. var keyCode = e.keyCode,
  1555. duration = media.duration,
  1556. seekTime = media.currentTime,
  1557. seekForward = player.options.defaultSeekForwardInterval(media),
  1558. seekBackward = player.options.defaultSeekBackwardInterval(media);
  1559. switch (keyCode) {
  1560. case 37: // left
  1561. case 40: // Down
  1562. seekTime -= seekBackward;
  1563. break;
  1564. case 39: // Right
  1565. case 38: // Up
  1566. seekTime += seekForward;
  1567. break;
  1568. case 36: // Home
  1569. seekTime = 0;
  1570. break;
  1571. case 35: // end
  1572. seekTime = duration;
  1573. break;
  1574. case 32: // space
  1575. case 13: // enter
  1576. media.paused ? media.play() : media.pause();
  1577. return;
  1578. default:
  1579. return;
  1580. }
  1581. seekTime = seekTime < 0 ? 0 : (seekTime >= duration ? duration : Math.floor(seekTime));
  1582. lastKeyPressTime = new Date();
  1583. if (!startedPaused) {
  1584. media.pause();
  1585. }
  1586. if (seekTime < media.duration && !startedPaused) {
  1587. setTimeout(restartPlayer, 1100);
  1588. }
  1589. media.setCurrentTime(seekTime);
  1590. e.preventDefault();
  1591. e.stopPropagation();
  1592. return false;
  1593. });
  1594. // handle clicks
  1595. //controls.find('.mejs-time-rail').delegate('span', 'click', handleMouseMove);
  1596. t.total
  1597. .bind('mousedown touchstart', function (e) {
  1598. // only handle left clicks or touch
  1599. if (e.which === 1 || e.which === 0) {
  1600. mouseIsDown = true;
  1601. handleMouseMove(e);
  1602. t.globalBind('mousemove.dur touchmove.dur', function(e) {
  1603. handleMouseMove(e);
  1604. });
  1605. t.globalBind('mouseup.dur touchend.dur', function (e) {
  1606. mouseIsDown = false;
  1607. if (typeof t.timefloat !== 'undefined') {
  1608. t.timefloat.hide();
  1609. }
  1610. t.globalUnbind('.dur');
  1611. });
  1612. }
  1613. })
  1614. .bind('mouseenter', function(e) {
  1615. mouseIsOver = true;
  1616. t.globalBind('mousemove.dur', function(e) {
  1617. handleMouseMove(e);
  1618. });
  1619. if (typeof t.timefloat !== 'undefined' && !mejs.MediaFeatures.hasTouch) {
  1620. t.timefloat.show();
  1621. }
  1622. })
  1623. .bind('mouseleave',function(e) {
  1624. mouseIsOver = false;
  1625. if (!mouseIsDown) {
  1626. t.globalUnbind('.dur');
  1627. if (typeof t.timefloat !== 'undefined') {
  1628. t.timefloat.hide();
  1629. }
  1630. }
  1631. });
  1632. // loading
  1633. media.addEventListener('progress', function (e) {
  1634. player.setProgressRail(e);
  1635. player.setCurrentRail(e);
  1636. }, false);
  1637. // current time
  1638. media.addEventListener('timeupdate', function(e) {
  1639. player.setProgressRail(e);
  1640. player.setCurrentRail(e);
  1641. updateSlider(e);
  1642. }, false);
  1643. t.container.on('controlsresize', function(e) {
  1644. player.setProgressRail(e);
  1645. player.setCurrentRail(e);
  1646. });
  1647. },
  1648. setProgressRail: function(e) {
  1649. var
  1650. t = this,
  1651. target = (e !== undefined) ? e.target : t.media,
  1652. percent = null;
  1653. // newest HTML5 spec has buffered array (FF4, Webkit)
  1654. if (target && target.buffered && target.buffered.length > 0 && target.buffered.end && target.duration) {
  1655. // account for a real array with multiple values - always read the end of the last buffer
  1656. percent = target.buffered.end(target.buffered.length - 1) / target.duration;
  1657. }
  1658. // Some browsers (e.g., FF3.6 and Safari 5) cannot calculate target.bufferered.end()
  1659. // to be anything other than 0. If the byte count is available we use this instead.
  1660. // Browsers that support the else if do not seem to have the bufferedBytes value and
  1661. // should skip to there. Tested in Safari 5, Webkit head, FF3.6, Chrome 6, IE 7/8.
  1662. else if (target && target.bytesTotal !== undefined && target.bytesTotal > 0 && target.bufferedBytes !== undefined) {
  1663. percent = target.bufferedBytes / target.bytesTotal;
  1664. }
  1665. // Firefox 3 with an Ogg file seems to go this way
  1666. else if (e && e.lengthComputable && e.total !== 0) {
  1667. percent = e.loaded / e.total;
  1668. }
  1669. // finally update the progress bar
  1670. if (percent !== null) {
  1671. percent = Math.min(1, Math.max(0, percent));
  1672. // update loaded bar
  1673. if (t.loaded && t.total) {
  1674. t.loaded.width(t.total.width() * percent);
  1675. }
  1676. }
  1677. },
  1678. setCurrentRail: function() {
  1679. var t = this;
  1680. if (t.media.currentTime !== undefined && t.media.duration) {
  1681. // update bar and handle
  1682. if (t.total && t.handle) {
  1683. var
  1684. newWidth = Math.round(t.total.width() * t.media.currentTime / t.media.duration),
  1685. handlePos = newWidth - Math.round(t.handle.outerWidth(true) / 2);
  1686. t.current.width(newWidth);
  1687. t.handle.css('left', handlePos);
  1688. }
  1689. }
  1690. }
  1691. });
  1692. })(mejs.$);
  1693. (function($) {
  1694. // options
  1695. $.extend(mejs.MepDefaults, {
  1696. duration: -1,
  1697. timeAndDurationSeparator: '<span> | </span>'
  1698. });
  1699. // current and duration 00:00 / 00:00
  1700. $.extend(MediaElementPlayer.prototype, {
  1701. buildcurrent: function(player, controls, layers, media) {
  1702. var t = this;
  1703. $('<div class="mejs-time" role="timer" aria-live="off">' +
  1704. '<span class="mejs-currenttime">' +
  1705. mejs.Utility.secondsToTimeCode(0, player.options) +
  1706. '</span>'+
  1707. '</div>')
  1708. .appendTo(controls);
  1709. t.currenttime = t.controls.find('.mejs-currenttime');
  1710. media.addEventListener('timeupdate',function() {
  1711. if (t.controlsAreVisible) {
  1712. player.updateCurrent();
  1713. }
  1714. }, false);
  1715. },
  1716. buildduration: function(player, controls, layers, media) {
  1717. var t = this;
  1718. if (controls.children().last().find('.mejs-currenttime').length > 0) {
  1719. $(t.options.timeAndDurationSeparator +
  1720. '<span class="mejs-duration">' +
  1721. mejs.Utility.secondsToTimeCode(t.options.duration, t.options) +
  1722. '</span>')
  1723. .appendTo(controls.find('.mejs-time'));
  1724. } else {
  1725. // add class to current time
  1726. controls.find('.mejs-currenttime').parent().addClass('mejs-currenttime-container');
  1727. $('<div class="mejs-time mejs-duration-container">'+
  1728. '<span class="mejs-duration">' +
  1729. mejs.Utility.secondsToTimeCode(t.options.duration, t.options) +
  1730. '</span>' +
  1731. '</div>')
  1732. .appendTo(controls);
  1733. }
  1734. t.durationD = t.controls.find('.mejs-duration');
  1735. media.addEventListener('timeupdate',function() {
  1736. if (t.controlsAreVisible) {
  1737. player.updateDuration();
  1738. }
  1739. }, false);
  1740. },
  1741. updateCurrent: function() {
  1742. var t = this;
  1743. var currentTime = t.media.currentTime;
  1744. if (isNaN(currentTime)) {
  1745. currentTime = 0;
  1746. }
  1747. if (t.currenttime) {
  1748. t.currenttime.html(mejs.Utility.secondsToTimeCode(currentTime, t.options));
  1749. }
  1750. },
  1751. updateDuration: function() {
  1752. var t = this;
  1753. var duration = t.media.duration;
  1754. if (t.options.duration > 0) {
  1755. duration = t.options.duration;
  1756. }
  1757. if (isNaN(duration)) {
  1758. duration = 0;
  1759. }
  1760. //Toggle the long video class if the video is longer than an hour.
  1761. t.container.toggleClass("mejs-long-video", duration > 3600);
  1762. if (t.durationD && duration > 0) {
  1763. t.durationD.html(mejs.Utility.secondsToTimeCode(duration, t.options));
  1764. }
  1765. }
  1766. });
  1767. })(mejs.$);
  1768. (function ($) {
  1769. $.extend(mejs.MepDefaults, {
  1770. muteText: mejs.i18n.t('mejs.mute-toggle'),
  1771. allyVolumeControlText: mejs.i18n.t('mejs.volume-help-text'),
  1772. hideVolumeOnTouchDevices: true,
  1773. audioVolume: 'horizontal',
  1774. videoVolume: 'vertical'
  1775. });
  1776. $.extend(MediaElementPlayer.prototype, {
  1777. buildvolume: function (player, controls, layers, media) {
  1778. // Android and iOS don't support volume controls
  1779. if ((mejs.MediaFeatures.isAndroid || mejs.MediaFeatures.isiOS) && this.options.hideVolumeOnTouchDevices)
  1780. return;
  1781. var t = this,
  1782. mode = (t.isVideo) ? t.options.videoVolume : t.options.audioVolume,
  1783. mute = (mode == 'horizontal') ?
  1784. // horizontal version
  1785. $('<div class="mejs-button mejs-volume-button mejs-mute">' +
  1786. '<button type="button" aria-controls="' + t.id +
  1787. '" title="' + t.options.muteText +
  1788. '" aria-label="' + t.options.muteText +
  1789. '"></button>' +
  1790. '</div>' +
  1791. '<a href="javascript:void(0);" class="mejs-horizontal-volume-slider">' + // outer background
  1792. '<span class="mejs-offscreen">' + t.options.allyVolumeControlText + '</span>' +
  1793. '<div class="mejs-horizontal-volume-total"></div>' + // line background
  1794. '<div class="mejs-horizontal-volume-current"></div>' + // current volume
  1795. '<div class="mejs-horizontal-volume-handle"></div>' + // handle
  1796. '</a>'
  1797. )
  1798. .appendTo(controls) :
  1799. // vertical version
  1800. $('<div class="mejs-button mejs-volume-button mejs-mute">' +
  1801. '<button type="button" aria-controls="' + t.id +
  1802. '" title="' + t.options.muteText +
  1803. '" aria-label="' + t.options.muteText +
  1804. '"></button>' +
  1805. '<a href="javascript:void(0);" class="mejs-volume-slider">' + // outer background
  1806. '<span class="mejs-offscreen">' + t.options.allyVolumeControlText + '</span>' +
  1807. '<div class="mejs-volume-total"></div>' + // line background
  1808. '<div class="mejs-volume-current"></div>' + // current volume
  1809. '<div class="mejs-volume-handle"></div>' + // handle
  1810. '</a>' +
  1811. '</div>')
  1812. .appendTo(controls),
  1813. volumeSlider = t.container.find('.mejs-volume-slider, .mejs-horizontal-volume-slider'),
  1814. volumeTotal = t.container.find('.mejs-volume-total, .mejs-horizontal-volume-total'),
  1815. volumeCurrent = t.container.find('.mejs-volume-current, .mejs-horizontal-volume-current'),
  1816. volumeHandle = t.container.find('.mejs-volume-handle, .mejs-horizontal-volume-handle'),
  1817. positionVolumeHandle = function (volume, secondTry) {
  1818. if (!volumeSlider.is(':visible') && typeof secondTry == 'undefined') {
  1819. volumeSlider.show();
  1820. positionVolumeHandle(volume, true);
  1821. volumeSlider.hide();
  1822. return;
  1823. }
  1824. // correct to 0-1
  1825. volume = Math.max(0, volume);
  1826. volume = Math.min(volume, 1);
  1827. // adjust mute button style
  1828. if (volume === 0) {
  1829. mute.removeClass('mejs-mute').addClass('mejs-unmute');
  1830. mute.children('button').attr('title', mejs.i18n.t('mejs.unmute')).attr('aria-label', mejs.i18n.t('mejs.unmute'));
  1831. } else {
  1832. mute.removeClass('mejs-unmute').addClass('mejs-mute');
  1833. mute.children('button').attr('title', mejs.i18n.t('mejs.mute')).attr('aria-label', mejs.i18n.t('mejs.mute'));
  1834. }
  1835. // top/left of full size volume slider background
  1836. var totalPosition = volumeTotal.position();
  1837. // position slider
  1838. if (mode == 'vertical') {
  1839. var
  1840. // height of the full size volume slider background
  1841. totalHeight = volumeTotal.height(),
  1842. // the new top position based on the current volume
  1843. // 70% volume on 100px height == top:30px
  1844. newTop = totalHeight - (totalHeight * volume);
  1845. // handle
  1846. volumeHandle.css('top', Math.round(totalPosition.top + newTop - (volumeHandle.height() / 2)));
  1847. // show the current visibility
  1848. volumeCurrent.height(totalHeight - newTop);
  1849. volumeCurrent.css('top', totalPosition.top + newTop);
  1850. } else {
  1851. var
  1852. // height of the full size volume slider background
  1853. totalWidth = volumeTotal.width(),
  1854. // the new left position based on the current volume
  1855. newLeft = totalWidth * volume;
  1856. // handle
  1857. volumeHandle.css('left', Math.round(totalPosition.left + newLeft - (volumeHandle.width() / 2)));
  1858. // rezize the current part of the volume bar
  1859. volumeCurrent.width(Math.round(newLeft));
  1860. }
  1861. },
  1862. handleVolumeMove = function (e) {
  1863. var volume = null,
  1864. totalOffset = volumeTotal.offset();
  1865. // calculate the new volume based on the moust position
  1866. if (mode === 'vertical') {
  1867. var
  1868. railHeight = volumeTotal.height(),
  1869. newY = e.pageY - totalOffset.top;
  1870. volume = (railHeight - newY) / railHeight;
  1871. // the controls just hide themselves (usually when mouse moves too far up)
  1872. if (totalOffset.top === 0 || totalOffset.left === 0) {
  1873. return;
  1874. }
  1875. } else {
  1876. var
  1877. railWidth = volumeTotal.width(),
  1878. newX = e.pageX - totalOffset.left;
  1879. volume = newX / railWidth;
  1880. }
  1881. // ensure the volume isn't outside 0-1
  1882. volume = Math.max(0, volume);
  1883. volume = Math.min(volume, 1);
  1884. // position the slider and handle
  1885. positionVolumeHandle(volume);
  1886. // set the media object (this will trigger the volumechanged event)
  1887. if (volume === 0) {
  1888. media.setMuted(true);
  1889. } else {
  1890. media.setMuted(false);
  1891. }
  1892. media.setVolume(volume);
  1893. },
  1894. mouseIsDown = false,
  1895. mouseIsOver = false;
  1896. // SLIDER
  1897. mute
  1898. .hover(function () {
  1899. volumeSlider.show();
  1900. mouseIsOver = true;
  1901. }, function () {
  1902. mouseIsOver = false;
  1903. if (!mouseIsDown && mode == 'vertical') {
  1904. volumeSlider.hide();
  1905. }
  1906. });
  1907. var updateVolumeSlider = function (e) {
  1908. var volume = Math.floor(media.volume * 100);
  1909. volumeSlider.attr({
  1910. 'aria-label': mejs.i18n.t('mejs.volume-slider'),
  1911. 'aria-valuemin': 0,
  1912. 'aria-valuemax': 100,
  1913. 'aria-valuenow': volume,
  1914. 'aria-valuetext': volume + '%',
  1915. 'role': 'slider',
  1916. 'tabindex': 0
  1917. });
  1918. };
  1919. volumeSlider
  1920. .bind('mouseover', function () {
  1921. mouseIsOver = true;
  1922. })
  1923. .bind('mousedown', function (e) {
  1924. handleVolumeMove(e);
  1925. t.globalBind('mousemove.vol', function (e) {
  1926. handleVolumeMove(e);
  1927. });
  1928. t.globalBind('mouseup.vol', function () {
  1929. mouseIsDown = false;
  1930. t.globalUnbind('.vol');
  1931. if (!mouseIsOver && mode == 'vertical') {
  1932. volumeSlider.hide();
  1933. }
  1934. });
  1935. mouseIsDown = true;
  1936. return false;
  1937. })
  1938. .bind('keydown', function (e) {
  1939. var keyCode = e.keyCode;
  1940. var volume = media.volume;
  1941. switch (keyCode) {
  1942. case 38: // Up
  1943. volume = Math.min(volume + 0.1, 1);
  1944. break;
  1945. case 40: // Down
  1946. volume = Math.max(0, volume - 0.1);
  1947. break;
  1948. default:
  1949. return true;
  1950. }
  1951. mouseIsDown = false;
  1952. positionVolumeHandle(volume);
  1953. media.setVolume(volume);
  1954. return false;
  1955. });
  1956. // MUTE button
  1957. mute.find('button').click(function () {
  1958. media.setMuted(!media.muted);
  1959. });
  1960. //Keyboard input
  1961. mute.find('button').bind('focus', function () {
  1962. volumeSlider.show();
  1963. });
  1964. // listen for volume change events from other sources
  1965. media.addEventListener('volumechange', function (e) {
  1966. if (!mouseIsDown) {
  1967. if (media.muted) {
  1968. positionVolumeHandle(0);
  1969. mute.removeClass('mejs-mute').addClass('mejs-unmute');
  1970. } else {
  1971. positionVolumeHandle(media.volume);
  1972. mute.removeClass('mejs-unmute').addClass('mejs-mute');
  1973. }
  1974. }
  1975. updateVolumeSlider(e);
  1976. }, false);
  1977. // mutes the media and sets the volume icon muted if the initial volume is set to 0
  1978. if (player.options.startVolume === 0) {
  1979. media.setMuted(true);
  1980. }
  1981. // shim gets the startvolume as a parameter, but we have to set it on the native <video> and <audio> elements
  1982. if (media.pluginType === 'native') {
  1983. media.setVolume(player.options.startVolume);
  1984. }
  1985. t.container.on('controlsresize', function () {
  1986. if (media.muted) {
  1987. positionVolumeHandle(0);
  1988. mute.removeClass('mejs-mute').addClass('mejs-unmute');
  1989. } else {
  1990. positionVolumeHandle(media.volume);
  1991. mute.removeClass('mejs-unmute').addClass('mejs-mute');
  1992. }
  1993. });
  1994. }
  1995. });
  1996. })(mejs.$);
  1997. (function($) {
  1998. $.extend(mejs.MepDefaults, {
  1999. usePluginFullScreen: true,
  2000. newWindowCallback: function() { return '';},
  2001. fullscreenText: ''
  2002. });
  2003. $.extend(MediaElementPlayer.prototype, {
  2004. isFullScreen: false,
  2005. isNativeFullScreen: false,
  2006. isInIframe: false,
  2007. // Possible modes
  2008. // (1) 'native-native' HTML5 video + browser fullscreen (IE10+, etc.)
  2009. // (2) 'plugin-native' plugin video + browser fullscreen (fails in some versions of Firefox)
  2010. // (3) 'fullwindow' Full window (retains all UI)
  2011. // usePluginFullScreen = true
  2012. // (4) 'plugin-click' Flash 1 - click through with pointer events
  2013. // (5) 'plugin-hover' Flash 2 - hover popup in flash (IE6-8)
  2014. fullscreenMode: '',
  2015. buildfullscreen: function(player, controls, layers, media) {
  2016. if (!player.isVideo)
  2017. return;
  2018. player.isInIframe = (window.location != window.parent.location);
  2019. // detect on start
  2020. media.addEventListener('loadstart', function() { player.detectFullscreenMode(); });
  2021. // build button
  2022. var t = this,
  2023. hideTimeout = null,
  2024. fullscreenTitle = t.options.fullscreenText ? t.options.fullscreenText : mejs.i18n.t('mejs.fullscreen'),
  2025. fullscreenBtn =
  2026. $('<div class="mejs-button mejs-fullscreen-button">' +
  2027. '<button type="button" aria-controls="' + t.id + '" title="' + fullscreenTitle + '" aria-label="' + fullscreenTitle + '"></button>' +
  2028. '</div>')
  2029. .appendTo(controls)
  2030. .on('click', function() {
  2031. // toggle fullscreen
  2032. var isFullScreen = (mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || player.isFullScreen;
  2033. if (isFullScreen) {
  2034. player.exitFullScreen();
  2035. } else {
  2036. player.enterFullScreen();
  2037. }
  2038. })
  2039. .on('mouseover', function() {
  2040. // very old browsers with a plugin
  2041. if (t.fullscreenMode == 'plugin-hover') {
  2042. if (hideTimeout !== null) {
  2043. clearTimeout(hideTimeout);
  2044. delete hideTimeout;
  2045. }
  2046. var buttonPos = fullscreenBtn.offset(),
  2047. containerPos = player.container.offset();
  2048. media.positionFullscreenButton(buttonPos.left - containerPos.left, buttonPos.top - containerPos.top, true);
  2049. }
  2050. })
  2051. .on('mouseout', function() {
  2052. if (t.fullscreenMode == 'plugin-hover') {
  2053. if (hideTimeout !== null) {
  2054. clearTimeout(hideTimeout);
  2055. delete hideTimeout;
  2056. }
  2057. hideTimeout = setTimeout(function() {
  2058. media.hideFullscreenButton();
  2059. }, 1500);
  2060. }
  2061. });
  2062. player.fullscreenBtn = fullscreenBtn;
  2063. t.globalBind('keydown',function (e) {
  2064. if (e.keyCode == 27 && ((mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || t.isFullScreen)) {
  2065. player.exitFullScreen();
  2066. }
  2067. });
  2068. t.normalHeight = 0;
  2069. t.normalWidth = 0;
  2070. // setup native fullscreen event
  2071. if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
  2072. // chrome doesn't alays fire this in an iframe
  2073. var fullscreenChanged = function(e) {
  2074. if (player.isFullScreen) {
  2075. if (mejs.MediaFeatures.isFullScreen()) {
  2076. player.isNativeFullScreen = true;
  2077. // reset the controls once we are fully in full screen
  2078. player.setControlsSize();
  2079. } else {
  2080. player.isNativeFullScreen = false;
  2081. // when a user presses ESC
  2082. // make sure to put the player back into place
  2083. player.exitFullScreen();
  2084. }
  2085. }
  2086. };
  2087. player.globalBind(mejs.MediaFeatures.fullScreenEventName, fullscreenChanged);
  2088. }
  2089. },
  2090. detectFullscreenMode: function() {
  2091. var t = this,
  2092. mode = '',
  2093. features = mejs.MediaFeatures;
  2094. if (features.hasTrueNativeFullScreen && t.media.pluginType === 'native') {
  2095. mode = 'native-native';
  2096. } else if (features.hasTrueNativeFullScreen && t.media.pluginType !== 'native' && !features.hasFirefoxPluginMovingProblem) {
  2097. mode = 'plugin-native';
  2098. } else if (t.usePluginFullScreen) {
  2099. if (mejs.MediaFeatures.supportsPointerEvents) {
  2100. mode = 'plugin-click';
  2101. // this needs some special setup
  2102. t.createPluginClickThrough();
  2103. } else {
  2104. mode = 'plugin-hover';
  2105. }
  2106. } else {
  2107. mode = 'fullwindow';
  2108. }
  2109. t.fullscreenMode = mode;
  2110. return mode;
  2111. },
  2112. isPluginClickThroughCreated: false,
  2113. createPluginClickThrough: function() {
  2114. var t = this;
  2115. // don't build twice
  2116. if (t.isPluginClickThroughCreated) {
  2117. return;
  2118. }
  2119. // allows clicking through the fullscreen button and controls down directly to Flash
  2120. /*
  2121. When a user puts his mouse over the fullscreen button, we disable the controls so that mouse events can go down to flash (pointer-events)
  2122. We then put a divs over the video and on either side of the fullscreen button
  2123. to capture mouse movement and restore the controls once the mouse moves outside of the fullscreen button
  2124. */
  2125. var fullscreenIsDisabled = false,
  2126. restoreControls = function() {
  2127. if (fullscreenIsDisabled) {
  2128. // hide the hovers
  2129. for (var i in hoverDivs) {
  2130. hoverDivs[i].hide();
  2131. }
  2132. // restore the control bar
  2133. t.fullscreenBtn.css('pointer-events', '');
  2134. t.controls.css('pointer-events', '');
  2135. // prevent clicks from pausing video
  2136. t.media.removeEventListener('click', t.clickToPlayPauseCallback);
  2137. // store for later
  2138. fullscreenIsDisabled = false;
  2139. }
  2140. },
  2141. hoverDivs = {},
  2142. hoverDivNames = ['top', 'left', 'right', 'bottom'],
  2143. i, len,
  2144. positionHoverDivs = function() {
  2145. var fullScreenBtnOffsetLeft = fullscreenBtn.offset().left - t.container.offset().left,
  2146. fullScreenBtnOffsetTop = fullscreenBtn.offset().top - t.container.offset().top,
  2147. fullScreenBtnWidth = fullscreenBtn.outerWidth(true),
  2148. fullScreenBtnHeight = fullscreenBtn.outerHeight(true),
  2149. containerWidth = t.container.width(),
  2150. containerHeight = t.container.height();
  2151. for (i in hoverDivs) {
  2152. hoverDivs[i].css({position: 'absolute', top: 0, left: 0}); //, backgroundColor: '#f00'});
  2153. }
  2154. // over video, but not controls
  2155. hoverDivs['top']
  2156. .width( containerWidth )
  2157. .height( fullScreenBtnOffsetTop );
  2158. // over controls, but not the fullscreen button
  2159. hoverDivs['left']
  2160. .width( fullScreenBtnOffsetLeft )
  2161. .height( fullScreenBtnHeight )
  2162. .css({top: fullScreenBtnOffsetTop});
  2163. // after the fullscreen button
  2164. hoverDivs['right']
  2165. .width( containerWidth - fullScreenBtnOffsetLeft - fullScreenBtnWidth )
  2166. .height( fullScreenBtnHeight )
  2167. .css({top: fullScreenBtnOffsetTop,
  2168. left: fullScreenBtnOffsetLeft + fullScreenBtnWidth});
  2169. // under the fullscreen button
  2170. hoverDivs['bottom']
  2171. .width( containerWidth )
  2172. .height( containerHeight - fullScreenBtnHeight - fullScreenBtnOffsetTop )
  2173. .css({top: fullScreenBtnOffsetTop + fullScreenBtnHeight});
  2174. };
  2175. t.globalBind('resize', function() {
  2176. positionHoverDivs();
  2177. });
  2178. for (i = 0, len = hoverDivNames.length; i < len; i++) {
  2179. hoverDivs[hoverDivNames[i]] = $('<div class="mejs-fullscreen-hover" />').appendTo(t.container).mouseover(restoreControls).hide();
  2180. }
  2181. // on hover, kill the fullscreen button's HTML handling, allowing clicks down to Flash
  2182. fullscreenBtn.on('mouseover',function() {
  2183. if (!t.isFullScreen) {
  2184. var buttonPos = fullscreenBtn.offset(),
  2185. containerPos = player.container.offset();
  2186. // move the button in Flash into place
  2187. media.positionFullscreenButton(buttonPos.left - containerPos.left, buttonPos.top - containerPos.top, false);
  2188. // allows click through
  2189. t.fullscreenBtn.css('pointer-events', 'none');
  2190. t.controls.css('pointer-events', 'none');
  2191. // restore click-to-play
  2192. t.media.addEventListener('click', t.clickToPlayPauseCallback);
  2193. // show the divs that will restore things
  2194. for (i in hoverDivs) {
  2195. hoverDivs[i].show();
  2196. }
  2197. positionHoverDivs();
  2198. fullscreenIsDisabled = true;
  2199. }
  2200. });
  2201. // restore controls anytime the user enters or leaves fullscreen
  2202. media.addEventListener('fullscreenchange', function(e) {
  2203. t.isFullScreen = !t.isFullScreen;
  2204. // don't allow plugin click to pause video - messes with
  2205. // plugin's controls
  2206. if (t.isFullScreen) {
  2207. t.media.removeEventListener('click', t.clickToPlayPauseCallback);
  2208. } else {
  2209. t.media.addEventListener('click', t.clickToPlayPauseCallback);
  2210. }
  2211. restoreControls();
  2212. });
  2213. // the mouseout event doesn't work on the fullscren button, because we already killed the pointer-events
  2214. // so we use the document.mousemove event to restore controls when the mouse moves outside the fullscreen button
  2215. t.globalBind('mousemove', function(e) {
  2216. // if the mouse is anywhere but the fullsceen button, then restore it all
  2217. if (fullscreenIsDisabled) {
  2218. var fullscreenBtnPos = fullscreenBtn.offset();
  2219. if (e.pageY < fullscreenBtnPos.top || e.pageY > fullscreenBtnPos.top + fullscreenBtn.outerHeight(true) ||
  2220. e.pageX < fullscreenBtnPos.left || e.pageX > fullscreenBtnPos.left + fullscreenBtn.outerWidth(true)
  2221. ) {
  2222. fullscreenBtn.css('pointer-events', '');
  2223. t.controls.css('pointer-events', '');
  2224. fullscreenIsDisabled = false;
  2225. }
  2226. }
  2227. });
  2228. t.isPluginClickThroughCreated = true;
  2229. },
  2230. cleanfullscreen: function(player) {
  2231. player.exitFullScreen();
  2232. },
  2233. containerSizeTimeout: null,
  2234. enterFullScreen: function() {
  2235. var t = this;
  2236. if (mejs.MediaFeatures.isiOS && mejs.MediaFeatures.hasiOSFullScreen && typeof t.media.webkitEnterFullscreen === 'function') {
  2237. t.media.webkitEnterFullscreen();
  2238. return;
  2239. }
  2240. // set it to not show scroll bars so 100% will work
  2241. $(document.documentElement).addClass('mejs-fullscreen');
  2242. // store sizing
  2243. t.normalHeight = t.container.height();
  2244. t.normalWidth = t.container.width();
  2245. // attempt to do true fullscreen
  2246. if (t.fullscreenMode === 'native-native' || t.fullscreenMode === 'plugin-native') {
  2247. mejs.MediaFeatures.requestFullScreen(t.container[0]);
  2248. //return;
  2249. if (t.isInIframe) {
  2250. // sometimes exiting from fullscreen doesn't work
  2251. // notably in Chrome <iframe>. Fixed in version 17
  2252. setTimeout(function checkFullscreen() {
  2253. if (t.isNativeFullScreen) {
  2254. var percentErrorMargin = 0.002, // 0.2%
  2255. windowWidth = $(window).width(),
  2256. screenWidth = screen.width,
  2257. absDiff = Math.abs(screenWidth - windowWidth),
  2258. marginError = screenWidth * percentErrorMargin;
  2259. // check if the video is suddenly not really fullscreen
  2260. if (absDiff > marginError) {
  2261. // manually exit
  2262. t.exitFullScreen();
  2263. } else {
  2264. // test again
  2265. setTimeout(checkFullscreen, 500);
  2266. }
  2267. }
  2268. }, 1000);
  2269. }
  2270. } else if (t.fullscreeMode == 'fullwindow') {
  2271. // move into position
  2272. }
  2273. // make full size
  2274. t.container
  2275. .addClass('mejs-container-fullscreen')
  2276. .width('100%')
  2277. .height('100%');
  2278. //.css({position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, overflow: 'hidden', width: '100%', height: '100%', 'z-index': 1000});
  2279. // Only needed for safari 5.1 native full screen, can cause display issues elsewhere
  2280. // Actually, it seems to be needed for IE8, too
  2281. //if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
  2282. t.containerSizeTimeout = setTimeout(function() {
  2283. t.container.css({width: '100%', height: '100%'});
  2284. t.setControlsSize();
  2285. }, 500);
  2286. //}
  2287. if (t.media.pluginType === 'native') {
  2288. t.$media
  2289. .width('100%')
  2290. .height('100%');
  2291. } else {
  2292. t.container.find('.mejs-shim')
  2293. .width('100%')
  2294. .height('100%');
  2295. setTimeout(function() {
  2296. var win = $(window),
  2297. winW = win.width(),
  2298. winH = win.height();
  2299. t.media.setVideoSize(winW,winH);
  2300. }, 500);
  2301. }
  2302. t.layers.children('div')
  2303. .width('100%')
  2304. .height('100%');
  2305. if (t.fullscreenBtn) {
  2306. t.fullscreenBtn
  2307. .removeClass('mejs-fullscreen')
  2308. .addClass('mejs-unfullscreen');
  2309. }
  2310. t.setControlsSize();
  2311. t.isFullScreen = true;
  2312. var zoomFactor = Math.min(screen.width / t.width, screen.height / t.height);
  2313. t.container.find('.mejs-captions-text').css('font-size', zoomFactor * 100 + '%');
  2314. t.container.find('.mejs-captions-text').css('line-height', 'normal');
  2315. t.container.find('.mejs-captions-position').css('bottom', '45px');
  2316. t.container.trigger('enteredfullscreen');
  2317. },
  2318. exitFullScreen: function() {
  2319. var t = this;
  2320. // Prevent container from attempting to stretch a second time
  2321. clearTimeout(t.containerSizeTimeout);
  2322. // firefox can't adjust plugins
  2323. /*
  2324. if (t.media.pluginType !== 'native' && mejs.MediaFeatures.isFirefox) {
  2325. t.media.setFullscreen(false);
  2326. //player.isFullScreen = false;
  2327. return;
  2328. }
  2329. */
  2330. // come out of native fullscreen
  2331. if (mejs.MediaFeatures.hasTrueNativeFullScreen && (mejs.MediaFeatures.isFullScreen() || t.isFullScreen)) {
  2332. mejs.MediaFeatures.cancelFullScreen();
  2333. }
  2334. // restore scroll bars to document
  2335. $(document.documentElement).removeClass('mejs-fullscreen');
  2336. t.container
  2337. .removeClass('mejs-container-fullscreen')
  2338. .width(t.normalWidth)
  2339. .height(t.normalHeight);
  2340. if (t.media.pluginType === 'native') {
  2341. t.$media
  2342. .width(t.normalWidth)
  2343. .height(t.normalHeight);
  2344. } else {
  2345. t.container.find('.mejs-shim')
  2346. .width(t.normalWidth)
  2347. .height(t.normalHeight);
  2348. t.media.setVideoSize(t.normalWidth, t.normalHeight);
  2349. }
  2350. t.layers.children('div')
  2351. .width(t.normalWidth)
  2352. .height(t.normalHeight);
  2353. t.fullscreenBtn
  2354. .removeClass('mejs-unfullscreen')
  2355. .addClass('mejs-fullscreen');
  2356. t.setControlsSize();
  2357. t.isFullScreen = false;
  2358. t.container.find('.mejs-captions-text').css('font-size','');
  2359. t.container.find('.mejs-captions-text').css('line-height', '');
  2360. t.container.find('.mejs-captions-position').css('bottom', '');
  2361. t.container.trigger('exitedfullscreen');
  2362. }
  2363. });
  2364. })(mejs.$);
  2365. (function($) {
  2366. // Speed
  2367. $.extend(mejs.MepDefaults, {
  2368. // We also support to pass object like this:
  2369. // [{name: 'Slow', value: '0.75'}, {name: 'Normal', value: '1.00'}, ...]
  2370. speeds: ['2.00', '1.50', '1.25', '1.00', '0.75'],
  2371. defaultSpeed: '1.00',
  2372. speedChar: 'x'
  2373. });
  2374. $.extend(MediaElementPlayer.prototype, {
  2375. buildspeed: function(player, controls, layers, media) {
  2376. var t = this;
  2377. if (t.media.pluginType == 'native') {
  2378. var
  2379. speedButton = null,
  2380. speedSelector = null,
  2381. playbackSpeed = null,
  2382. inputId = null;
  2383. var speeds = [];
  2384. var defaultInArray = false;
  2385. for (var i=0, len=t.options.speeds.length; i < len; i++) {
  2386. var s = t.options.speeds[i];
  2387. if (typeof(s) === 'string'){
  2388. speeds.push({
  2389. name: s + t.options.speedChar,
  2390. value: s
  2391. });
  2392. if(s === t.options.defaultSpeed) {
  2393. defaultInArray = true;
  2394. }
  2395. }
  2396. else {
  2397. speeds.push(s);
  2398. if(s.value === t.options.defaultSpeed) {
  2399. defaultInArray = true;
  2400. }
  2401. }
  2402. }
  2403. if (!defaultInArray) {
  2404. speeds.push({
  2405. name: t.options.defaultSpeed + t.options.speedChar,
  2406. value: t.options.defaultSpeed
  2407. });
  2408. }
  2409. speeds.sort(function(a, b) {
  2410. return parseFloat(b.value) - parseFloat(a.value);
  2411. });
  2412. var getSpeedNameFromValue = function(value) {
  2413. for(i=0,len=speeds.length; i <len; i++) {
  2414. if (speeds[i].value === value) {
  2415. return speeds[i].name;
  2416. }
  2417. }
  2418. };
  2419. var html = '<div class="mejs-button mejs-speed-button">' +
  2420. '<button type="button">' + getSpeedNameFromValue(t.options.defaultSpeed) + '</button>' +
  2421. '<div class="mejs-speed-selector">' +
  2422. '<ul>';
  2423. for (i = 0, il = speeds.length; i<il; i++) {
  2424. inputId = t.id + '-speed-' + speeds[i].value;
  2425. html += '<li>' +
  2426. '<input type="radio" name="speed" ' +
  2427. 'value="' + speeds[i].value + '" ' +
  2428. 'id="' + inputId + '" ' +
  2429. (speeds[i].value === t.options.defaultSpeed ? ' checked' : '') +
  2430. ' />' +
  2431. '<label for="' + inputId + '" ' +
  2432. (speeds[i].value === t.options.defaultSpeed ? ' class="mejs-speed-selected"' : '') +
  2433. '>' + speeds[i].name + '</label>' +
  2434. '</li>';
  2435. }
  2436. html += '</ul></div></div>';
  2437. speedButton = $(html).appendTo(controls);
  2438. speedSelector = speedButton.find('.mejs-speed-selector');
  2439. playbackSpeed = t.options.defaultSpeed;
  2440. media.addEventListener('loadedmetadata', function(e) {
  2441. if (playbackSpeed) {
  2442. media.playbackRate = parseFloat(playbackSpeed);
  2443. }
  2444. }, true);
  2445. speedSelector
  2446. .on('click', 'input[type="radio"]', function() {
  2447. var newSpeed = $(this).attr('value');
  2448. playbackSpeed = newSpeed;
  2449. media.playbackRate = parseFloat(newSpeed);
  2450. speedButton.find('button').html(getSpeedNameFromValue(newSpeed));
  2451. speedButton.find('.mejs-speed-selected').removeClass('mejs-speed-selected');
  2452. speedButton.find('input[type="radio"]:checked').next().addClass('mejs-speed-selected');
  2453. });
  2454. speedButton
  2455. .one( 'mouseenter focusin', function() {
  2456. speedSelector
  2457. .height(
  2458. speedButton.find('.mejs-speed-selector ul').outerHeight(true) +
  2459. speedButton.find('.mejs-speed-translations').outerHeight(true))
  2460. .css('top', (-1 * speedSelector.height()) + 'px');
  2461. });
  2462. }
  2463. }
  2464. });
  2465. })(mejs.$);
  2466. (function($) {
  2467. // add extra default options
  2468. $.extend(mejs.MepDefaults, {
  2469. // this will automatically turn on a <track>
  2470. startLanguage: '',
  2471. tracksText: '',
  2472. // By default, no WAI-ARIA live region - don't make a
  2473. // screen reader speak captions over an audio track.
  2474. tracksAriaLive: false,
  2475. // option to remove the [cc] button when no <track kind="subtitles"> are present
  2476. hideCaptionsButtonWhenEmpty: true,
  2477. // If true and we only have one track, change captions to popup
  2478. toggleCaptionsButtonWhenOnlyOne: false,
  2479. // #id or .class
  2480. slidesSelector: ''
  2481. });
  2482. $.extend(MediaElementPlayer.prototype, {
  2483. hasChapters: false,
  2484. cleartracks: function(player, controls, layers, media){
  2485. if(player) {
  2486. if(player.captions) player.captions.remove();
  2487. if(player.chapters) player.chapters.remove();
  2488. if(player.captionsText) player.captionsText.remove();
  2489. if(player.captionsButton) player.captionsButton.remove();
  2490. }
  2491. },
  2492. buildtracks: function(player, controls, layers, media) {
  2493. if (player.tracks.length === 0)
  2494. return;
  2495. var t = this,
  2496. attr = t.options.tracksAriaLive ?
  2497. 'role="log" aria-live="assertive" aria-atomic="false"' : '',
  2498. tracksTitle = t.options.tracksText ? t.options.tracksText : mejs.i18n.t('mejs.captions-subtitles'),
  2499. i,
  2500. kind;
  2501. if (t.domNode.textTracks) { // if browser will do native captions, prefer mejs captions, loop through tracks and hide
  2502. for (i = t.domNode.textTracks.length - 1; i >= 0; i--) {
  2503. t.domNode.textTracks[i].mode = "hidden";
  2504. }
  2505. }
  2506. t.cleartracks(player, controls, layers, media);
  2507. player.chapters =
  2508. $('<div class="mejs-chapters mejs-layer"></div>')
  2509. .prependTo(layers).hide();
  2510. player.captions =
  2511. $('<div class="mejs-captions-layer mejs-layer"><div class="mejs-captions-position mejs-captions-position-hover" ' +
  2512. attr + '><span class="mejs-captions-text"></span></div></div>')
  2513. .prependTo(layers).hide();
  2514. player.captionsText = player.captions.find('.mejs-captions-text');
  2515. player.captionsButton =
  2516. $('<div class="mejs-button mejs-captions-button">'+
  2517. '<button type="button" aria-controls="' + t.id + '" title="' + tracksTitle + '" aria-label="' + tracksTitle + '"></button>'+
  2518. '<div class="mejs-captions-selector">'+
  2519. '<ul>'+
  2520. '<li>'+
  2521. '<input type="radio" name="' + player.id + '_captions" id="' + player.id + '_captions_none" value="none" checked="checked" />' +
  2522. '<label for="' + player.id + '_captions_none">' + mejs.i18n.t('mejs.none') +'</label>'+
  2523. '</li>' +
  2524. '</ul>'+
  2525. '</div>'+
  2526. '</div>')
  2527. .appendTo(controls);
  2528. var subtitleCount = 0;
  2529. for (i=0; i<player.tracks.length; i++) {
  2530. kind = player.tracks[i].kind;
  2531. if (kind === 'subtitles' || kind === 'captions') {
  2532. subtitleCount++;
  2533. }
  2534. }
  2535. // if only one language then just make the button a toggle
  2536. if (t.options.toggleCaptionsButtonWhenOnlyOne && subtitleCount == 1){
  2537. // click
  2538. player.captionsButton.on('click',function() {
  2539. if (player.selectedTrack === null) {
  2540. lang = player.tracks[0].srclang;
  2541. } else {
  2542. lang = 'none';
  2543. }
  2544. player.setTrack(lang);
  2545. });
  2546. } else {
  2547. // hover or keyboard focus
  2548. player.captionsButton.on( 'mouseenter focusin', function() {
  2549. $(this).find('.mejs-captions-selector').removeClass('mejs-offscreen');
  2550. })
  2551. // handle clicks to the language radio buttons
  2552. .on('click','input[type=radio]',function() {
  2553. lang = this.value;
  2554. player.setTrack(lang);
  2555. });
  2556. player.captionsButton.on( 'mouseleave focusout', function() {
  2557. $(this).find(".mejs-captions-selector").addClass("mejs-offscreen");
  2558. });
  2559. }
  2560. if (!player.options.alwaysShowControls) {
  2561. // move with controls
  2562. player.container
  2563. .bind('controlsshown', function () {
  2564. // push captions above controls
  2565. player.container.find('.mejs-captions-position').addClass('mejs-captions-position-hover');
  2566. })
  2567. .bind('controlshidden', function () {
  2568. if (!media.paused) {
  2569. // move back to normal place
  2570. player.container.find('.mejs-captions-position').removeClass('mejs-captions-position-hover');
  2571. }
  2572. });
  2573. } else {
  2574. player.container.find('.mejs-captions-position').addClass('mejs-captions-position-hover');
  2575. }
  2576. player.trackToLoad = -1;
  2577. player.selectedTrack = null;
  2578. player.isLoadingTrack = false;
  2579. // add to list
  2580. for (i=0; i<player.tracks.length; i++) {
  2581. kind = player.tracks[i].kind;
  2582. if (kind === 'subtitles' || kind === 'captions') {
  2583. player.addTrackButton(player.tracks[i].srclang, player.tracks[i].label);
  2584. }
  2585. }
  2586. // start loading tracks
  2587. player.loadNextTrack();
  2588. media.addEventListener('timeupdate',function() {
  2589. player.displayCaptions();
  2590. }, false);
  2591. if (player.options.slidesSelector !== '') {
  2592. player.slidesContainer = $(player.options.slidesSelector);
  2593. media.addEventListener('timeupdate',function() {
  2594. player.displaySlides();
  2595. }, false);
  2596. }
  2597. media.addEventListener('loadedmetadata', function() {
  2598. player.displayChapters();
  2599. }, false);
  2600. player.container.hover(
  2601. function () {
  2602. // chapters
  2603. if (player.hasChapters) {
  2604. player.chapters.removeClass('mejs-offscreen');
  2605. player.chapters.fadeIn(200).height(player.chapters.find('.mejs-chapter').outerHeight());
  2606. }
  2607. },
  2608. function () {
  2609. if (player.hasChapters && !media.paused) {
  2610. player.chapters.fadeOut(200, function() {
  2611. $(this).addClass('mejs-offscreen');
  2612. $(this).css('display','block');
  2613. });
  2614. }
  2615. });
  2616. t.container.on('controlsresize', function() {
  2617. t.adjustLanguageBox();
  2618. });
  2619. // check for autoplay
  2620. if (player.node.getAttribute('autoplay') !== null) {
  2621. player.chapters.addClass('mejs-offscreen');
  2622. }
  2623. },
  2624. setTrack: function(lang){
  2625. var t = this,
  2626. i;
  2627. if (lang == 'none') {
  2628. t.selectedTrack = null;
  2629. t.captionsButton.removeClass('mejs-captions-enabled');
  2630. } else {
  2631. for (i=0; i<t.tracks.length; i++) {
  2632. if (t.tracks[i].srclang == lang) {
  2633. if (t.selectedTrack === null)
  2634. t.captionsButton.addClass('mejs-captions-enabled');
  2635. t.selectedTrack = t.tracks[i];
  2636. t.captions.attr('lang', t.selectedTrack.srclang);
  2637. t.displayCaptions();
  2638. break;
  2639. }
  2640. }
  2641. }
  2642. },
  2643. loadNextTrack: function() {
  2644. var t = this;
  2645. t.trackToLoad++;
  2646. if (t.trackToLoad < t.tracks.length) {
  2647. t.isLoadingTrack = true;
  2648. t.loadTrack(t.trackToLoad);
  2649. } else {
  2650. // add done?
  2651. t.isLoadingTrack = false;
  2652. t.checkForTracks();
  2653. }
  2654. },
  2655. loadTrack: function(index){
  2656. var
  2657. t = this,
  2658. track = t.tracks[index],
  2659. after = function() {
  2660. track.isLoaded = true;
  2661. t.enableTrackButton(track.srclang, track.label);
  2662. t.loadNextTrack();
  2663. };
  2664. if (track.src !== undefined || track.src !== "") {
  2665. $.ajax({
  2666. url: track.src,
  2667. dataType: "text",
  2668. success: function(d) {
  2669. // parse the loaded file
  2670. if (typeof d == "string" && (/<tt\s+xml/ig).exec(d)) {
  2671. track.entries = mejs.TrackFormatParser.dfxp.parse(d);
  2672. } else {
  2673. track.entries = mejs.TrackFormatParser.webvtt.parse(d);
  2674. }
  2675. after();
  2676. if (track.kind == 'chapters') {
  2677. t.media.addEventListener('play', function() {
  2678. if (t.media.duration > 0) {
  2679. t.displayChapters(track);
  2680. }
  2681. }, false);
  2682. }
  2683. if (track.kind == 'slides') {
  2684. t.setupSlides(track);
  2685. }
  2686. },
  2687. error: function() {
  2688. t.removeTrackButton(track.srclang);
  2689. t.loadNextTrack();
  2690. }
  2691. });
  2692. }
  2693. },
  2694. enableTrackButton: function(lang, label) {
  2695. var t = this;
  2696. if (label === '') {
  2697. label = mejs.language.codes[lang] || lang;
  2698. }
  2699. t.captionsButton
  2700. .find('input[value=' + lang + ']')
  2701. .prop('disabled',false)
  2702. .siblings('label')
  2703. .html( label );
  2704. // auto select
  2705. if (t.options.startLanguage == lang) {
  2706. $('#' + t.id + '_captions_' + lang).prop('checked', true).trigger('click');
  2707. }
  2708. t.adjustLanguageBox();
  2709. },
  2710. removeTrackButton: function(lang) {
  2711. var t = this;
  2712. t.captionsButton.find('input[value=' + lang + ']').closest('li').remove();
  2713. t.adjustLanguageBox();
  2714. },
  2715. addTrackButton: function(lang, label) {
  2716. var t = this;
  2717. if (label === '') {
  2718. label = mejs.language.codes[lang] || lang;
  2719. }
  2720. t.captionsButton.find('ul').append(
  2721. $('<li>'+
  2722. '<input type="radio" name="' + t.id + '_captions" id="' + t.id + '_captions_' + lang + '" value="' + lang + '" disabled="disabled" />' +
  2723. '<label for="' + t.id + '_captions_' + lang + '">' + label + ' (loading)' + '</label>'+
  2724. '</li>')
  2725. );
  2726. t.adjustLanguageBox();
  2727. // remove this from the dropdownlist (if it exists)
  2728. t.container.find('.mejs-captions-translations option[value=' + lang + ']').remove();
  2729. },
  2730. adjustLanguageBox:function() {
  2731. var t = this;
  2732. // adjust the size of the outer box
  2733. t.captionsButton.find('.mejs-captions-selector').height(
  2734. t.captionsButton.find('.mejs-captions-selector ul').outerHeight(true) +
  2735. t.captionsButton.find('.mejs-captions-translations').outerHeight(true)
  2736. );
  2737. },
  2738. checkForTracks: function() {
  2739. var
  2740. t = this,
  2741. hasSubtitles = false;
  2742. // check if any subtitles
  2743. if (t.options.hideCaptionsButtonWhenEmpty) {
  2744. for (var i=0; i<t.tracks.length; i++) {
  2745. var kind = t.tracks[i].kind;
  2746. if ((kind === 'subtitles' || kind === 'captions') && t.tracks[i].isLoaded) {
  2747. hasSubtitles = true;
  2748. break;
  2749. }
  2750. }
  2751. if (!hasSubtitles) {
  2752. t.captionsButton.hide();
  2753. t.setControlsSize();
  2754. }
  2755. }
  2756. },
  2757. displayCaptions: function() {
  2758. if (typeof this.tracks == 'undefined')
  2759. return;
  2760. var
  2761. t = this,
  2762. i,
  2763. track = t.selectedTrack;
  2764. if (track !== null && track.isLoaded) {
  2765. for (i=0; i<track.entries.times.length; i++) {
  2766. if (t.media.currentTime >= track.entries.times[i].start && t.media.currentTime <= track.entries.times[i].stop) {
  2767. // Set the line before the timecode as a class so the cue can be targeted if needed
  2768. t.captionsText.html(track.entries.text[i]).attr('class', 'mejs-captions-text ' + (track.entries.times[i].identifier || ''));
  2769. t.captions.show().height(0);
  2770. return; // exit out if one is visible;
  2771. }
  2772. }
  2773. t.captions.hide();
  2774. } else {
  2775. t.captions.hide();
  2776. }
  2777. },
  2778. setupSlides: function(track) {
  2779. var t = this;
  2780. t.slides = track;
  2781. t.slides.entries.imgs = [t.slides.entries.text.length];
  2782. t.showSlide(0);
  2783. },
  2784. showSlide: function(index) {
  2785. if (typeof this.tracks == 'undefined' || typeof this.slidesContainer == 'undefined') {
  2786. return;
  2787. }
  2788. var t = this,
  2789. url = t.slides.entries.text[index],
  2790. img = t.slides.entries.imgs[index];
  2791. if (typeof img == 'undefined' || typeof img.fadeIn == 'undefined') {
  2792. t.slides.entries.imgs[index] = img = $('<img src="' + url + '">')
  2793. .on('load', function() {
  2794. img.appendTo(t.slidesContainer)
  2795. .hide()
  2796. .fadeIn()
  2797. .siblings(':visible')
  2798. .fadeOut();
  2799. });
  2800. } else {
  2801. if (!img.is(':visible') && !img.is(':animated')) {
  2802. //
  2803. img.fadeIn()
  2804. .siblings(':visible')
  2805. .fadeOut();
  2806. }
  2807. }
  2808. },
  2809. displaySlides: function() {
  2810. if (typeof this.slides == 'undefined')
  2811. return;
  2812. var
  2813. t = this,
  2814. slides = t.slides,
  2815. i;
  2816. for (i=0; i<slides.entries.times.length; i++) {
  2817. if (t.media.currentTime >= slides.entries.times[i].start && t.media.currentTime <= slides.entries.times[i].stop){
  2818. t.showSlide(i);
  2819. return; // exit out if one is visible;
  2820. }
  2821. }
  2822. },
  2823. displayChapters: function() {
  2824. var
  2825. t = this,
  2826. i;
  2827. for (i=0; i<t.tracks.length; i++) {
  2828. if (t.tracks[i].kind == 'chapters' && t.tracks[i].isLoaded) {
  2829. t.drawChapters(t.tracks[i]);
  2830. t.hasChapters = true;
  2831. break;
  2832. }
  2833. }
  2834. },
  2835. drawChapters: function(chapters) {
  2836. var
  2837. t = this,
  2838. i,
  2839. dur,
  2840. //width,
  2841. //left,
  2842. percent = 0,
  2843. usedPercent = 0;
  2844. t.chapters.empty();
  2845. for (i=0; i<chapters.entries.times.length; i++) {
  2846. dur = chapters.entries.times[i].stop - chapters.entries.times[i].start;
  2847. percent = Math.floor(dur / t.media.duration * 100);
  2848. if (percent + usedPercent > 100 || // too large
  2849. i == chapters.entries.times.length-1 && percent + usedPercent < 100) // not going to fill it in
  2850. {
  2851. percent = 100 - usedPercent;
  2852. }
  2853. //width = Math.floor(t.width * dur / t.media.duration);
  2854. //left = Math.floor(t.width * chapters.entries.times[i].start / t.media.duration);
  2855. //if (left + width > t.width) {
  2856. // width = t.width - left;
  2857. //}
  2858. t.chapters.append( $(
  2859. '<div class="mejs-chapter" rel="' + chapters.entries.times[i].start + '" style="left: ' + usedPercent.toString() + '%;width: ' + percent.toString() + '%;">' +
  2860. '<div class="mejs-chapter-block' + ((i==chapters.entries.times.length-1) ? ' mejs-chapter-block-last' : '') + '">' +
  2861. '<span class="ch-title">' + chapters.entries.text[i] + '</span>' +
  2862. '<span class="ch-time">' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].start, t.options) + '&ndash;' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].stop, t.options) + '</span>' +
  2863. '</div>' +
  2864. '</div>'));
  2865. usedPercent += percent;
  2866. }
  2867. t.chapters.find('div.mejs-chapter').click(function() {
  2868. t.media.setCurrentTime( parseFloat( $(this).attr('rel') ) );
  2869. if (t.media.paused) {
  2870. t.media.play();
  2871. }
  2872. });
  2873. t.chapters.show();
  2874. }
  2875. });
  2876. mejs.language = {
  2877. codes: {
  2878. af:'Afrikaans',
  2879. sq:'Albanian',
  2880. ar:'Arabic',
  2881. be:'Belarusian',
  2882. bg:'Bulgarian',
  2883. ca:'Catalan',
  2884. zh:'Chinese',
  2885. 'zh-cn':'Chinese Simplified',
  2886. 'zh-tw':'Chinese Traditional',
  2887. hr:'Croatian',
  2888. cs:'Czech',
  2889. da:'Danish',
  2890. nl:'Dutch',
  2891. en:'English',
  2892. et:'Estonian',
  2893. fl:'Filipino',
  2894. fi:'Finnish',
  2895. fr:'French',
  2896. gl:'Galician',
  2897. de:'German',
  2898. el:'Greek',
  2899. ht:'Haitian Creole',
  2900. iw:'Hebrew',
  2901. hi:'Hindi',
  2902. hu:'Hungarian',
  2903. is:'Icelandic',
  2904. id:'Indonesian',
  2905. ga:'Irish',
  2906. it:'Italian',
  2907. ja:'Japanese',
  2908. ko:'Korean',
  2909. lv:'Latvian',
  2910. lt:'Lithuanian',
  2911. mk:'Macedonian',
  2912. ms:'Malay',
  2913. mt:'Maltese',
  2914. no:'Norwegian',
  2915. fa:'Persian',
  2916. pl:'Polish',
  2917. pt:'Portuguese',
  2918. // 'pt-pt':'Portuguese (Portugal)',
  2919. ro:'Romanian',
  2920. ru:'Russian',
  2921. sr:'Serbian',
  2922. sk:'Slovak',
  2923. sl:'Slovenian',
  2924. es:'Spanish',
  2925. sw:'Swahili',
  2926. sv:'Swedish',
  2927. tl:'Tagalog',
  2928. th:'Thai',
  2929. tr:'Turkish',
  2930. uk:'Ukrainian',
  2931. vi:'Vietnamese',
  2932. cy:'Welsh',
  2933. yi:'Yiddish'
  2934. }
  2935. };
  2936. /*
  2937. Parses WebVTT format which should be formatted as
  2938. ================================
  2939. WEBVTT
  2940. 1
  2941. 00:00:01,1 --> 00:00:05,000
  2942. A line of text
  2943. 2
  2944. 00:01:15,1 --> 00:02:05,000
  2945. A second line of text
  2946. ===============================
  2947. Adapted from: http://www.delphiki.com/html5/playr
  2948. */
  2949. mejs.TrackFormatParser = {
  2950. webvtt: {
  2951. pattern_timecode: /^((?:[0-9]{1,2}:)?[0-9]{2}:[0-9]{2}([,.][0-9]{1,3})?) --\> ((?:[0-9]{1,2}:)?[0-9]{2}:[0-9]{2}([,.][0-9]{3})?)(.*)$/,
  2952. parse: function(trackText) {
  2953. var
  2954. i = 0,
  2955. lines = mejs.TrackFormatParser.split2(trackText, /\r?\n/),
  2956. entries = {text:[], times:[]},
  2957. timecode,
  2958. text,
  2959. identifier;
  2960. for(; i<lines.length; i++) {
  2961. timecode = this.pattern_timecode.exec(lines[i]);
  2962. if (timecode && i<lines.length) {
  2963. if ((i - 1) >= 0 && lines[i - 1] !== '') {
  2964. identifier = lines[i - 1];
  2965. }
  2966. i++;
  2967. // grab all the (possibly multi-line) text that follows
  2968. text = lines[i];
  2969. i++;
  2970. while(lines[i] !== '' && i<lines.length){
  2971. text = text + '\n' + lines[i];
  2972. i++;
  2973. }
  2974. text = $.trim(text).replace(/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig, "<a href='$1' target='_blank'>$1</a>");
  2975. // Text is in a different array so I can use .join
  2976. entries.text.push(text);
  2977. entries.times.push(
  2978. {
  2979. identifier: identifier,
  2980. start: (mejs.Utility.convertSMPTEtoSeconds(timecode[1]) === 0) ? 0.200 : mejs.Utility.convertSMPTEtoSeconds(timecode[1]),
  2981. stop: mejs.Utility.convertSMPTEtoSeconds(timecode[3]),
  2982. settings: timecode[5]
  2983. });
  2984. }
  2985. identifier = '';
  2986. }
  2987. return entries;
  2988. }
  2989. },
  2990. // Thanks to Justin Capella: https://github.com/johndyer/mediaelement/pull/420
  2991. dfxp: {
  2992. parse: function(trackText) {
  2993. trackText = $(trackText).filter("tt");
  2994. var
  2995. i = 0,
  2996. container = trackText.children("div").eq(0),
  2997. lines = container.find("p"),
  2998. styleNode = trackText.find("#" + container.attr("style")),
  2999. styles,
  3000. text,
  3001. entries = {text:[], times:[]};
  3002. if (styleNode.length) {
  3003. var attributes = styleNode.removeAttr("id").get(0).attributes;
  3004. if (attributes.length) {
  3005. styles = {};
  3006. for (i = 0; i < attributes.length; i++) {
  3007. styles[attributes[i].name.split(":")[1]] = attributes[i].value;
  3008. }
  3009. }
  3010. }
  3011. for(i = 0; i<lines.length; i++) {
  3012. var style;
  3013. var _temp_times = {
  3014. start: null,
  3015. stop: null,
  3016. style: null
  3017. };
  3018. if (lines.eq(i).attr("begin")) _temp_times.start = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i).attr("begin"));
  3019. if (!_temp_times.start && lines.eq(i-1).attr("end")) _temp_times.start = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i-1).attr("end"));
  3020. if (lines.eq(i).attr("end")) _temp_times.stop = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i).attr("end"));
  3021. if (!_temp_times.stop && lines.eq(i+1).attr("begin")) _temp_times.stop = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i+1).attr("begin"));
  3022. if (styles) {
  3023. style = "";
  3024. for (var _style in styles) {
  3025. style += _style + ":" + styles[_style] + ";";
  3026. }
  3027. }
  3028. if (style) _temp_times.style = style;
  3029. if (_temp_times.start === 0) _temp_times.start = 0.200;
  3030. entries.times.push(_temp_times);
  3031. text = $.trim(lines.eq(i).html()).replace(/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig, "<a href='$1' target='_blank'>$1</a>");
  3032. entries.text.push(text);
  3033. }
  3034. return entries;
  3035. }
  3036. },
  3037. split2: function (text, regex) {
  3038. // normal version for compliant browsers
  3039. // see below for IE fix
  3040. return text.split(regex);
  3041. }
  3042. };
  3043. // test for browsers with bad String.split method.
  3044. if ('x\n\ny'.split(/\n/gi).length != 3) {
  3045. // add super slow IE8 and below version
  3046. mejs.TrackFormatParser.split2 = function(text, regex) {
  3047. var
  3048. parts = [],
  3049. chunk = '',
  3050. i;
  3051. for (i=0; i<text.length; i++) {
  3052. chunk += text.substring(i,i+1);
  3053. if (regex.test(chunk)) {
  3054. parts.push(chunk.replace(regex, ''));
  3055. chunk = '';
  3056. }
  3057. }
  3058. parts.push(chunk);
  3059. return parts;
  3060. };
  3061. }
  3062. })(mejs.$);
  3063. // Source Chooser Plugin
  3064. (function($) {
  3065. $.extend(mejs.MepDefaults, {
  3066. sourcechooserText: ''
  3067. });
  3068. $.extend(MediaElementPlayer.prototype, {
  3069. buildsourcechooser: function(player, controls, layers, media) {
  3070. var
  3071. t = this,
  3072. sourceTitle = t.options.sourcechooserText ? t.options.sourcechooserText : mejs.i18n.t('mejs.source-chooser'),
  3073. hoverTimeout
  3074. ;
  3075. player.sourcechooserButton =
  3076. $('<div class="mejs-button mejs-sourcechooser-button">'+
  3077. '<button type="button" role="button" aria-haspopup="true" aria-owns="' + t.id + '" title="' + sourceTitle + '" aria-label="' + sourceTitle + '"></button>'+
  3078. '<div class="mejs-sourcechooser-selector mejs-offscreen" role="menu" aria-expanded="false" aria-hidden="true">'+
  3079. '<ul>'+
  3080. '</ul>'+
  3081. '</div>'+
  3082. '</div>')
  3083. .appendTo(controls)
  3084. // hover
  3085. .hover(function() {
  3086. clearTimeout(hoverTimeout);
  3087. player.showSourcechooserSelector();
  3088. }, function() {
  3089. var self = $(this);
  3090. hoverTimeout = setTimeout(function () {
  3091. player.hideSourcechooserSelector();
  3092. }, 500);
  3093. })
  3094. // keyboard menu activation
  3095. .on('keydown', function (e) {
  3096. var keyCode = e.keyCode;
  3097. switch (keyCode) {
  3098. case 32: // space
  3099. if (!mejs.MediaFeatures.isFirefox) { // space sends the click event in Firefox
  3100. player.showSourcechooserSelector();
  3101. }
  3102. $(this).find('.mejs-sourcechooser-selector')
  3103. .find('input[type=radio]:checked').first().focus();
  3104. break;
  3105. case 13: // enter
  3106. player.showSourcechooserSelector();
  3107. $(this).find('.mejs-sourcechooser-selector')
  3108. .find('input[type=radio]:checked').first().focus();
  3109. break;
  3110. case 27: // esc
  3111. player.hideSourcechooserSelector();
  3112. $(this).find('button').focus();
  3113. break;
  3114. default:
  3115. return true;
  3116. }
  3117. })
  3118. // close menu when tabbing away
  3119. .on('focusout', mejs.Utility.debounce(function (e) { // Safari triggers focusout multiple times
  3120. // Firefox does NOT support e.relatedTarget to see which element
  3121. // just lost focus, so wait to find the next focused element
  3122. setTimeout(function () {
  3123. var parent = $(document.activeElement).closest('.mejs-sourcechooser-selector');
  3124. if (!parent.length) {
  3125. // focus is outside the control; close menu
  3126. player.hideSourcechooserSelector();
  3127. }
  3128. }, 0);
  3129. }, 100))
  3130. // handle clicks to the source radio buttons
  3131. .delegate('input[type=radio]', 'click', function() {
  3132. // set aria states
  3133. $(this).attr('aria-selected', true).attr('checked', 'checked');
  3134. $(this).closest('.mejs-sourcechooser-selector').find('input[type=radio]').not(this).attr('aria-selected', 'false').removeAttr('checked');
  3135. var src = this.value;
  3136. if (media.currentSrc != src) {
  3137. var currentTime = media.currentTime;
  3138. var paused = media.paused;
  3139. media.pause();
  3140. media.setSrc(src);
  3141. media.addEventListener('loadedmetadata', function(e) {
  3142. media.currentTime = currentTime;
  3143. }, true);
  3144. var canPlayAfterSourceSwitchHandler = function(e) {
  3145. if (!paused) {
  3146. media.play();
  3147. }
  3148. media.removeEventListener("canplay", canPlayAfterSourceSwitchHandler, true);
  3149. };
  3150. media.addEventListener('canplay', canPlayAfterSourceSwitchHandler, true);
  3151. media.load();
  3152. }
  3153. })
  3154. // Handle click so that screen readers can toggle the menu
  3155. .delegate('button', 'click', function (e) {
  3156. if ($(this).siblings('.mejs-sourcechooser-selector').hasClass('mejs-offscreen')) {
  3157. player.showSourcechooserSelector();
  3158. $(this).siblings('.mejs-sourcechooser-selector').find('input[type=radio]:checked').first().focus();
  3159. } else {
  3160. player.hideSourcechooserSelector();
  3161. }
  3162. });
  3163. // add to list
  3164. for (var i in this.node.children) {
  3165. var src = this.node.children[i];
  3166. if (src.nodeName === 'SOURCE' && (media.canPlayType(src.type) == 'probably' || media.canPlayType(src.type) == 'maybe')) {
  3167. player.addSourceButton(src.src, src.title, src.type, media.src == src.src);
  3168. }
  3169. }
  3170. },
  3171. addSourceButton: function(src, label, type, isCurrent) {
  3172. var t = this;
  3173. if (label === '' || label == undefined) {
  3174. label = src;
  3175. }
  3176. type = type.split('/')[1];
  3177. t.sourcechooserButton.find('ul').append(
  3178. $('<li>'+
  3179. '<input type="radio" name="' + t.id + '_sourcechooser" id="' + t.id + '_sourcechooser_' + label + type + '" role="menuitemradio" value="' + src + '" ' + (isCurrent ? 'checked="checked"' : '') + 'aria-selected="' + isCurrent + '"' + ' />'+
  3180. '<label for="' + t.id + '_sourcechooser_' + label + type + '" aria-hidden="true">' + label + ' (' + type + ')</label>'+
  3181. '</li>')
  3182. );
  3183. t.adjustSourcechooserBox();
  3184. },
  3185. adjustSourcechooserBox: function() {
  3186. var t = this;
  3187. // adjust the size of the outer box
  3188. t.sourcechooserButton.find('.mejs-sourcechooser-selector').height(
  3189. t.sourcechooserButton.find('.mejs-sourcechooser-selector ul').outerHeight(true)
  3190. );
  3191. },
  3192. hideSourcechooserSelector: function () {
  3193. this.sourcechooserButton.find('.mejs-sourcechooser-selector')
  3194. .addClass('mejs-offscreen')
  3195. .attr('aria-expanded', 'false')
  3196. .attr('aria-hidden', 'true')
  3197. .find('input[type=radio]') // make radios not fucusable
  3198. .attr('tabindex', '-1');
  3199. },
  3200. showSourcechooserSelector: function () {
  3201. this.sourcechooserButton.find('.mejs-sourcechooser-selector')
  3202. .removeClass('mejs-offscreen')
  3203. .attr('aria-expanded', 'true')
  3204. .attr('aria-hidden', 'false')
  3205. .find('input[type=radio]')
  3206. .attr('tabindex', '0');
  3207. }
  3208. });
  3209. })(mejs.$);
  3210. /*
  3211. * ContextMenu Plugin
  3212. *
  3213. *
  3214. */
  3215. (function($) {
  3216. $.extend(mejs.MepDefaults,
  3217. { 'contextMenuItems': [
  3218. // demo of a fullscreen option
  3219. {
  3220. render: function(player) {
  3221. // check for fullscreen plugin
  3222. if (typeof player.enterFullScreen == 'undefined')
  3223. return null;
  3224. if (player.isFullScreen) {
  3225. return mejs.i18n.t('mejs.fullscreen-off');
  3226. } else {
  3227. return mejs.i18n.t('mejs.fullscreen-on');
  3228. }
  3229. },
  3230. click: function(player) {
  3231. if (player.isFullScreen) {
  3232. player.exitFullScreen();
  3233. } else {
  3234. player.enterFullScreen();
  3235. }
  3236. }
  3237. }
  3238. ,
  3239. // demo of a mute/unmute button
  3240. {
  3241. render: function(player) {
  3242. if (player.media.muted) {
  3243. return mejs.i18n.t('mejs.unmute');
  3244. } else {
  3245. return mejs.i18n.t('mejs.mute');
  3246. }
  3247. },
  3248. click: function(player) {
  3249. if (player.media.muted) {
  3250. player.setMuted(false);
  3251. } else {
  3252. player.setMuted(true);
  3253. }
  3254. }
  3255. },
  3256. // separator
  3257. {
  3258. isSeparator: true
  3259. }
  3260. ,
  3261. // demo of simple download video
  3262. {
  3263. render: function(player) {
  3264. return mejs.i18n.t('mejs.download-video');
  3265. },
  3266. click: function(player) {
  3267. window.location.href = player.media.currentSrc;
  3268. }
  3269. }
  3270. ]}
  3271. );
  3272. $.extend(MediaElementPlayer.prototype, {
  3273. buildcontextmenu: function(player, controls, layers, media) {
  3274. // create context menu
  3275. player.contextMenu = $('<div class="mejs-contextmenu"></div>')
  3276. .appendTo($('body'))
  3277. .hide();
  3278. // create events for showing context menu
  3279. player.container.bind('contextmenu', function(e) {
  3280. if (player.isContextMenuEnabled) {
  3281. e.preventDefault();
  3282. player.renderContextMenu(e.clientX-1, e.clientY-1);
  3283. return false;
  3284. }
  3285. });
  3286. player.container.bind('click', function() {
  3287. player.contextMenu.hide();
  3288. });
  3289. player.contextMenu.bind('mouseleave', function() {
  3290. //
  3291. player.startContextMenuTimer();
  3292. });
  3293. },
  3294. cleancontextmenu: function(player) {
  3295. player.contextMenu.remove();
  3296. },
  3297. isContextMenuEnabled: true,
  3298. enableContextMenu: function() {
  3299. this.isContextMenuEnabled = true;
  3300. },
  3301. disableContextMenu: function() {
  3302. this.isContextMenuEnabled = false;
  3303. },
  3304. contextMenuTimeout: null,
  3305. startContextMenuTimer: function() {
  3306. //
  3307. var t = this;
  3308. t.killContextMenuTimer();
  3309. t.contextMenuTimer = setTimeout(function() {
  3310. t.hideContextMenu();
  3311. t.killContextMenuTimer();
  3312. }, 750);
  3313. },
  3314. killContextMenuTimer: function() {
  3315. var timer = this.contextMenuTimer;
  3316. //
  3317. if (timer != null) {
  3318. clearTimeout(timer);
  3319. delete timer;
  3320. timer = null;
  3321. }
  3322. },
  3323. hideContextMenu: function() {
  3324. this.contextMenu.hide();
  3325. },
  3326. renderContextMenu: function(x,y) {
  3327. // alway re-render the items so that things like "turn fullscreen on" and "turn fullscreen off" are always written correctly
  3328. var t = this,
  3329. html = '',
  3330. items = t.options.contextMenuItems;
  3331. for (var i=0, il=items.length; i<il; i++) {
  3332. if (items[i].isSeparator) {
  3333. html += '<div class="mejs-contextmenu-separator"></div>';
  3334. } else {
  3335. var rendered = items[i].render(t);
  3336. // render can return null if the item doesn't need to be used at the moment
  3337. if (rendered != null) {
  3338. html += '<div class="mejs-contextmenu-item" data-itemindex="' + i + '" id="element-' + (Math.random()*1000000) + '">' + rendered + '</div>';
  3339. }
  3340. }
  3341. }
  3342. // position and show the context menu
  3343. t.contextMenu
  3344. .empty()
  3345. .append($(html))
  3346. .css({top:y, left:x})
  3347. .show();
  3348. // bind events
  3349. t.contextMenu.find('.mejs-contextmenu-item').each(function() {
  3350. // which one is this?
  3351. var $dom = $(this),
  3352. itemIndex = parseInt( $dom.data('itemindex'), 10 ),
  3353. item = t.options.contextMenuItems[itemIndex];
  3354. // bind extra functionality?
  3355. if (typeof item.show != 'undefined')
  3356. item.show( $dom , t);
  3357. // bind click action
  3358. $dom.click(function() {
  3359. // perform click action
  3360. if (typeof item.click != 'undefined')
  3361. item.click(t);
  3362. // close
  3363. t.contextMenu.hide();
  3364. });
  3365. });
  3366. // stop the controls from hiding
  3367. setTimeout(function() {
  3368. t.killControlsTimer('rev3');
  3369. }, 100);
  3370. }
  3371. });
  3372. })(mejs.$);
  3373. (function($) {
  3374. // skip back button
  3375. $.extend(mejs.MepDefaults, {
  3376. skipBackInterval: 30,
  3377. // %1 will be replaced with skipBackInterval in this string
  3378. skipBackText: ''
  3379. });
  3380. $.extend(MediaElementPlayer.prototype, {
  3381. buildskipback: function(player, controls, layers, media) {
  3382. var
  3383. t = this,
  3384. defaultTitle = mejs.i18n.t('mejs.time-skip-back', t.options.skipBackInterval),
  3385. skipTitle = t.options.skipBackText ? t.options.skipBackText : defaultTitle,
  3386. // create the loop button
  3387. loop =
  3388. $('<div class="mejs-button mejs-skip-back-button">' +
  3389. '<button type="button" aria-controls="' + t.id + '" title="' + skipTitle + '" aria-label="' + skipTitle + '">' + t.options.skipBackInterval + '</button>' +
  3390. '</div>')
  3391. // append it to the toolbar
  3392. .appendTo(controls)
  3393. // add a click toggle event
  3394. .click(function() {
  3395. media.setCurrentTime(Math.max(media.currentTime - t.options.skipBackInterval, 0));
  3396. $(this).find('button').blur();
  3397. });
  3398. }
  3399. });
  3400. })(mejs.$);
  3401. /**
  3402. * Postroll plugin
  3403. */
  3404. (function($) {
  3405. $.extend(mejs.MepDefaults, {
  3406. postrollCloseText: ''
  3407. });
  3408. // Postroll
  3409. $.extend(MediaElementPlayer.prototype, {
  3410. buildpostroll: function(player, controls, layers, media) {
  3411. var
  3412. t = this,
  3413. postrollTitle = t.options.postrollCloseText ? t.options.postrollCloseText : mejs.i18n.t('mejs.close'),
  3414. postrollLink = t.container.find('link[rel="postroll"]').attr('href');
  3415. if (typeof postrollLink !== 'undefined') {
  3416. player.postroll =
  3417. $('<div class="mejs-postroll-layer mejs-layer"><a class="mejs-postroll-close" onclick="$(this).parent().hide();return false;">' + postrollTitle + '</a><div class="mejs-postroll-layer-content"></div></div>').prependTo(layers).hide();
  3418. t.media.addEventListener('ended', function (e) {
  3419. $.ajax({
  3420. dataType: 'html',
  3421. url: postrollLink,
  3422. success: function (data, textStatus) {
  3423. layers.find('.mejs-postroll-layer-content').html(data);
  3424. }
  3425. });
  3426. player.postroll.show();
  3427. }, false);
  3428. }
  3429. }
  3430. });
  3431. })(mejs.$);
  3432. /*
  3433. MediaElement-Markers is a MediaElement.js plugin that lets you add Visual Cues in the progress time rail.
  3434. This plugin also lets you register a custom callback function that will be called everytime the play position reaches a marker.
  3435. Marker position and a reference to the MediaElement Player object is passed to the registered callback function for any post processing. Marker color is configurable.
  3436. */
  3437. (function ($) {
  3438. // markers
  3439. $.extend(mejs.MepDefaults, {
  3440. markerColor: '#E9BC3D', //default marker color
  3441. markers: [],
  3442. markerCallback: function () {
  3443. }
  3444. });
  3445. $.extend(MediaElementPlayer.prototype, {
  3446. buildmarkers: function (player, controls, layers, media) {
  3447. var t = this,
  3448. i = 0,
  3449. currentPos = -1,
  3450. currentMarker = -1,
  3451. lastPlayPos = -1, //Track backward seek
  3452. lastMarkerCallBack = -1; //Prevents successive firing of callbacks
  3453. for (i = 0; i < player.options.markers.length; ++i) {
  3454. controls.find('.mejs-time-total').append('<span class="mejs-time-marker"></span>');
  3455. }
  3456. media.addEventListener('durationchange', function (e) {
  3457. player.setmarkers(controls);
  3458. });
  3459. media.addEventListener('timeupdate', function (e) {
  3460. currentPos = Math.floor(media.currentTime);
  3461. if (lastPlayPos > currentPos) {
  3462. if (lastMarkerCallBack > currentPos) {
  3463. lastMarkerCallBack = -1;
  3464. }
  3465. } else {
  3466. lastPlayPos = currentPos;
  3467. }
  3468. for (i = 0; i < player.options.markers.length; ++i) {
  3469. currentMarker = Math.floor(player.options.markers[i]);
  3470. if (currentPos === currentMarker && currentMarker !== lastMarkerCallBack) {
  3471. player.options.markerCallback(media, media.currentTime); //Fires the callback function
  3472. lastMarkerCallBack = currentMarker;
  3473. }
  3474. }
  3475. }, false);
  3476. },
  3477. setmarkers: function (controls) {
  3478. var t = this,
  3479. i = 0,
  3480. left;
  3481. for (i = 0; i < t.options.markers.length; ++i) {
  3482. if (Math.floor(t.options.markers[i]) <= t.media.duration && Math.floor(t.options.markers[i]) >= 0) {
  3483. left = 100 * Math.floor(t.options.markers[i]) / t.media.duration;
  3484. $(controls.find('.mejs-time-marker')[i]).css({
  3485. "width": "1px",
  3486. "left": left+"%",
  3487. "background": t.options.markerColor
  3488. });
  3489. }
  3490. }
  3491. }
  3492. });
  3493. })(mejs.$);