1 /** The minplayer namespace. */ 2 minplayer = minplayer || {}; 3 4 /** Static array to keep track of all plugins. */ 5 minplayer.plugins = minplayer.plugins || {}; 6 7 /** Static array to keep track of queues. */ 8 minplayer.queue = minplayer.queue || []; 9 10 /** Mutex lock to keep multiple triggers from occuring. */ 11 minplayer.lock = false; 12 13 /** 14 * @constructor 15 * @class The base class for all plugins. 16 * 17 * @param {string} name The name of this plugin. 18 * @param {object} context The jQuery context. 19 * @param {object} options This components options. 20 */ 21 minplayer.plugin = function(name, context, options) { 22 23 /** The name of this plugin. */ 24 this.name = name; 25 26 /** The ready flag. */ 27 this.pluginReady = false; 28 29 /** The options for this plugin. */ 30 this.options = options; 31 32 /** The event queue. */ 33 this.queue = {}; 34 35 /** Keep track of already triggered events. */ 36 this.triggered = {}; 37 38 /** Create a queue lock. */ 39 this.lock = false; 40 41 // Only call the constructor if we have a context. 42 if (context) { 43 44 // Construct this plugin. 45 this.construct(); 46 } 47 }; 48 49 /** 50 * The constructor which is called once the context is set. 51 * Any class deriving from the plugin class should place all context 52 * dependant functionality within this function instead of the standard 53 * constructor function since it is called on object derivation as well 54 * as object creation. 55 */ 56 minplayer.plugin.prototype.construct = function() { 57 58 // Adds this as a plugin. 59 this.addPlugin(); 60 }; 61 62 /** 63 * Destructor. 64 */ 65 minplayer.plugin.prototype.destroy = function() { 66 67 // Unbind all events. 68 this.unbind(); 69 }; 70 71 /** 72 * Loads all of the available plugins. 73 */ 74 minplayer.plugin.prototype.loadPlugins = function() { 75 76 // Get all the plugins to load. 77 var instance = ''; 78 79 // Iterate through all the plugins. 80 for (var name in this.options.plugins) { 81 82 // Only load if it does not already exist. 83 if (!minplayer.plugins[this.options.id][name]) { 84 85 // Get the instance name from the setting. 86 instance = this.options.plugins[name]; 87 88 // If this object exists. 89 if (minplayer[name][instance]) { 90 91 // Declare a new object. 92 new minplayer[name][instance](this.display, this.options); 93 } 94 } 95 } 96 }; 97 98 /** 99 * Plugins should call this method when they are ready. 100 */ 101 minplayer.plugin.prototype.ready = function() { 102 103 // Keep this plugin from triggering multiple ready events. 104 if (!this.pluginReady) { 105 106 // Set the ready flag. 107 this.pluginReady = true; 108 109 // Now trigger that I am ready. 110 this.trigger('ready'); 111 112 // Check the queue. 113 this.checkQueue(); 114 } 115 }; 116 117 /** 118 * Adds a new plugin to this player. 119 * 120 * @param {string} name The name of this plugin. 121 * @param {object} plugin A new plugin object, derived from media.plugin. 122 */ 123 minplayer.plugin.prototype.addPlugin = function(name, plugin) { 124 name = name || this.name; 125 plugin = plugin || this; 126 127 // Make sure the plugin is valid. 128 if (plugin.isValid()) { 129 130 // If the plugins for this instance do not exist. 131 if (!minplayer.plugins[this.options.id]) { 132 133 // Initialize the plugins. 134 minplayer.plugins[this.options.id] = {}; 135 } 136 137 // Add this plugin. 138 minplayer.plugins[this.options.id][name] = plugin; 139 } 140 }; 141 142 /** 143 * Gets a plugin by name and calls callback when it is ready. 144 * 145 * @param {string} plugin The plugin of the plugin. 146 * @param {function} callback Called when the plugin is ready. 147 * @return {object} The plugin if no callback is provided. 148 */ 149 minplayer.plugin.prototype.get = function(plugin, callback) { 150 151 // If they pass just a callback, then return all plugins when ready. 152 if (typeof plugin === 'function') { 153 callback = plugin; 154 plugin = null; 155 } 156 157 // Return the minplayer.get equivalent. 158 return minplayer.get.call(this, this.options.id, plugin, callback); 159 }; 160 161 /** 162 * Check the queue and execute it. 163 */ 164 minplayer.plugin.prototype.checkQueue = function() { 165 166 // Initialize our variables. 167 var q = null, i = 0, check = false, newqueue = []; 168 169 // Set the lock. 170 minplayer.lock = true; 171 172 // Iterate through all the queues. 173 var length = minplayer.queue.length; 174 for (i = 0; i < length; i++) { 175 176 // Get the queue. 177 q = minplayer.queue[i]; 178 179 // Now check to see if this queue is about us. 180 check = !q.id && !q.plugin; 181 check |= (q.plugin == this.name) && (!q.id || (q.id == this.options.id)); 182 183 // If the check passes... 184 if (check) { 185 check = minplayer.bind.call( 186 q.context, 187 q.event, 188 this.options.id, 189 this.name, 190 q.callback 191 ); 192 } 193 194 // Add the queue back if it doesn't check out. 195 if (!check) { 196 197 // Add this back to the queue. 198 newqueue.push(q); 199 } 200 } 201 202 // Set the old queue to the new queue. 203 minplayer.queue = newqueue; 204 205 // Release the lock. 206 minplayer.lock = false; 207 }; 208 209 /** 210 * Trigger a media event. 211 * 212 * @param {string} type The event type. 213 * @param {object} data The event data object. 214 * @return {object} The plugin object. 215 */ 216 minplayer.plugin.prototype.trigger = function(type, data) { 217 data = data || {}; 218 data.plugin = this; 219 220 // Add this to our triggered array. 221 this.triggered[type] = data; 222 223 // Check to make sure the queue for this type exists. 224 if (this.queue[type]) { 225 226 var i = 0, queue = {}; 227 228 // Iterate through all the callbacks in this queue. 229 for (i in this.queue[type]) { 230 231 // Setup the event object, and call the callback. 232 queue = this.queue[type][i]; 233 queue.callback({target: this, data: queue.data}, data); 234 } 235 } 236 237 // Return the plugin object. 238 return this; 239 }; 240 241 /** 242 * Bind to a media event. 243 * 244 * @param {string} type The event type. 245 * @param {object} data The data to bind with the event. 246 * @param {function} fn The callback function. 247 * @return {object} The plugin object. 248 **/ 249 minplayer.plugin.prototype.bind = function(type, data, fn) { 250 251 // Allow the data to be the callback. 252 if (typeof data === 'function') { 253 fn = data; 254 data = null; 255 } 256 257 // You must bind to a specific event and have a callback. 258 if (!type || !fn) { 259 return; 260 } 261 262 // Initialize the queue for this type. 263 this.queue[type] = this.queue[type] || []; 264 265 // Unbind any existing equivalent events. 266 this.unbind(type, fn); 267 268 // Now add this event to the queue. 269 this.queue[type].push({ 270 callback: fn, 271 data: data 272 }); 273 274 // Now see if this event has already been triggered. 275 if (this.triggered[type]) { 276 277 // Go ahead and trigger the event. 278 fn({target: this, data: data}, this.triggered[type]); 279 } 280 281 // Return the plugin. 282 return this; 283 }; 284 285 /** 286 * Unbind a media event. 287 * 288 * @param {string} type The event type. 289 * @param {function} fn The callback function. 290 * @return {object} The plugin object. 291 **/ 292 minplayer.plugin.prototype.unbind = function(type, fn) { 293 294 // If this is locked then try again after 10ms. 295 if (this.lock) { 296 var _this = this; 297 setTimeout(function() { 298 _this.unbind(type, fn); 299 }, 10); 300 } 301 302 // Set the lock. 303 this.lock = true; 304 305 if (!type) { 306 this.queue = {}; 307 } 308 else if (!fn) { 309 this.queue[type] = []; 310 } 311 else { 312 // Iterate through all the callbacks and search for equal callbacks. 313 var i = 0, queue = {}; 314 for (i in this.queue[type]) { 315 if (this.queue[type][i].callback === fn) { 316 queue = this.queue[type].splice(1, 1); 317 delete queue; 318 } 319 } 320 } 321 322 // Reset the lock. 323 this.lock = false; 324 325 // Return the plugin. 326 return this; 327 }; 328 329 /** 330 * Adds an item to the queue. 331 * 332 * @param {object} context The context which this is called within. 333 * @param {string} event The event to trigger on. 334 * @param {string} id The player ID. 335 * @param {string} plugin The name of the plugin. 336 * @param {function} callback Called when the event occurs. 337 */ 338 minplayer.addQueue = function(context, event, id, plugin, callback) { 339 340 // See if it is locked... 341 if (!minplayer.lock) { 342 minplayer.queue.push({ 343 context: context, 344 id: id, 345 event: event, 346 plugin: plugin, 347 callback: callback 348 }); 349 } 350 else { 351 352 // If so, then try again after 10 milliseconds. 353 setTimeout(function() { 354 minplayer.addQueue(context, id, event, plugin, callback); 355 }, 10); 356 } 357 }; 358 359 /** 360 * Binds an event to a plugin instance, and if it doesn't exist, then caches 361 * it for a later time. 362 * 363 * @param {string} event The event to trigger on. 364 * @param {string} id The player ID. 365 * @param {string} plugin The name of the plugin. 366 * @param {function} callback Called when the event occurs. 367 * @return {boolean} If the bind was successful. 368 * @this The object in context who called this method. 369 */ 370 minplayer.bind = function(event, id, plugin, callback) { 371 372 // If no callback exists, then just return false. 373 if (!callback) { 374 return false; 375 } 376 377 // Get the plugins. 378 var inst = minplayer.plugins; 379 380 // See if this plugin exists. 381 if (inst[id][plugin]) { 382 383 // If so, then bind the event to this plugin. 384 inst[id][plugin].bind(event, {context: this}, function(event, data) { 385 callback.call(event.data.context, data.plugin); 386 }); 387 return true; 388 } 389 390 // If not, then add it to the queue to bind later. 391 minplayer.addQueue(this, event, id, plugin, callback); 392 393 // Return that this wasn't handled. 394 return false; 395 }; 396 397 /** 398 * The main API for minPlayer. 399 * 400 * Provided that this function takes three parameters, there are 8 different 401 * ways to use this api. 402 * 403 * id (0x100) - You want a specific player. 404 * plugin (0x010) - You want a specific plugin. 405 * callback (0x001) - You only want it when it is ready. 406 * 407 * 000 - You want all plugins from all players, ready or not. 408 * 409 * var plugins = minplayer.get(); 410 * 411 * 001 - You want all plugins from all players, but only when ready. 412 * 413 * minplayer.get(function(plugin) { 414 * // Code goes here. 415 * }); 416 * 417 * 010 - You want a specific plugin from all players, ready or not... 418 * 419 * var medias = minplayer.get(null, 'media'); 420 * 421 * 011 - You want a specific plugin from all players, but only when ready. 422 * 423 * minplayer.get('player', function(player) { 424 * // Code goes here. 425 * }); 426 * 427 * 100 - You want all plugins from a specific player, ready or not. 428 * 429 * var plugins = minplayer.get('player_id'); 430 * 431 * 101 - You want all plugins from a specific player, but only when ready. 432 * 433 * minplayer.get('player_id', null, function(plugin) { 434 * // Code goes here. 435 * }); 436 * 437 * 110 - You want a specific plugin from a specific player, ready or not. 438 * 439 * var plugin = minplayer.get('player_id', 'media'); 440 * 441 * 111 - You want a specific plugin from a specific player, only when ready. 442 * 443 * minplayer.get('player_id', 'media', function(media) { 444 * // Code goes here. 445 * }); 446 * 447 * @this The context in which this function was called. 448 * @param {string} id The ID of the widget to get the plugins from. 449 * @param {string} plugin The name of the plugin. 450 * @param {function} callback Called when the plugin is ready. 451 * @return {object} The plugin object if it is immediately available. 452 */ 453 minplayer.get = function(id, plugin, callback) { 454 455 // Normalize the arguments for a better interface. 456 if (typeof id === 'function') { 457 callback = id; 458 plugin = id = null; 459 } 460 461 if (typeof plugin === 'function') { 462 callback = plugin; 463 plugin = id; 464 id = null; 465 } 466 467 // Make sure the callback is a callback. 468 callback = (typeof callback === 'function') ? callback : null; 469 470 // Get the plugins. 471 var plugins = minplayer.plugins; 472 473 // 0x000 474 if (!id && !plugin && !callback) { 475 return plugins; 476 } 477 // 0x100 478 else if (id && !plugin && !callback) { 479 return plugins[id]; 480 } 481 // 0x110 482 else if (id && plugin && !callback) { 483 return plugins[id][plugin]; 484 } 485 // 0x111 486 else if (id && plugin && callback) { 487 minplayer.bind.call(this, 'ready', id, plugin, callback); 488 } 489 // 0x011 490 else if (!id && plugin && callback) { 491 for (var id in plugins) { 492 minplayer.bind.call(this, 'ready', id, plugin, callback); 493 } 494 } 495 // 0x101 496 else if (id && !plugin && callback) { 497 for (var plugin in plugins[id]) { 498 minplayer.bind.call(this, 'ready', id, plugin, callback); 499 } 500 } 501 // 0x010 502 else if (!id && plugin && !callback) { 503 var plugin_types = {}; 504 for (var id in plugins) { 505 if (plugins.hasOwnProperty(id) && plugins[id].hasOwnProperty(plugin)) { 506 plugin_types[id] = plugins[id][plugin]; 507 } 508 } 509 return plugin_types; 510 } 511 // 0x001 512 else { 513 for (var id in plugins) { 514 for (var plugin in plugins[id]) { 515 minplayer.bind.call(this, 'ready', id, plugin, callback); 516 } 517 } 518 } 519 }; 520