1 /** The minplayer namespace. */ 2 minplayer = minplayer || {}; 3 4 /** Static array to keep track of plugin instances. */ 5 minplayer.instances = minplayer.instances || {}; 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.instances[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.instances[this.options.id]) { 132 133 // Initialize the instances. 134 minplayer.instances[this.options.id] = {}; 135 } 136 137 // Add this plugin. 138 minplayer.instances[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 // Allow this to be called on itself with a single callback. 152 if (typeof plugin === 'function') { 153 this.get(this.name, plugin); 154 return; 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 setTimeout(function() { 297 this.unbind(type, fn); 298 }, 10); 299 } 300 301 // Set the lock. 302 this.lock = true; 303 304 if (!type) { 305 this.queue = {}; 306 } 307 else if (!fn) { 308 this.queue[type] = []; 309 } 310 else { 311 // Iterate through all the callbacks and search for equal callbacks. 312 var i = 0, queue = {}; 313 for (i in this.queue[type]) { 314 if (this.queue[type][i].callback === fn) { 315 queue = this.queue[type].splice(1, 1); 316 delete queue; 317 } 318 } 319 } 320 321 // Reset the lock. 322 this.lock = false; 323 324 // Return the plugin. 325 return this; 326 }; 327 328 /** 329 * Adds an item to the queue. 330 * 331 * @param {object} context The context which this is called within. 332 * @param {string} event The event to trigger on. 333 * @param {string} id The player ID. 334 * @param {string} plugin The name of the plugin. 335 * @param {function} callback Called when the event occurs. 336 */ 337 minplayer.addQueue = function(context, event, id, plugin, callback) { 338 339 // See if it is locked... 340 if (!minplayer.lock) { 341 minplayer.queue.push({ 342 context: context, 343 id: id, 344 event: event, 345 plugin: plugin, 346 callback: callback 347 }); 348 } 349 else { 350 351 // If so, then try again after 10 milliseconds. 352 setTimeout(function() { 353 minplayer.addQueue(context, id, event, plugin, callback); 354 }, 10); 355 } 356 }; 357 358 /** 359 * Binds an event to a plugin instance, and if it doesn't exist, then caches 360 * it for a later time. 361 * 362 * @param {string} event The event to trigger on. 363 * @param {string} id The player ID. 364 * @param {string} plugin The name of the plugin. 365 * @param {function} callback Called when the event occurs. 366 * @return {boolean} If the bind was successful. 367 * @this The object in context who called this method. 368 */ 369 minplayer.bind = function(event, id, plugin, callback) { 370 371 // If no callback exists, then just return false. 372 if (!callback) { 373 return false; 374 } 375 376 // Get the instances. 377 var inst = minplayer.instances; 378 379 // See if this plugin exists. 380 if (inst[id][plugin]) { 381 382 // If so, then bind the event to this plugin. 383 inst[id][plugin].bind(event, {context: this}, function(event, data) { 384 callback.call(event.data.context, data.plugin); 385 }); 386 return true; 387 } 388 389 // If not, then add it to the queue to bind later. 390 minplayer.addQueue(this, event, id, plugin, callback); 391 392 // Return that this wasn't handled. 393 return false; 394 }; 395 396 /** 397 * The main API for minPlayer. 398 * 399 * Provided that this function takes three parameters, there are 8 different 400 * ways to use this api. 401 * 402 * id (0x100) - You want a specific player. 403 * plugin (0x010) - You want a specific plugin. 404 * callback (0x001) - You only want it when it is ready. 405 * 406 * 000 - You want all plugins from all players, ready or not. 407 * 408 * var instances = minplayer.get(); 409 * 410 * 001 - You want all plugins from all players, but only when ready. 411 * 412 * minplayer.get(function(plugin) { 413 * // Code goes here. 414 * }); 415 * 416 * 010 - You want a specific plugin from all players, ready or not... 417 * 418 * var medias = minplayer.get(null, 'media'); 419 * 420 * 011 - You want a specific plugin from all players, but only when ready. 421 * 422 * minplayer.get('player', function(player) { 423 * // Code goes here. 424 * }); 425 * 426 * 100 - You want all plugins from a specific player, ready or not. 427 * 428 * var plugins = minplayer.get('player_id'); 429 * 430 * 101 - You want all plugins from a specific player, but only when ready. 431 * 432 * minplayer.get('player_id', null, function(plugin) { 433 * // Code goes here. 434 * }); 435 * 436 * 110 - You want a specific plugin from a specific player, ready or not. 437 * 438 * var plugin = minplayer.get('player_id', 'media'); 439 * 440 * 111 - You want a specific plugin from a specific player, only when ready. 441 * 442 * minplayer.get('player_id', 'media', function(media) { 443 * // Code goes here. 444 * }); 445 * 446 * @this The context in which this function was called. 447 * @param {string} id The ID of the widget to get the plugins from. 448 * @param {string} plugin The name of the plugin. 449 * @param {function} callback Called when the plugin is ready. 450 * @return {object} The plugin object if it is immediately available. 451 */ 452 minplayer.get = function(id, plugin, callback) { 453 454 // Normalize the arguments for a better interface. 455 if (typeof id === 'function') { 456 callback = id; 457 plugin = id = null; 458 } 459 460 if (typeof plugin === 'function') { 461 callback = plugin; 462 plugin = id; 463 id = null; 464 } 465 466 // Make sure the callback is a callback. 467 callback = (typeof callback === 'function') ? callback : null; 468 469 // Get the instances. 470 var inst = minplayer.instances; 471 472 // 0x000 473 if (!id && !plugin && !callback) { 474 return inst; 475 } 476 // 0x100 477 else if (id && !plugin && !callback) { 478 return inst[id]; 479 } 480 // 0x110 481 else if (id && plugin && !callback) { 482 return inst[id][plugin]; 483 } 484 // 0x111 485 else if (id && plugin && callback) { 486 minplayer.bind.call(this, 'ready', id, plugin, callback); 487 } 488 // 0x011 489 else if (!id && plugin && callback) { 490 for (var id in inst) { 491 minplayer.bind.call(this, 'ready', id, plugin, callback); 492 } 493 } 494 // 0x101 495 else if (id && !plugin && callback) { 496 for (var plugin in inst[id]) { 497 minplayer.bind.call(this, 'ready', id, plugin, callback); 498 } 499 } 500 // 0x010 501 else if (!id && plugin && !callback) { 502 var plugins = {}; 503 for (var id in inst) { 504 if (inst.hasOwnProperty(id) && inst[id].hasOwnProperty(plugin)) { 505 plugins[id] = inst[id][plugin]; 506 } 507 } 508 return plugins; 509 } 510 // 0x001 511 else { 512 for (var id in inst) { 513 for (var plugin in inst[id]) { 514 minplayer.bind.call(this, 'ready', id, plugin, callback); 515 } 516 } 517 } 518 }; 519