1/* 2 * jPlayerInspector Plugin for jPlayer Plugin for jQuery JavaScript Library 3 * http://www.jplayer.org 4 * 5 * Copyright (c) 2009 - 2014 Happyworm Ltd 6 * Licensed under the MIT license. 7 * http://www.opensource.org/licenses/MIT 8 * 9 * Author: Mark J Panaghiston 10 * Version: 1.0.5 11 * Date: 1st April 2014 12 * 13 * For use with jPlayer Version: 2.6.0+ 14 * 15 * Note: Declare inspector instances after jPlayer instances. ie., Otherwise the jPlayer instance is nonsense. 16 */ 17 18(function($, undefined) { 19 $.jPlayerInspector = {}; 20 $.jPlayerInspector.i = 0; 21 $.jPlayerInspector.defaults = { 22 jPlayer: undefined, // The jQuery selector of the jPlayer instance to inspect. 23 idPrefix: "jplayer_inspector_", 24 visible: false 25 }; 26 27 var methods = { 28 init: function(options) { 29 var self = this; 30 var $this = $(this); 31 32 var config = $.extend({}, $.jPlayerInspector.defaults, options); 33 $(this).data("jPlayerInspector", config); 34 35 config.id = $(this).attr("id"); 36 config.jPlayerId = config.jPlayer.attr("id"); 37 38 config.windowId = config.idPrefix + "window_" + $.jPlayerInspector.i; 39 config.statusId = config.idPrefix + "status_" + $.jPlayerInspector.i; 40 config.configId = config.idPrefix + "config_" + $.jPlayerInspector.i; 41 config.toggleId = config.idPrefix + "toggle_" + $.jPlayerInspector.i; 42 config.eventResetId = config.idPrefix + "event_reset_" + $.jPlayerInspector.i; 43 config.updateId = config.idPrefix + "update_" + $.jPlayerInspector.i; 44 config.eventWindowId = config.idPrefix + "event_window_" + $.jPlayerInspector.i; 45 46 config.eventId = {}; 47 config.eventJq = {}; 48 config.eventTimeout = {}; 49 config.eventOccurrence = {}; 50 51 $.each($.jPlayer.event, function(eventName,eventType) { 52 config.eventId[eventType] = config.idPrefix + "event_" + eventName + "_" + $.jPlayerInspector.i; 53 config.eventOccurrence[eventType] = 0; 54 }); 55 56 var structure = 57 '<p><a href="#" id="' + config.toggleId + '">' + (config.visible ? "Hide" : "Show") + '</a> jPlayer Inspector</p>' 58 + '<div id="' + config.windowId + '">' 59 + '<div id="' + config.statusId + '"></div>' 60 + '<div id="' + config.eventWindowId + '" style="padding:5px 5px 0 5px;background-color:#eee;border:1px dotted #000;">' 61 + '<p style="margin:0 0 10px 0;"><strong>jPlayer events that have occurred over the past 1 second:</strong>' 62 + '<br />(Backgrounds: <span style="padding:0 5px;background-color:#eee;border:1px dotted #000;">Never occurred</span> <span style="padding:0 5px;background-color:#fff;border:1px dotted #000;">Occurred before</span> <span style="padding:0 5px;background-color:#9f9;border:1px dotted #000;">Occurred</span> <span style="padding:0 5px;background-color:#ff9;border:1px dotted #000;">Multiple occurrences</span> <a href="#" id="' + config.eventResetId + '">reset</a>)</p>'; 63 64 // MJP: Would use the next 3 lines for ease, but the events are just slapped on the page. 65 // $.each($.jPlayer.event, function(eventName,eventType) { 66 // structure += '<div id="' + config.eventId[eventType] + '" style="float:left;">' + eventName + '</div>'; 67 // }); 68 69 var eventStyle = "float:left;margin:0 5px 5px 0;padding:0 5px;border:1px dotted #000;"; 70 // MJP: Doing it longhand so order and layout easier to control. 71 structure += 72 '<div id="' + config.eventId[$.jPlayer.event.ready] + '" style="' + eventStyle + '"></div>' 73 + '<div id="' + config.eventId[$.jPlayer.event.setmedia] + '" style="' + eventStyle + '"></div>' 74 + '<div id="' + config.eventId[$.jPlayer.event.flashreset] + '" style="' + eventStyle + '"></div>' 75 + '<div id="' + config.eventId[$.jPlayer.event.resize] + '" style="' + eventStyle + '"></div>' 76 + '<div id="' + config.eventId[$.jPlayer.event.repeat] + '" style="' + eventStyle + '"></div>' 77 + '<div id="' + config.eventId[$.jPlayer.event.click] + '" style="' + eventStyle + '"></div>' 78 + '<div id="' + config.eventId[$.jPlayer.event.warning] + '" style="' + eventStyle + '"></div>' 79 80 + '<div id="' + config.eventId[$.jPlayer.event.loadstart] + '" style="clear:left;' + eventStyle + '"></div>' 81 + '<div id="' + config.eventId[$.jPlayer.event.progress] + '" style="' + eventStyle + '"></div>' 82 + '<div id="' + config.eventId[$.jPlayer.event.timeupdate] + '" style="' + eventStyle + '"></div>' 83 + '<div id="' + config.eventId[$.jPlayer.event.volumechange] + '" style="' + eventStyle + '"></div>' 84 + '<div id="' + config.eventId[$.jPlayer.event.error] + '" style="' + eventStyle + '"></div>' 85 86 + '<div id="' + config.eventId[$.jPlayer.event.play] + '" style="clear:left;' + eventStyle + '"></div>' 87 + '<div id="' + config.eventId[$.jPlayer.event.pause] + '" style="' + eventStyle + '"></div>' 88 + '<div id="' + config.eventId[$.jPlayer.event.waiting] + '" style="' + eventStyle + '"></div>' 89 + '<div id="' + config.eventId[$.jPlayer.event.playing] + '" style="' + eventStyle + '"></div>' 90 + '<div id="' + config.eventId[$.jPlayer.event.seeking] + '" style="' + eventStyle + '"></div>' 91 + '<div id="' + config.eventId[$.jPlayer.event.seeked] + '" style="' + eventStyle + '"></div>' 92 + '<div id="' + config.eventId[$.jPlayer.event.ended] + '" style="' + eventStyle + '"></div>' 93 94 + '<div id="' + config.eventId[$.jPlayer.event.loadeddata] + '" style="clear:left;' + eventStyle + '"></div>' 95 + '<div id="' + config.eventId[$.jPlayer.event.loadedmetadata] + '" style="' + eventStyle + '"></div>' 96 + '<div id="' + config.eventId[$.jPlayer.event.canplay] + '" style="' + eventStyle + '"></div>' 97 + '<div id="' + config.eventId[$.jPlayer.event.canplaythrough] + '" style="' + eventStyle + '"></div>' 98 99 + '<div id="' + config.eventId[$.jPlayer.event.suspend] + '" style="clear:left;' + eventStyle + '"></div>' 100 + '<div id="' + config.eventId[$.jPlayer.event.abort] + '" style="' + eventStyle + '"></div>' 101 + '<div id="' + config.eventId[$.jPlayer.event.emptied] + '" style="' + eventStyle + '"></div>' 102 + '<div id="' + config.eventId[$.jPlayer.event.stalled] + '" style="' + eventStyle + '"></div>' 103 + '<div id="' + config.eventId[$.jPlayer.event.ratechange] + '" style="' + eventStyle + '"></div>' 104 + '<div id="' + config.eventId[$.jPlayer.event.durationchange] + '" style="' + eventStyle + '"></div>' 105 106 + '<div style="clear:both"></div>'; 107 108 // MJP: Would like a check here in case we missed an event. 109 110 // MJP: Check fails, since it is not on the page yet. 111/* $.each($.jPlayer.event, function(eventName,eventType) { 112 if($("#" + config.eventId[eventType])[0] === undefined) { 113 structure += '<div id="' + config.eventId[eventType] + '" style="clear:left;' + eventStyle + '">' + eventName + '</div>'; 114 } 115 }); 116*/ 117 structure += 118 '</div>' 119 + '<p><a href="#" id="' + config.updateId + '">Update</a> jPlayer Inspector</p>' 120 + '<div id="' + config.configId + '"></div>' 121 + '</div>'; 122 $(this).html(structure); 123 124 config.windowJq = $("#" + config.windowId); 125 config.statusJq = $("#" + config.statusId); 126 config.configJq = $("#" + config.configId); 127 config.toggleJq = $("#" + config.toggleId); 128 config.eventResetJq = $("#" + config.eventResetId); 129 config.updateJq = $("#" + config.updateId); 130 131 $.each($.jPlayer.event, function(eventName,eventType) { 132 config.eventJq[eventType] = $("#" + config.eventId[eventType]); 133 config.eventJq[eventType].text(eventName + " (" + config.eventOccurrence[eventType] + ")"); // Sets the text to the event name and (0); 134 135 config.jPlayer.bind(eventType + ".jPlayerInspector", function(e) { 136 config.eventOccurrence[e.type]++; 137 if(config.eventOccurrence[e.type] > 1) { 138 config.eventJq[e.type].css("background-color","#ff9"); 139 } else { 140 config.eventJq[e.type].css("background-color","#9f9"); 141 } 142 config.eventJq[e.type].text(eventName + " (" + config.eventOccurrence[e.type] + ")"); 143 // The timer to handle the color 144 clearTimeout(config.eventTimeout[e.type]); 145 config.eventTimeout[e.type] = setTimeout(function() { 146 config.eventJq[e.type].css("background-color","#fff"); 147 }, 1000); 148 // The timer to handle the occurences. 149 setTimeout(function() { 150 config.eventOccurrence[e.type]--; 151 config.eventJq[e.type].text(eventName + " (" + config.eventOccurrence[e.type] + ")"); 152 }, 1000); 153 if(config.visible) { // Update the status, if inspector open. 154 $this.jPlayerInspector("updateStatus"); 155 } 156 }); 157 }); 158 159 config.jPlayer.bind($.jPlayer.event.ready + ".jPlayerInspector", function(e) { 160 $this.jPlayerInspector("updateConfig"); 161 }); 162 163 config.toggleJq.click(function() { 164 if(config.visible) { 165 $(this).text("Show"); 166 config.windowJq.hide(); 167 config.statusJq.empty(); 168 config.configJq.empty(); 169 } else { 170 $(this).text("Hide"); 171 config.windowJq.show(); 172 config.updateJq.click(); 173 } 174 config.visible = !config.visible; 175 $(this).blur(); 176 return false; 177 }); 178 179 config.eventResetJq.click(function() { 180 $.each($.jPlayer.event, function(eventName,eventType) { 181 config.eventJq[eventType].css("background-color","#eee"); 182 }); 183 $(this).blur(); 184 return false; 185 }); 186 187 config.updateJq.click(function() { 188 $this.jPlayerInspector("updateStatus"); 189 $this.jPlayerInspector("updateConfig"); 190 return false; 191 }); 192 193 if(!config.visible) { 194 config.windowJq.hide(); 195 } else { 196 // config.updateJq.click(); 197 } 198 199 $.jPlayerInspector.i++; 200 201 return this; 202 }, 203 destroy: function() { 204 $(this).data("jPlayerInspector") && $(this).data("jPlayerInspector").jPlayer.unbind(".jPlayerInspector"); 205 $(this).empty(); 206 }, 207 updateConfig: function() { // This displays information about jPlayer's configuration in inspector 208 209 var jPlayerInfo = "<p>This jPlayer instance is running in your browser where:<br />" 210 211 for(i = 0; i < $(this).data("jPlayerInspector").jPlayer.data("jPlayer").solutions.length; i++) { 212 var solution = $(this).data("jPlayerInspector").jPlayer.data("jPlayer").solutions[i]; 213 jPlayerInfo += " jPlayer's <strong>" + solution + "</strong> solution is"; 214 if($(this).data("jPlayerInspector").jPlayer.data("jPlayer")[solution].used) { 215 jPlayerInfo += " being <strong>used</strong> and will support:<strong>"; 216 for(format in $(this).data("jPlayerInspector").jPlayer.data("jPlayer")[solution].support) { 217 if($(this).data("jPlayerInspector").jPlayer.data("jPlayer")[solution].support[format]) { 218 jPlayerInfo += " " + format; 219 } 220 } 221 jPlayerInfo += "</strong><br />"; 222 } else { 223 jPlayerInfo += " <strong>not required</strong><br />"; 224 } 225 } 226 jPlayerInfo += "</p>"; 227 228 if($(this).data("jPlayerInspector").jPlayer.data("jPlayer").html.active) { 229 if($(this).data("jPlayerInspector").jPlayer.data("jPlayer").flash.active) { 230 jPlayerInfo += "<strong>Problem with jPlayer since both HTML5 and Flash are active.</strong>"; 231 } else { 232 jPlayerInfo += "The <strong>HTML5 is active</strong>."; 233 } 234 } else { 235 if($(this).data("jPlayerInspector").jPlayer.data("jPlayer").flash.active) { 236 jPlayerInfo += "The <strong>Flash is active</strong>."; 237 } else { 238 jPlayerInfo += "No solution is currently active. jPlayer needs a setMedia()."; 239 } 240 } 241 jPlayerInfo += "</p>"; 242 243 var formatType = $(this).data("jPlayerInspector").jPlayer.data("jPlayer").status.formatType; 244 jPlayerInfo += "<p><code>status.formatType = '" + formatType + "'</code><br />"; 245 if(formatType) { 246 jPlayerInfo += "<code>Browser canPlay('" + $.jPlayer.prototype.format[formatType].codec + "')</code>"; 247 } else { 248 jPlayerInfo += "</p>"; 249 } 250 251 jPlayerInfo += "<p><code>status.src = '" + $(this).data("jPlayerInspector").jPlayer.data("jPlayer").status.src + "'</code></p>"; 252 253 jPlayerInfo += "<p><code>status.media = {<br />"; 254 for(prop in $(this).data("jPlayerInspector").jPlayer.data("jPlayer").status.media) { 255 jPlayerInfo += " " + prop + ": " + $(this).data("jPlayerInspector").jPlayer.data("jPlayer").status.media[prop] + "<br />"; // Some are strings 256 } 257 jPlayerInfo += "};</code></p>" 258 259 jPlayerInfo += "<p>"; 260 jPlayerInfo += "<code>status.videoWidth = '" + $(this).data("jPlayerInspector").jPlayer.data("jPlayer").status.videoWidth + "'</code>"; 261 jPlayerInfo += " | <code>status.videoHeight = '" + $(this).data("jPlayerInspector").jPlayer.data("jPlayer").status.videoHeight + "'</code>"; 262 jPlayerInfo += "<br /><code>status.width = '" + $(this).data("jPlayerInspector").jPlayer.data("jPlayer").status.width + "'</code>"; 263 jPlayerInfo += " | <code>status.height = '" + $(this).data("jPlayerInspector").jPlayer.data("jPlayer").status.height + "'</code>"; 264 jPlayerInfo += "</p>"; 265 266 + "<p>Raw browser test for HTML5 support. Should equal a function if HTML5 is available.<br />"; 267 if($(this).data("jPlayerInspector").jPlayer.data("jPlayer").html.audio.available) { 268 jPlayerInfo += "<code>htmlElement.audio.canPlayType = " + (typeof $(this).data("jPlayerInspector").jPlayer.data("jPlayer").htmlElement.audio.canPlayType) +"</code><br />" 269 } 270 if($(this).data("jPlayerInspector").jPlayer.data("jPlayer").html.video.available) { 271 jPlayerInfo += "<code>htmlElement.video.canPlayType = " + (typeof $(this).data("jPlayerInspector").jPlayer.data("jPlayer").htmlElement.video.canPlayType) +"</code>"; 272 } 273 jPlayerInfo += "</p>"; 274 275 jPlayerInfo += "<p>This instance is using the constructor options:<br />" 276 + "<code>$('#" + $(this).data("jPlayerInspector").jPlayer.data("jPlayer").internal.self.id + "').jPlayer({<br />" 277 278 + " swfPath: '" + $(this).data("jPlayerInspector").jPlayer.jPlayer("option", "swfPath") + "',<br />" 279 280 + " solution: '" + $(this).data("jPlayerInspector").jPlayer.jPlayer("option", "solution") + "',<br />" 281 282 + " supplied: '" + $(this).data("jPlayerInspector").jPlayer.jPlayer("option", "supplied") + "',<br />" 283 284 + " preload: '" + $(this).data("jPlayerInspector").jPlayer.jPlayer("option", "preload") + "',<br />" 285 286 + " volume: " + $(this).data("jPlayerInspector").jPlayer.jPlayer("option", "volume") + ",<br />" 287 288 + " muted: " + $(this).data("jPlayerInspector").jPlayer.jPlayer("option", "muted") + ",<br />" 289 290 + " backgroundColor: '" + $(this).data("jPlayerInspector").jPlayer.jPlayer("option", "backgroundColor") + "',<br />" 291 292 + " cssSelectorAncestor: '" + $(this).data("jPlayerInspector").jPlayer.jPlayer("option", "cssSelectorAncestor") + "',<br />" 293 294 + " cssSelector: {"; 295 296 var cssSelector = $(this).data("jPlayerInspector").jPlayer.jPlayer("option", "cssSelector"); 297 for(prop in cssSelector) { 298 299 // jPlayerInfo += "<br /> " + prop + ": '" + cssSelector[prop] + "'," // This works too of course, but want to use option method for deep keys. 300 jPlayerInfo += "<br /> " + prop + ": '" + $(this).data("jPlayerInspector").jPlayer.jPlayer("option", "cssSelector." + prop) + "'," 301 } 302 303 jPlayerInfo = jPlayerInfo.slice(0, -1); // Because the sloppy comma was bugging me. 304 305 jPlayerInfo += "<br /> },<br />" 306 307 + " errorAlerts: " + $(this).data("jPlayerInspector").jPlayer.jPlayer("option", "errorAlerts") + ",<br />" 308 309 + " warningAlerts: " + $(this).data("jPlayerInspector").jPlayer.jPlayer("option", "warningAlerts") + "<br />" 310 311 + "});</code></p>"; 312 $(this).data("jPlayerInspector").configJq.html(jPlayerInfo); 313 return this; 314 }, 315 updateStatus: function() { // This displays information about jPlayer's status in the inspector 316 $(this).data("jPlayerInspector").statusJq.html( 317 "<p>jPlayer is " + 318 ($(this).data("jPlayerInspector").jPlayer.data("jPlayer").status.paused ? "paused" : "playing") + 319 " at time: " + Math.floor($(this).data("jPlayerInspector").jPlayer.data("jPlayer").status.currentTime*10)/10 + "s." + 320 " (d: " + Math.floor($(this).data("jPlayerInspector").jPlayer.data("jPlayer").status.duration*10)/10 + "s" + 321 ", sp: " + Math.floor($(this).data("jPlayerInspector").jPlayer.data("jPlayer").status.seekPercent) + "%" + 322 ", cpr: " + Math.floor($(this).data("jPlayerInspector").jPlayer.data("jPlayer").status.currentPercentRelative) + "%" + 323 ", cpa: " + Math.floor($(this).data("jPlayerInspector").jPlayer.data("jPlayer").status.currentPercentAbsolute) + "%)</p>" 324 ); 325 return this; 326 } 327 }; 328 $.fn.jPlayerInspector = function( method ) { 329 // Method calling logic 330 if ( methods[method] ) { 331 return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 )); 332 } else if ( typeof method === 'object' || ! method ) { 333 return methods.init.apply( this, arguments ); 334 } else { 335 $.error( 'Method ' + method + ' does not exist on jQuery.jPlayerInspector' ); 336 } 337 }; 338})(jQuery); 339