/** * Object description for PlayerHandler * The PlayerHandler chooses the right medium (jPlayer or image/p tag) to display content * from the loaded playlist. It handles all interaction (via controls) and working through * the whole playlist. Bumpers are taken into account. * * @param opts Object with options. See for overwriteable options PlayerHandler.defaults */ function PlayerHandler(opts) { //default option settings this.defaults = { playercontainer : null, //jquery object determining the div that holds both player/image/text div and controls playerdiv : null, //jquery object determining the div that holds the player html5 audio/video or flash playerwidth : "640px", //css style (so include 'px') width of the playerdiv ratio : '16:9', //ratio of the player. Can be 16:9 or 4:3 autoplay : false, //should the first item start playing automatically? loop : false, //should the playlist loop back to the first item after finishing? autoplayonloop : false, //when looping is turned on, should the looped playlist start automatically? showtitle : true, //should the title of the media be shown? autohide : {}, //autohide options for the player controls. true_fullscreen : true, // allow true fullscreen? default_supplied : '' // string of supplied formats for drawing the player. Should be given via V4aHtml5PlayerConfig and V4aPlaylist which bases it on the current domain settings }; /* merge defaults and opts, without modifying defaults, merged result resides in this.options */ this.options = $.extend({}, this.defaults, opts); //keep track of instances of this object so one instance can influence others this.index = PlayerHandler.instances.length; PlayerHandler.instances[this.index] = this; this.playlist = null; this.current_playlist_index = -1;//not 0, this index gets +1 on call of this.playNextItem, so for first item we want to get to 0 and need to start at -1 this.current_item;//item that is currently playing, or ready to play, in the player this.nonvideoTimeout = null;//something to store the image/text countdown timeout in so we can destroy it elsewhere this.nonvideoCounterInterval = null;//something to store the image/text countdown interval in, so we can destroy it elsewhere this.nonvideoProgress = 1;//progress in interval counter this.isFullscreen = false;//keep track of fullscreen state this.isPlaying = false;//keep track of wether item is playing or not (image/text) this.isPaused = false;//keep track of wether item is paused or not (image/text). isPlaying can also be false after Stop, not only after Pause, so this variable explicitely states Pause state. this.loadRegistered = false;//keep track of wether the load event has been triggered for the current item this.original_px_width = false; // initiate on false, will be set by the first call to calculateDimensions when given options.playerwidth is not in % this.original_px_height = false; // initiate on false, will be set by the first call to calculateDimensions when given options.playerwidth is not in % this.plugins = [] /**************** Setting Up The Player *******************************************************/ /** * Set up player size and which solution and formats are suplied. * Is called inside the startupPlayer ajax call on success. */ this.connectJplayer = function() { this.options.playerdiv.jPlayer('destroy'); // destroy old player stuff before initializing new stuff var myself = this; var ancestor_selector = '#'+this.options.playercontainer.attr('id'); //set width of ancestor so plugin stuff has a max width $(ancestor_selector).css('width', this.options.playerwidth); //initial calculation and setting of playerwidth and height this.calculateDimensions(true); this.options.playerdiv.jPlayer({ swfPath: "/krn/inc/player/flash", solution: "html, flash", supplied: (this.playlist ? this.playlist.supplied : this.options.default_supplied), // we need SOMETHING, even though there might be no playlist. Loading a new playlist later will redo this whole function with correct supplied parameters etc. cssSelectorAncestor: ancestor_selector, autohide: this.options.autohide, size: { width: myself.playerwidth, height: myself.playerheight, cssClass: "" }, ready: function() { //when player is ready redo its handlers and play first item // if there's no playlist we do not need to redefine the handlers or play the next item if(myself.playlist && myself.playlist.numitems > 0) { myself.redefineHandlers(); myself.playNextItem(); } else // we do want to show that there's no media in the player myself.showNoMediaMessage(); myself.options.playercontainer.trigger('player_ready'); }, //first of all 'playing' should be named 'startedplaying' //on touch devices "playing" can not trigger on first time use of video/audio tag, so we want the playbuttons to appear and urge the user to press 'play'. //For some unknown reason the big playbutton is not shown, but at least the small one is. playing: function() { myself.togglePlayButton(false); myself.isPlaying = true; myself.isPaused = false; }, timeupdate: function (event) { //on every timeupdate (256 ms) send the percentage to plugins myself.options.playercontainer.trigger('player_progressed', event.jPlayer.status.currentPercentAbsolute); }, ended: function() { //after item finished playing, register ended event, clear the media and play the next item myself.options.playercontainer.trigger('player_item_ended'); myself.options.playerdiv.jPlayer('clearMedia'); // this is needed to prevent continuing progress bar updates on Android 4 (Nexus 7) myself.playNextItem(); } }); }//end connectJplayer this.calculateDimensions = function(firsttime) { //if we originally stated that the size of the player should be in % every resize of screen should trigger a re-calculation //wether it be resize of screen or going fullscreen/restorescreen with the player. //otherwise the size will always be the same: rigid in px. //either way we'll have to calculate the height if(this.isFullscreen) { this.playerwidth = this.playerheight = '100%'; } else { if(this.original_px_width !== false) { // the original value was set, so we can reuse it and do not have to recalculate this.playerwidth = this.original_px_width; this.playerheight = this.original_px_height; } else { // recalculate or initial calculation of px values //get px width of playercontainer (but without px string in result) var playercontainer_width = $('#'+this.options.playercontainer.attr('id')).outerWidth(); this.playerwidth = playercontainer_width + 'px'; //the width of the actual play screen is always filling the playercontainer //calculate, acording to ratio, what the player height should be if(this.options.ratio == '16:9') var h = playercontainer_width / (16/9); else if (this.options.ratio == '4:3') var h = playercontainer_width / (4/3); if(this.options.autohide.restored == true) { //only do this when controls are set to disappear in normal view //get height of interface element bar interface_elm = this.options.playercontainer.find('.jp-interface'); interfaceheight = interface_elm.outerHeight(); interface_elm.css('margin-top', '-'+interfaceheight+'px'); //put interface bar on top of player instead of under by giving a negative margin } this.playerheight = Math.floor(h) + 'px'; } } //if this is the first setup and % was not present in the string playerwidth if(firsttime && this.options.playerwidth.indexOf("%") === -1) { //we can store the width and height in pixels (we do not have to recalculate for resizing) this.original_px_width = this.playerwidth; this.original_px_height = this.playerheight; } } /** * @param xmlfile url to xml file to get playlist from * @param callconnect When there's a playlist, should callconnect be called for certain? (otherwise it depends on if the supplied attribute of the playlist is different from what was set for the last playlist) * This method is called from V4aHtml5Player on setting up the player javascript. In that case callconnect is not set, so it will be set to true. */ this.startupPlayer = function(xmlfile, callconnect) { if(xmlfile) { if(callconnect == undefined) callconnect = true; this.parsePlaylist(xmlfile, callconnect); } else { this.connectJplayer(); } }//end startupPlayer method /** * Replace defect player with something of the right size and display an error message inside. * This way we don't have a broken player (html) showing the default and incorrect 'you need to update your flash' * warning. */ this.noPlayerShowWarning = function(errmsg) { this.options.playerdiv.jPlayer('destroy'); // destroy old player stuff before initializing new stuff var myself = this; var ancestor_selector = '#'+this.options.playercontainer.attr('id'); //set width of player so it won't screw up the page. $(ancestor_selector).css('width', this.options.playerwidth); this.calculateDimensions(true); this.options.playerdiv.css('width', this.playerwidth); this.options.playerdiv.css('height', this.playerheight); //position mediaplay this.options.playercontainer.find('.mediaplay').css({'height':this.playerheight, 'margin-top':'-'+this.playerheight}); //hide the play icon, it won't function anyway this.options.playercontainer.find('.jp-no-solution, .playicon').hide(); this.showError(errmsg); } /** * Shows an error inside the span.nomedia and makes it visible. */ this.showError = function(errmsg) { var ancestor_selector = '#'+this.options.playercontainer.attr('id'); // use nomedia span for error message and show it (in case it was hidden) $(ancestor_selector).find('.nomedia').text(errmsg).show(); } /** * @param xmlfile url to xml file to get playlist from * @param callconnect When there's a playlist, should callconnect be called for certain? (otherwise it depends on if the supplied attribute of the playlist is different from what was set for the last playlist) * This method is called by startupPlayer AND replacePlaylist. The latter does not want an unconditional callconnect, it depends on if the new playlist has a different supplied value from the old playlist. */ this.parsePlaylist = function(xmlfile, callconnect) { //get a playlist from an xml file for initial display /*this will result in an object of the following format: playlist { supplied: 'string of supplied formats', 0 : { type: 'movie', attributes: { cid : 'cid', title : 'title', sharelnk : 'sharelnk', .... } }, 1 : { type: 'image', attributes: { cid : 'cid', title : 'title', sharelnk : 'sharelnk', .... } }, } in the attributes object all attributes that are in the xml are collected. */ var myself = this; var oldplaylist = this.playlist; var jobject = this.options.playerdiv.data("jPlayer"); // ajax call $.ajax({ type: 'GET', url: xmlfile, dataType: 'xml', success: function(xml){ //for each item inside the playlist myself.playlist = new Object(); playlistnode = $(xml).find("playlist"); myself.playlist.supplied = playlistnode.attr('supplied'); i = 0; playlistnode.children().each(function() { playlistitem = {};//new object playlistitem.type = (this).nodeName; //since what we're looping over are already jquery representations of the DOM objects, no need to use $(this) = pass this to jquery to make it a jquery thingy attributes = {}; //collect all attributes in the form name = value in an object. $.each((this).attributes, function(i, attrib){ attributes[attrib.name] = attrib.value; }); if(playlistitem.type == 'image') attributes.poster = attributes.url; else attributes.poster = attributes.thumbnail; playlistitem.attributes = attributes; myself.playlist[i] = playlistitem; i++; }); myself.playlist.numitems = i; if(callconnect) { myself.connectJplayer(); } else { if(!jobject) { // there was no object yet, make one myself.connectJplayer(); } else { // supplied is not the same, so we need to reconnect the whole player in case a different solution is needed if(jobject.options.supplied != myself.playlist.supplied) myself.connectJplayer(); else { // supplied stayed the same, so after we have reset the whole playlist info (above) we should be able to just call playNextItem if(!oldplaylist) // if there was no playlist (empty player) we still need to redefine the handlers. myself.redefineHandlers(); myself.playNextItem(); } } } }, error: function(jqXHR, textStatus, errorThrown) { errmsg = ''; if(textStatus == 'timeout') errmsg = 'Timout bij het laden van de playlist' if(textStatus == 'parsererror') errmsg = 'Fout bij het verwerken van de playlist' myself.noPlayerShowWarning(errmsg); } }); } /** * Redefine the jplayer handlers so we can decide what to do according to * type of media for which the event was triggered. */ this.redefineHandlers = function() { playcont = this.options.playercontainer;//only for finding the buttons to put handles on, not inside those callback functions! playcont.find(".jp-previous, .jp-next, .jp-play, .jp-pause, .jp-stop, .jp-seek-bar, .jp-play-bar, .jp-full-screen, .jp-restore-screen, .mediaplay").unbind();//remove jplayer's native listeners var myself = this;//for use inside the bound function, the word this is reserverd for the element to which the click is bound //clicking on seekbar or play bar playcont.find(".jp-seek-bar, .jp-play-bar").bind('click', function(e) { var offset = $(this).offset(); var x = e.pageX - offset.left; var w = $(this).width(); var p = 100*x/w; myself.seek(p); }); //clicking on next button playcont.find(".jp-next").bind('click', function() { myself.next(); }); playcont.find(".jp-previous").bind('click', function() { myself.previous(); }); playcont.find(".jp-stop").bind('click', function() { myself.stop(); }); playcont.find(".jp-play").bind('click', function() { myself.play(); }); playcont.find(".jp-pause").bind('click', function() { myself.pause(); }); //clicking on fullscreen button playcont.find(".jp-full-screen").bind('click', function(e) { myself.fullscreen(); }); //clicking on fullscreen button playcont.find(".jp-restore-screen").bind('click', function(e) { myself.restoreScreen(); }); //when clicking on mediaplay div, which overlays the media playcont.find(".mediaplay").bind('click', function(e) { myself.mediaClicked(); }); playcont.bind($.jPlayer.event.error, function(event) { switch(event.jPlayer.error.type) { case $.jPlayer.error.NO_SUPPORT: myself.showError('Niet mogelijk om media af te spelen van het type dat in de playlist staat'); break; case $.jPlayer.error.URL: myself.showError('Media kan niet afgespeeld worden vanwege een ongeldige url'); break; case $.jPlayer.error.NO_SOLUTION: myself.showError('Geen afspeel mogelijkheid (flash of html5) beschikbaar voor gegeven media type'); break; } }); }//end redefineHandlers this.addPlugin = function(pluginobject) { this.plugins.push(pluginobject); } /**************** Player Controls Methods **************************************/ this.play = function() { //pause other players on the page this.pauseOthers(); if(this.isPaused) //if item was paused, we resume play this.options.playercontainer.trigger('player_play_resumed'); else {//not paused, so we start play. Either for first time or after stop // start will load the data first, if needed. So trigger this event here when load was not registered yet (on first play, contrary to replay after stop) // Using loadstart on the jplayer setup itself gets called later than this play and would screw up the stats order. if(!this.loadRegistered) { this.options.playercontainer.trigger('player_item_loaded'); this.loadRegistered = true; } this.options.playercontainer.trigger('player_play_started'); } //clear possible timeouts this.resetTimeouts(); if(this.playableByJplayer()) this.options.playerdiv.jPlayer('play'); else { //image or text //set new interval and timeout. Continue counting down to next item this.handleNonVideoDuration(); } // these things SHOULD set by jplayers 'playing' (should be:startedplaying) event for sound/video // but FF on Mac does not do that, so we'll do it here just to be sure. this.togglePlayButton(false); this.isPlaying = true; this.isPaused = false; } this.pause = function() { if(this.isPlaying) { //only invoke pause if item was actually playing. So not when we get paused by pauseOthers but there's nothing to pause this.options.playercontainer.trigger('player_pause_invoked'); if(this.playableByJplayer()) this.options.playerdiv.jPlayer('pause'); else { //image or text //clear possible timeouts this.resetTimeouts(); } this.togglePlayButton(true); this.isPlaying = false; this.isPaused = true; } } this.pauseOthers = function() { var self = this; $.each(PlayerHandler.instances, function(i, playerhandler) { if(self !== playerhandler) { // Do not do this instance. playerhandler.pause(); } }); } this.stop = function() { this.options.playercontainer.trigger('player_item_stopped'); if(this.playableByJplayer()) this.options.playerdiv.jPlayer('stop'); else { //image or text //clear possible timeouts this.resetTimeouts(); //reset progress to 0, just like video and audio do on stop this.nonvideoProgress = 0; this.updateProgress(); } this.togglePlayButton(true); this.isPaused = false; // unset this for when stop is called while item was paused. This way we don't trigger 'continue/resume' when play is started again. this.isPlaying = false; } this.previous = function() { //for bumpers previous should not work, so just return if(this.isBumper()) return; this.stop();//undo/reset progress bar // not previous item (on which playNextItem would go to the current item), but the one before that. this.current_playlist_index = this.current_playlist_index - 2; if(this.current_playlist_index < -1) { if(this.options.loop) { //number of items is not 0-based, so we need to substract 1 to get to the last item of the list var lastitem = this.playlist.numitems -1; // since playNextItem adds 1 to the given index, we need to substract another 1 to make playNextItem get to the actual last item this.current_playlist_index = lastitem -1; } else // no looping, so just reset to first item of playlist this.current_playlist_index = -1; //reset so playNextItem will start on item 0 } this.playNextItem(); } this.next = function() { //for bumpers next should not work, so just return if(this.isBumper()) return; this.stop();//undo/reset progress bar this.playNextItem(); } this.seek = function(position) { //for bumpers seeking should not work, so just return if(this.isBumper()) return; //have jplayer take care of the playbar positioning and stuff for all media if(this.playableByJplayer()) this.options.playerdiv.jPlayer("playHead", position); else { //for images/text we need to set the countdown to the right value this.nonvideoProgress = Math.floor(position/100 * this.current_item.attributes.duration);//calculate where we'd be in the countdown this.resetTimeouts();//reset timeouts this.updateProgress();//set playbar and current time to right values if(this.isPlaying) this.handleNonVideoDuration();// when we were playing, immediately continue from where the playhead is now positioned } } this.fullscreen = function() { this.options.playercontainer.trigger('player_fullscreen_invoked'); this.isFullscreen = true; var elm = this.options.playercontainer[0]; // with [0] we get the actual div element, instead of a jquery set of elements // https://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html#dom-element-requestfullscreen if(this.options.true_fullscreen) { if (elm.mozRequestFullScreen) elm.mozRequestFullScreen(); else if (elm.webkitRequestFullScreen) elm.webkitRequestFullScreen(); else if (elm.requestFullscreen) elm.requestFullscreen(); } // resize player to fit new screen size and for browsers that don't support fullscreen make it as big as possible inside the browser window this.resizePlayer(this.current_item);//resize player } this.restoreScreen = function() { this.options.playercontainer.trigger('player_restorescreen_invoked'); this.isFullscreen = false; // if we're in fullscreen mode, check for 3 different ways of implementation... if (this.options.true_fullscreen && (document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement) ) { if (document.mozCancelFullScreen) document.mozCancelFullScreen(); else if (document.webkitCancelFullScreen) document.webkitCancelFullScreen(); else if (document.exitFullScreen) document.exitFullScreen(); } this.resizePlayer(this.current_item);//resize player } this.mediaClicked = function() { this.options.playercontainer.trigger('player_media_clicked'); if(this.isPlaying) { this.pause(); //do something more for bumpers if(this.isBumper()) window.open(this.current_item.attributes.bumperlink);//open link of bumper in new window } else this.play(); } /**************** Methods To Manipulate HTML *******************************************************/ /** * Sets the playlist to null and clears the media from the player. */ this.clearPlayer = function() { this.playlist = null; this.options.playerdiv.jPlayer('clearMedia'); } /** * Destroy current jPlayer instance and set up a new one with a new playlist. * @param playlist URL to xml playlist * @param start_index Index on which to start in the playlist. When not given, starts at -1. * 2param autoplay Should we really start autoplaying? */ this.replacePlaylist = function (playlist, start_index, autoplay) { // reset our position in the playlist if(start_index !== undefined) this.current_playlist_index = start_index; else this.current_playlist_index = -1; // reset possible timeouts this.resetTimeouts() if(autoplay == undefined) // we want the just clicked item to start playing immediately this.options.autoplay = true; else this.options.autoplay = autoplay; // now parse the playlist and don't just call connectJplayer, only when necessary this.parsePlaylist(playlist, false); } /** * Plays the next item in the playlist. If we have not played anything yet, plays the first item. * When last item is reached will loop on to the first again if option loop is true. Otherwise it returns nothing * and playing is stopped (for this.play is never called). */ this.playNextItem = function() { //we increase the current index here instead of at the end of this function because //inside this function we call stuff like timeouts or play that take time. During that time //we want the current_playlist_index to refer to the item that started, and not already the next one. this.current_playlist_index++; //reset the progress of non-video/audio items (resetting timeouts is done inside this.play() but we can not do //the reset of progress there because play is also called on resuming play of paused items, in which case //progress needs to be in the state it was on pause) this.nonvideoProgress = 1; this.loadRegistered = false; // this item is not loaded yet. //when the given number is not in the playlist if(!this.playlist[this.current_playlist_index]) { startplaying = false; if(this.options.loop) {//if we can loop, start over again with counting this.current_playlist_index = 0; //reset playlist pointer if(this.options.autoplayonloop) startplaying = true; else this.stop();//stop everything the player is doing } else { this.stop(); this.current_playlist_index--;//set current item back to the actual item that's still in view so all controls still work on the right item } } else { startplaying = (this.current_playlist_index != 0 || this.options.autoplay == true); } this.current_item = this.playlist[this.current_playlist_index]; this.togglePlayButton(!startplaying);//when startplay is false, we want true on togglePlayButton and v.v. //when the first playlistitem is a bumper, get the poster of the next item and pretend it is the poster of the bumper if(this.current_playlist_index == 0 && this.isBumper()) { if(this.playlist[1].attributes.poster) this.current_item.attributes.poster = this.playlist[1].attributes.poster } this.hideInterfacesShowOne(); this.prepareMedia(); //only start playing when it's not the first item or when autoplay is true if(startplaying) this.play(); }// end playNextItem method /** * Hides all interfaces and then shows only the one that is requested. */ this.hideInterfacesShowOne = function() { playcont = this.options.playercontainer; //set the title of the media and add a type-specific class to the title container if(this.options.showtitle) { title_container = playcont.find('.mediatitle'); title_container.text(this.current_item.attributes.title); title_class = 'media_type_'+this.current_item.type; title_container.attr('class', 'mediatitle '+ title_class); this.options.playercontainer.trigger('player_shows_new_title', this.current_item.attributes.title); } //reset playbar to 0%, usefull for all types of media playcont.find(".jp-play-bar").width("0%"); //decide what to show again if(this.playableByJplayer()) { //hide all image/text related stuff this.options.playerdiv.find('img.media_item, p.media_item').remove(); //give the poster image width and height again this.options.playerdiv.find('img').css({'width':this.playerwidth, 'height':this.playerheight}); //only for not-first items do we resize the player to actual width. //when we'd do so for first items we'd get both the poster image and the videoscreen in view, making the player way too high //jPlayer takes care of hiding the poster and showing the videoscreen on play. if(this.current_playlist_index != 0){ if(this.current_item.type == 'movie') this.options.playerdiv.find('video, object').css({'width':this.playerwidth, 'height':this.playerheight}); } //show video/sound related stuff again playcont.find('.volume_control').css('visibility','visible'); } else { //hide all video/sound related stuff and the poster image of possible video this.options.playerdiv.find('audio, video, object, img').css({'width':'0px', 'height':'0px'}); this.options.playerdiv.find('img.media_item, p.media_item').remove();//remove old image/p content playcont.find('.volume_control').css('visibility','hidden'); playcont.find(".jp-seek-bar").css('width', '100%');//no need to download/seek, so seekbar can be 100% already. Should be because when first item is image/text, playbar won't show if seekbar is not set to 100% } //for bumpers we have to disable some stuff. Toggle css classes to change appearance if(this.isBumper()) { playcont.find(".jp-progress, .jp-controls").addClass('disabled'); playcont.find(".jp-jplayer").addClass('showPointer');/* only works on html 5 player, not flash */ } else { playcont.find(".jp-progress, .jp-controls").removeClass('disabled'); playcont.find(".jp-jplayer").removeClass('showPointer'); } } //end hideInterfacesShowOne this.prepareMedia = function() { if(this.playableByJplayer()){ this.options.playerdiv.jPlayer('setMedia', this.current_item.attributes);//pass attributes of requested playlistitem to setMedia }else { if(this.current_item.type == 'image') this.drawImage(); else this.drawText(); } } /** * Puts the current image into the 'player'. Measures its original width and height and scales it (down or up) * to fit the player dimensions. Also takes care of margin for centering the image in the 'player'. */ this.drawImage = function() { imgattr = this.current_item.attributes; //get the real dimensions of image var img = new Image(); //when fullscreen we can not just get the playerwidth/height value because it is 100%, telling us nothing about pixel size //so in fullscreen we use width() and height() of the actual image div. if(this.isFullscreen) { pwidth = this.options.playerdiv.width();//get pixel value but without 'px' pheight = this.options.playerdiv.height(); } else { //in normal mode we need to get the playerwidth/height (is in px) because for some reason the image div does not resize correctly for landscape images. pwidth = parseFloat(this.playerwidth); pheight = parseFloat(this.playerheight); } var myself = this; img.onload = function() { //'this' now refers to the image, 'myself' is used for the PlayerHandler instance //get actual dimensions of image var imgwidth = this.naturalWidth var imgheight = this.naturalHeight; var image = $(this); image.attr('class', 'media_item'); image.attr('alt', imgattr.title); //when image is landscape, set width to player width if((imgwidth / imgheight) > (pwidth/pheight)) { //calculate what the new height would be var newheight = imgheight / (imgwidth/pwidth ); var newcss = {}; newcss.width = pwidth + 'px'; newcss.height = newheight+'px'; image.css(newcss); }//height and width are either the same, or height is bigger (portrait) else { //calculate what the new width would be var newwidth = imgwidth / (imgheight/pheight ) var newcss = {}; newcss.width = newwidth+'px'; newcss.height = pheight+'px'; image.css(newcss); } myself.options.playerdiv.append(image); } img.src = imgattr.url; }//end drawImage this.drawText = function() { p = $('

'+this.current_item.attributes.description+'

'); this.options.playerdiv.append(p); var playerheight = parseFloat(this.playerheight); var player_fontsize = parseFloat(this.options.playercontainer.css("font-size")); var p_fontsize = parseFloat(p.css("font-size"));//always gets calculated size in px. //get percentage value of font-size (which is defined in css but not returned by p.css('font-size')) var p_font_percent = p_fontsize / player_fontsize * 100; //for as long as the p tag is bigger than the player, take 5% from the font size while(p.outerHeight(true) > playerheight) {//the true indicates to take into consideration the margin p_font_percent = p_font_percent - 5; p.css('font-size', p_font_percent +'%'); } } /** * Show current time and duration for non-video/non-audio files * After given duration calls play_item to proceed to the next item in the playlist * * @param total_duration Total duration of text/image. Used to show total duration * @param continue_from Time that countdown was at when pause was pressed. Interval should continue from here. * When this is ommitted, 0 is presumed. */ this.handleNonVideoDuration = function(total_duration) { total_duration = parseInt(this.current_item.attributes.duration); //setting the duration this.options.playercontainer.find(".jp-duration").text($.jPlayer.convertTime(total_duration)); //set up state so setInterval does not have to do that but can continue from here this.options.playercontainer.find(".jp-current-time").text($.jPlayer.convertTime(this.nonvideoProgress)); var myself = this; this.nonvideoCounterInterval = setInterval(function() { myself.nonvideoProgress++; myself.updateProgress(); }, 1000);//every second time_left = total_duration - this.nonvideoProgress; this.nonvideoTimeout = setTimeout(function () {myself.options.playercontainer.trigger('player_item_ended'); myself.playNextItem()}, time_left*1000);//to miliseconds }//end handleNonVideoDuration /** * Update the current time and the width of the play bar */ this.updateProgress = function () { percent = (this.nonvideoProgress/this.current_item.attributes.duration * 100); this.options.playercontainer.find(".jp-current-time").text($.jPlayer.convertTime(this.nonvideoProgress)); this.options.playercontainer.find(".jp-play-bar").width(percent+"%"); this.options.playercontainer.trigger('player_progressed', percent); } /** * Clear the non video timeouts/intervals. */ this.resetTimeouts = function() { clearTimeout(this.nonvideoTimeout); clearInterval(this.nonvideoCounterInterval); } /** * Shorthand function to check if current item is movie or sound. i.e. playable by the jplayer itself. */ this.playableByJplayer = function() { if(!this.current_item) // no item (for example empty playlist) can not be played by jplayer return false; return (this.current_item.type == 'movie' || this.current_item.type == 'sound'); } //end playableByJplayer /** * Shorthand function to check if current item is a bumper */ this.isBumper = function() { if(!this.playlist[this.current_playlist_index]) return false; return (this.current_item.attributes.bumper); } //end isBumper /** * Shorthand function show/hide the big play button on top of the player. */ this.togglePlayButton = function(show) { this.options.playercontainer.trigger('player_toggle_playbutton', show); butdiv = this.options.playercontainer.find('.mediaplay'); playicon = butdiv.find('span.playicon'); //hide any nomedia span that might be visible and show the playicon (which is only really shown if the whole div is shown) butdiv.find('span.nomedia').hide(); playicon.show(); //before possibly showing, check if style is set. If not, do so. if(!butdiv.attr('style')) butdiv.css({'height':this.playerheight, 'margin-top': '-'+(this.isFullscreen ? '0px' : this.playerheight)}) if(show) { playicon.show(); this.options.playercontainer.find('.jp-play').show(); this.options.playercontainer.find('.jp-pause').hide(); } else { playicon.hide(); this.options.playercontainer.find('.jp-play').hide(); this.options.playercontainer.find('.jp-pause').show(); } } //end togglePlayButton this.showNoMediaMessage = function() { butdiv = this.options.playercontainer.find('.mediaplay'); //before possibly showing, check if style is set. If not, do so. if(!butdiv.attr('style')) butdiv.css({'height':this.playerheight, 'margin-top': '-'+(this.isFullscreen ? '0px' : this.playerheight)}) //show the no media span, but hide the playbutton butdiv.find('span.nomedia').show(); butdiv.find('span.playicon').hide(); } /** * Emulates the _resizePlayer of jPlayer for images and text. * For audio/video calls the fullScreen/restoreScreen 'functions' of jPlayer because for those * media types resizePlayer is only called after fullscreen/restorescreen button is pressed on player. */ this.resizePlayer = function (item) { jobject = this.options.playerdiv.data("jPlayer"); if(this.isFullscreen) { jobject.options.fullWindow = true; this.calculateDimensions(); this.options.playercontainer.find('.mediaplay').css({'height':'100%', 'margin-top':'0px'}); } else { jobject.options.fullWindow = false; this.calculateDimensions(); this.options.playercontainer.find('.mediaplay').css({'height':this.playerheight, 'margin-top':'-'+this.playerheight}); } jobject._removeUiClass() //set the internal jplayer object 'original width' and 'original height' to the newly calculated values jobject.options.size.width = this.playerwidth; jobject.options.size.height = this.playerheight; jobject._setSize(); jobject._addUiClass(); // update the ui class, works according to stuff set in setSize, which uses the fullScreen option. //following can only be called for items that can actually play in jplayer (video/sound) for it sizes the audio/video tag or flash AND the poster image if(this.playableByJplayer()) jobject._updateSize() jobject._updateButtons(); jobject._updateAutohide(); jobject._trigger($.jPlayer.event.resize); //remove old sized image and/or p this.options.playerdiv.find('img.media_item, p.media_item').remove(); //now that all the resizing is done, redraw the image/text we were currently playing (if any, of course) if(this.current_item) { itemtype = this.current_item.type; if(itemtype == 'image') this.drawImage(); if(itemtype == 'text') this.drawText(); } }//end resizePlayer } PlayerHandler.instances = [];//instantiate array with playerhandler instances /** * On every window resize event sets a timeout to 500 miliseconds. When timeout is finally * passed (i.e. resizing stopped 500 miliseconds ago) calls resizePlayer on each playerhandler. */ $(window).resize(function() { if(this.resizeTO) clearTimeout(this.resizeTO); this.resizeTO = setTimeout(function() { $.each(PlayerHandler.instances, function(i, playerhandler) { playerhandler.resizePlayer(); }); }, 500); }); /** * Looks up the playerhandler object for the player we want to give * a new playlist. * @param playlist path to the playlist xml file * @param playerid id postfix of the player we want to reload */ function play_new_playlist(playlist, playerid) { phandler = $('#jp_container_'+ playerid).data('playerhandler'); phandler.stop(); // stop anything the player was playing before replacing the playlist. phandler.replacePlaylist(playlist) } /** * Builds the html for an html5 player * @param id String (no spaces allowed) to append to the id of the player, for unique players on the page * @param parent_container Jquery object to append this player html to */ function build_player_html(main_cont_id, id, parent_container, progressbar, controls, share, vote, info) { container = $('
').appendTo(parent_container); $('
').appendTo(container); big_body = $('
').appendTo(container); $('
').appendTo(big_body); gui_container = $('
').appendTo(big_body); $('
Er is nog geen menu voor u gemaakt
').appendTo(gui_container); gui_interface = $('
').appendTo(gui_container); if(progressbar) { progress_div = $('
').appendTo(gui_interface); seek_div = $('
').appendTo(progress_div); $('
').appendTo(seek_div); } if(controls) { controls_div = $('
').appendTo(gui_interface); left_controls_div = $('
').appendTo(controls_div); $('
').appendTo(left_controls_div); left_controls = $('
').appendTo(left_controls_div); if(controls.play) { $('').appendTo(left_controls); $('').appendTo(left_controls); } if(controls.stop) $('').appendTo(left_controls); if(controls.previous) $('').appendTo(left_controls); if(controls.next) $('').appendTo(left_controls); if(controls.fullscreen) { $('').appendTo(left_controls); $('').appendTo(left_controls); } if(controls.volume) { volume_controls = $('
').appendTo(left_controls_div); $('').appendTo(volume_controls); $('').appendTo(volume_controls); cur_vol_div = $('
').appendTo(volume_controls); vol_bar_div = $('
').appendTo(cur_vol_div); $('
').appendTo(vol_bar_div); $('').appendTo(volume_controls); } right_controls_div = $('
').appendTo(controls_div); $('
').appendTo(right_controls_div); if(controls.plugins && (share || info || vote) ) { plugin_controls = $('
').appendTo(right_controls_div); if(share) $('').appendTo(plugin_controls); if(vote)//todo: pass on object with type= and button= and do other types of voting interfaces $('').appendTo(plugin_controls); if(info) $('').appendTo(plugin_controls); } } no_solution = $('
').appendTo(big_body); $('Update Required').appendTo(no_solution); $('

To play the media you will need to either update your browser to a recent version or update your Flash plugin.

').appendTo(no_solution); return container; // return the initially made container }