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 * @param {object} queue The event queue to pass events around. 21 */ 22 minplayer.plugin = function(name, context, options, queue) { 23 24 /** The name of this plugin. */ 25 this.name = name; 26 27 /** The ready flag. */ 28 this.pluginReady = false; 29 30 /** The options for this plugin. */ 31 this.options = options || {}; 32 33 /** The event queue. */ 34 this.queue = queue || {}; 35 36 /** Keep track of already triggered events. */ 37 this.triggered = {}; 38 39 /** Create a queue lock. */ 40 this.lock = false; 41 42 // Only call the constructor if we have a context. 43 if (context) { 44 45 /** Say that we are active. */ 46 this.active = true; 47 48 /** Keep track of the context. */ 49 this.context = jQuery(context); 50 51 // Construct this plugin. 52 this.construct(); 53 } 54 }; 55 56 /** 57 * The constructor which is called once the context is set. 58 * Any class deriving from the plugin class should place all context 59 * dependant functionality within this function instead of the standard 60 * constructor function since it is called on object derivation as well 61 * as object creation. 62 */ 63 minplayer.plugin.prototype.construct = function() { 64 65 // Adds this as a plugin. 66 this.addPlugin(); 67 }; 68 69 /** 70 * Destructor. 71 */ 72 minplayer.plugin.prototype.destroy = function() { 73 74 // Unbind all events. 75 this.active = false; 76 this.unbind(); 77 }; 78 79 /** 80 * Creates a new plugin within this context. 81 * 82 * @param {string} name The name of the plugin you wish to create. 83 * @param {object} base The base object for this plugin. 84 * @param {object} context The context which you would like to create. 85 * @return {object} The new plugin object. 86 */ 87 minplayer.plugin.prototype.create = function(name, base, context) { 88 var plugin = null; 89 90 // Make sure we have a base object. 91 base = base || 'minplayer'; 92 if (!window[base][name]) { 93 base = 'minplayer'; 94 } 95 96 // Make sure there is a context. 97 context = context || this.display; 98 99 // See if this plugin exists within this object. 100 if (window[base][name]) { 101 102 // Set the plugin. 103 plugin = window[base][name]; 104 105 // See if a template version of the plugin exists. 106 if (plugin[this.options.template]) { 107 108 plugin = plugin[this.options.template]; 109 } 110 111 // Make sure the plugin is a function. 112 if (typeof plugin !== 'function') { 113 plugin = window['minplayer'][name]; 114 } 115 116 // Make sure it is a function. 117 if (typeof plugin === 'function') { 118 return new plugin(context, this.options); 119 } 120 } 121 122 return null; 123 }; 124 125 /** 126 * Plugins should call this method when they are ready. 127 */ 128 minplayer.plugin.prototype.ready = function() { 129 130 // Keep this plugin from triggering multiple ready events. 131 if (!this.pluginReady) { 132 133 // Set the ready flag. 134 this.pluginReady = true; 135 136 // Now trigger that I am ready. 137 this.trigger('ready'); 138 139 // Check the queue. 140 this.checkQueue(); 141 } 142 }; 143 144 /** 145 * Returns if this component is valid. 146 * 147 * @return {boolean} TRUE if the plugin display is valid. 148 */ 149 minplayer.plugin.prototype.isValid = function() { 150 return !!this.options.id && this.active; 151 }; 152 153 /** 154 * Adds a new plugin to this player. 155 * 156 * @param {string} name The name of this plugin. 157 * @param {object} plugin A new plugin object, derived from media.plugin. 158 */ 159 minplayer.plugin.prototype.addPlugin = function(name, plugin) { 160 name = name || this.name; 161 plugin = plugin || this; 162 163 // Make sure the plugin is valid. 164 if (plugin.isValid()) { 165 166 // If the plugins for this instance do not exist. 167 if (!minplayer.plugins[this.options.id]) { 168 169 // Initialize the plugins. 170 minplayer.plugins[this.options.id] = {}; 171 } 172 173 if (!minplayer.plugins[this.options.id][name]) { 174 175 // Add the plugins array. 176 minplayer.plugins[this.options.id][name] = []; 177 } 178 179 // Add this plugin. 180 minplayer.plugins[this.options.id][name].push(plugin); 181 182 // Now check the queue for this plugin. 183 this.checkQueue(plugin); 184 } 185 }; 186 187 /** 188 * Create a polling timer. 189 * 190 * @param {function} callback The function to call when you poll. 191 * @param {integer} interval The interval you would like to poll. 192 */ 193 minplayer.plugin.prototype.poll = function(callback, interval) { 194 setTimeout((function(context) { 195 return function callLater() { 196 if (callback.call(context)) { 197 setTimeout(callLater, interval); 198 } 199 }; 200 })(this), interval); 201 }; 202 203 /** 204 * Gets a plugin by name and calls callback when it is ready. 205 * 206 * @param {string} plugin The plugin of the plugin. 207 * @param {function} callback Called when the plugin is ready. 208 * @return {object} The plugin if no callback is provided. 209 */ 210 minplayer.plugin.prototype.get = function(plugin, callback) { 211 212 // If they pass just a callback, then return all plugins when ready. 213 if (typeof plugin === 'function') { 214 callback = plugin; 215 plugin = null; 216 } 217 218 // Return the minplayer.get equivalent. 219 return minplayer.get.call(this, this.options.id, plugin, callback); 220 }; 221 222 /** 223 * Check the queue and execute it. 224 * 225 * @param {object} plugin The plugin object to check the queue against. 226 */ 227 minplayer.plugin.prototype.checkQueue = function(plugin) { 228 229 // Initialize our variables. 230 var q = null, i = 0, check = false, newqueue = []; 231 232 // Normalize the plugin variable. 233 plugin = plugin || this; 234 235 // Set the lock. 236 minplayer.lock = true; 237 238 // Iterate through all the queues. 239 var length = minplayer.queue.length; 240 for (i = 0; i < length; i++) { 241 if (minplayer.queue.hasOwnProperty(i)) { 242 // Get the queue. 243 q = minplayer.queue[i]; 244 245 // Now check to see if this queue is about us. 246 check = !q.id && !q.plugin; 247 check |= (q.plugin == plugin.name); 248 check &= (!q.id || (q.id == this.options.id)); 249 250 // If the check passes... 251 if (check) { 252 check = minplayer.bind.call( 253 q.context, 254 q.event, 255 this.options.id, 256 plugin.name, 257 q.callback, 258 true 259 ); 260 } 261 262 // Add the queue back if it doesn't check out. 263 if (!check) { 264 265 // Add this back to the queue. 266 newqueue.push(q); 267 } 268 } 269 } 270 271 // Set the old queue to the new queue. 272 minplayer.queue = newqueue; 273 274 // Release the lock. 275 minplayer.lock = false; 276 }; 277 278 /** 279 * Trigger a media event. 280 * 281 * @param {string} type The event type. 282 * @param {object} data The event data object. 283 * @return {object} The plugin object. 284 */ 285 minplayer.plugin.prototype.trigger = function(type, data) { 286 287 // Don't trigger if this plugin is inactive. 288 if (!this.active) { 289 return this; 290 } 291 292 // Add this to our triggered array. 293 this.triggered[type] = data; 294 295 // Check to make sure the queue for this type exists. 296 if (this.queue.hasOwnProperty(type)) { 297 298 var i = 0, queue = {}, queuetype = this.queue[type]; 299 300 // Iterate through all the callbacks in this queue. 301 for (i in queuetype) { 302 303 // Check to make sure the queue index exists. 304 if (queuetype.hasOwnProperty(i)) { 305 306 // Setup the event object, and call the callback. 307 queue = queuetype[i]; 308 queue.callback({target: this, data: queue.data}, data); 309 } 310 } 311 } 312 313 // Return the plugin object. 314 return this; 315 }; 316 317 /** 318 * Bind to a media event. 319 * 320 * @param {string} type The event type. 321 * @param {object} data The data to bind with the event. 322 * @param {function} fn The callback function. 323 * @return {object} The plugin object. 324 **/ 325 minplayer.plugin.prototype.bind = function(type, data, fn) { 326 327 // Only bind if active. 328 if (!this.active) { 329 return this; 330 } 331 332 // Allow the data to be the callback. 333 if (typeof data === 'function') { 334 fn = data; 335 data = null; 336 } 337 338 // You must bind to a specific event and have a callback. 339 if (!type || !fn) { 340 return; 341 } 342 343 // Initialize the queue for this type. 344 this.queue[type] = this.queue[type] || []; 345 346 // Unbind any existing equivalent events. 347 this.unbind(type, fn); 348 349 // Now add this event to the queue. 350 this.queue[type].push({ 351 callback: fn, 352 data: data 353 }); 354 355 // Now see if this event has already been triggered. 356 if (this.triggered.hasOwnProperty(type)) { 357 358 // Go ahead and trigger the event. 359 fn({target: this, data: data}, this.triggered[type]); 360 } 361 362 // Return the plugin. 363 return this; 364 }; 365 366 /** 367 * Unbind a media event. 368 * 369 * @param {string} type The event type. 370 * @param {function} fn The callback function. 371 * @return {object} The plugin object. 372 **/ 373 minplayer.plugin.prototype.unbind = function(type, fn) { 374 375 // If this is locked then try again after 10ms. 376 if (this.lock) { 377 setTimeout((function(plugin) { 378 return function() { 379 plugin.unbind(type, fn); 380 }; 381 })(this), 10); 382 } 383 384 // Set the lock. 385 this.lock = true; 386 387 // Get the queue type. 388 var queuetype = this.queue.hasOwnProperty(type) ? this.queue[type] : null; 389 390 if (!type) { 391 this.queue = {}; 392 } 393 else if (!fn) { 394 this.queue[type] = []; 395 } 396 else if (queuetype) { 397 // Iterate through all the callbacks and search for equal callbacks. 398 var i = 0, queue = {}; 399 for (i in queuetype) { 400 if (queuetype.hasOwnProperty(i)) { 401 if (queuetype[i].callback === fn) { 402 queue = this.queue[type].splice(i, 1); 403 delete queue; 404 } 405 } 406 } 407 } 408 409 // Reset the lock. 410 this.lock = false; 411 412 // Return the plugin. 413 return this; 414 }; 415 416 /** 417 * Adds an item to the queue. 418 * 419 * @param {object} context The context which this is called within. 420 * @param {string} event The event to trigger on. 421 * @param {string} id The player ID. 422 * @param {string} plugin The name of the plugin. 423 * @param {function} callback Called when the event occurs. 424 */ 425 minplayer.addQueue = function(context, event, id, plugin, callback) { 426 427 // See if it is locked... 428 if (!minplayer.lock) { 429 minplayer.queue.push({ 430 context: context, 431 id: id, 432 event: event, 433 plugin: plugin, 434 callback: callback 435 }); 436 } 437 else { 438 439 // If so, then try again after 10 milliseconds. 440 setTimeout(function() { 441 minplayer.addQueue(context, id, event, plugin, callback); 442 }, 10); 443 } 444 }; 445 446 /** 447 * Binds an event to a plugin instance, and if it doesn't exist, then caches 448 * it for a later time. 449 * 450 * @param {string} event The event to trigger on. 451 * @param {string} id The player ID. 452 * @param {string} plugin The name of the plugin. 453 * @param {function} callback Called when the event occurs. 454 * @param {boolean} fromCheck If this is from a checkqueue. 455 * @return {boolean} If the bind was successful. 456 * @this The object in context who called this method. 457 */ 458 minplayer.bind = function(event, id, plugin, callback, fromCheck) { 459 460 // If no callback exists, then just return false. 461 if (!callback) { 462 return false; 463 } 464 465 // Get the plugins. 466 var plugins = minplayer.plugins; 467 468 // Determine the selected plugins. 469 var selected = []; 470 471 // Create a quick add. 472 var addSelected = function(id, plugin) { 473 if (plugins.hasOwnProperty(id) && plugins[id].hasOwnProperty(plugin)) { 474 var i = plugins[id][plugin].length; 475 while (i--) { 476 selected.push(plugins[id][plugin][i]); 477 } 478 } 479 }; 480 481 // If they provide id && plugin 482 if (id && plugin) { 483 addSelected(id, plugin); 484 } 485 486 // If they provide no id but a plugin. 487 else if (!id && plugin) { 488 for (var id in plugins) { 489 addSelected(id, plugin); 490 } 491 } 492 493 // If they provide an id but no plugin. 494 else if (id && !plugin && plugins[id]) { 495 for (var plugin in plugins[id]) { 496 addSelected(id, plugin); 497 } 498 } 499 500 // If they provide niether an id or a plugin. 501 else if (!id && !plugin) { 502 for (var id in plugins) { 503 for (var plugin in plugins[id]) { 504 addSelected(id, plugin); 505 } 506 } 507 } 508 509 // Iterate through the selected plugins and bind. 510 var i = selected.length; 511 while (i--) { 512 selected[i].bind(event, (function(context) { 513 return function(event) { 514 callback.call(context, event.target); 515 }; 516 })(this)); 517 } 518 519 // Add it to the queue for post bindings... 520 if ((selected.length == 0) && !fromCheck) { 521 minplayer.addQueue(this, event, id, plugin, callback); 522 } 523 524 // Return that this wasn't handled. 525 return (selected.length > 0); 526 }; 527 528 /** 529 * The main API for minPlayer. 530 * 531 * Provided that this function takes three parameters, there are 8 different 532 * ways to use this api. 533 * 534 * id (0x100) - You want a specific player. 535 * plugin (0x010) - You want a specific plugin. 536 * callback (0x001) - You only want it when it is ready. 537 * 538 * 000 - You want all plugins from all players, ready or not. 539 * 540 * var plugins = minplayer.get(); 541 * 542 * 001 - You want all plugins from all players, but only when ready. 543 * 544 * minplayer.get(function(plugin) { 545 * // Code goes here. 546 * }); 547 * 548 * 010 - You want a specific plugin from all players, ready or not... 549 * 550 * var medias = minplayer.get(null, 'media'); 551 * 552 * 011 - You want a specific plugin from all players, but only when ready. 553 * 554 * minplayer.get('player', function(player) { 555 * // Code goes here. 556 * }); 557 * 558 * 100 - You want all plugins from a specific player, ready or not. 559 * 560 * var plugins = minplayer.get('player_id'); 561 * 562 * 101 - You want all plugins from a specific player, but only when ready. 563 * 564 * minplayer.get('player_id', null, function(plugin) { 565 * // Code goes here. 566 * }); 567 * 568 * 110 - You want a specific plugin from a specific player, ready or not. 569 * 570 * var plugin = minplayer.get('player_id', 'media'); 571 * 572 * 111 - You want a specific plugin from a specific player, only when ready. 573 * 574 * minplayer.get('player_id', 'media', function(media) { 575 * // Code goes here. 576 * }); 577 * 578 * @this The context in which this function was called. 579 * @param {string} id The ID of the widget to get the plugins from. 580 * @param {string} plugin The name of the plugin. 581 * @param {function} callback Called when the plugin is ready. 582 * @return {object} The plugin object if it is immediately available. 583 */ 584 minplayer.get = function(id, plugin, callback) { 585 586 // Normalize the arguments for a better interface. 587 if (typeof id === 'function') { 588 callback = id; 589 plugin = id = null; 590 } 591 592 if (typeof plugin === 'function') { 593 callback = plugin; 594 plugin = id; 595 id = null; 596 } 597 598 // Make sure the callback is a callback. 599 callback = (typeof callback === 'function') ? callback : null; 600 601 // If a callback was provided, then just go ahead and bind. 602 if (callback) { 603 minplayer.bind.call(this, 'ready', id, plugin, callback); 604 return; 605 } 606 607 // Get the plugins. 608 var plugins = minplayer.plugins; 609 610 // 0x000 611 if (!id && !plugin && !callback) { 612 return plugins; 613 } 614 // 0x100 615 else if (id && !plugin && !callback) { 616 return plugins[id]; 617 } 618 // 0x110 619 else if (id && plugin && !callback) { 620 return plugins[id][plugin]; 621 } 622 // 0x010 623 else if (!id && plugin && !callback) { 624 var plugin_types = []; 625 for (var id in plugins) { 626 if (plugins.hasOwnProperty(id) && plugins[id].hasOwnProperty(plugin)) { 627 var i = plugins[id][plugin].length; 628 while (i--) { 629 plugin_types.push(plugins[id][plugin][i]); 630 } 631 } 632 } 633 return plugin_types; 634 } 635 }; 636