* fixed remote search driver js bug in file check
[lhc/web/wiklou.git] / js2 / mwEmbed / mv_embed.js
1 /*
2 * ~mv_embed ~
3 * For details see: http://metavid.org/wiki/index.php/Mv_embed
4 *
5 * All Metavid Wiki code is released under the GPL2.
6 * For more information visit http://metavid.org/wiki/Code
7 *
8 * @url http://metavid.org
9 *
10 * parseUri:
11 * http://stevenlevithan.com/demo/parseuri/js/
12 *
13 * Config values: you can manually set the location of the mv_embed folder here
14 * (in cases where media will be hosted in a different place than the embedding page)
15 *
16 */
17 // Fix multiple instances of mv_embed (i.e. include twice from two different servers)
18 var MV_DO_INIT=true;
19 if( MV_EMBED_VERSION ){
20 MV_DO_INIT=false;
21 }
22 // Used to grab fresh copies of scripts.
23 var MV_EMBED_VERSION = '1.0r20';
24
25 /*
26 * Configuration variables should be set by extending mwConfigOptions
27 * here is the default config:
28 */
29 var mwDefaultConfig = {
30 'skin_name': 'mvpcf',
31 'jui_skin': 'redmond',
32 'video_size':'400x300'
33 }
34
35 // (We install the default config values for anything not set in mwConfig once we know we have jquery)
36 if( !mwConfig )
37 var mwConfig = {};
38
39 // parseUri 1.2.2
40 // (c) Steven Levithan <stevenlevithan.com>
41 // MIT License
42 function parseUri (str) {
43 var o = parseUri.options,
44 m = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
45 uri = {},
46 i = 14;
47
48 while (i--) uri[o.key[i]] = m[i] || "";
49
50 uri[o.q.name] = {};
51 uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
52 if ($1) uri[o.q.name][$1] = $2;
53 });
54
55 return uri;
56 };
57 parseUri.options = {
58 strictMode: false,
59 key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
60 q: {
61 name: "queryKey",
62 parser: /(?:^|&)([^&=]*)=?([^&]*)/g
63 },
64 parser: {
65 strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
66 loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
67 }
68 };
69
70 // For use when mv_embed with script-loader is in the root MediaWiki path
71 var mediaWiki_mvEmbed_path = 'js2/mwEmbed/';
72
73 var _global = this; // Global obj (depreciate use window)
74
75 /*
76 * setup the empty global $mw object
77 * will ensure all our functions are properly namespaced
78 */
79 if(!window['$mw']){
80 window['$mw'] = {}
81 }
82
83 //@@todo move these into $mw
84 var mv_init_done = false;
85 var global_cb_count = 0;
86 var global_player_list = new Array(); // The global player list per page
87 var global_req_cb = new Array(); // The global request callback array
88
89 // Get the mv_embed location if it has not been set
90 if( !mv_embed_path ) {
91 var mv_embed_path = getMvEmbedPath();
92 }
93 /**
94 * wrap the global $mw object here:
95 *
96 * Any global functions/classes that are not jQuery plugins should make
97 * there way into the $mw namespace
98 */
99 (function( $ ) {
100 /*
101 * Language classes $mw.lang
102 *
103 * Localized Language support attempts to mirror the functionality of Language.php in MediaWiki
104 * It contains methods for loading and transforming msg text
105 *
106 */
107 $.lang = {};
108 /**
109 * Setup the lang object
110 */
111 var gMsg = {};
112 var gRuleSet = {};
113
114 /**
115 * loadGM function
116 * Loads a set of json messages into the lng object.
117 *
118 * @param json msgSet The set of msgs to be loaded
119 */
120 $.lang.loadGM = function( msgSet ){
121 for( var i in msgSet ) {
122 gMsg[ i ] = msgSet[i];
123 }
124 },
125 /**
126 * loadRS function
127 * Loads a ruleset by given template key ie PLURAL : { //ruleSetObj }
128 *
129 * @param json ruleSet The ruleset object ( extends gRuleSet )
130 */
131 $.lang.loadRS = function( ruleSet ){
132 for( var i in ruleSet){
133 gRuleSet[ i ] = ruleSet[ i ];
134 }
135 }
136
137 /**
138 * Returns a transformed msg string
139 *
140 * it take a msg key and array of replacement values of form
141 * $1, $2 and does relevant msgkey transformation returning
142 * the user msg.
143 *
144 * @param string key The msg key as set by loadGm
145 * @param [mixed] args An array of replacement strings
146 * @return string
147 */
148 $.lang.gM = function( key , args ) {
149 if(! gMsg[ key ])
150 return '&lt;' + key + '&gt;';// Missing key placeholder
151
152 //swap in the arg values
153 var ms = $.lang.gMsgSwap( key, args) ;
154
155 //a quick check to see if we need to send the msg via the 'parser'
156 //(we can add more detailed check once we support more wiki syntax)
157 if(ms.indexOf('{{')==-1){
158 return ms;
159 //return ms;
160 }
161
162 //make sure we have the lagMagic setup:
163 $.lang.magicSetup();
164 //send the msg key through the parser
165 pObj = $.parser.pNew( ms );
166 //return the transformed msg
167 return pObj.getHTML();
168 }
169 /**
170 * gMsgSwap
171 *
172 * @param string key The msg key as set by loadGm
173 * @param [mixed] args An array or string to be replaced
174 * @return string
175 */
176 $.lang.gMsgSwap = function( key , args ){
177 if(! gMsg[ key ])
178 return '&lt;' + key + '&gt;';// Missing key placeholder
179 //get the messege string:
180 var ms = gMsg[ key ];
181
182 //replace values
183 if( typeof args == 'object' || typeof args == 'array' ) {
184 for( var v in args ) {
185 // Message test replace arguments start at 1 instead of zero:
186 var rep = new RegExp('\\$'+ ( parseInt(v) + 1 ), 'g');
187 ms = ms.replace( rep, args[v] );
188 }
189 } else if( typeof args =='string' || typeof args =='number' ) {
190 ms = ms.replace(/\$1/g, args);
191 }
192 return ms;
193 }
194
195 /**
196 * gMsgNoTrans
197 *
198 * @returns string The msg key without transforming it
199 */
200 $.lang.gMsgNoTrans = function( key ){
201 if( gMsg[ key ] )
202 return gMsg[ key ]
203
204 // Missing key placeholder
205 return '&lt;' + key + '&gt;';
206 }
207 /**
208 * Add Supported Magic Words to parser
209 */
210 //set the setupflag to false:
211 $.lang.doneSetup=false;
212 $.lang.magicSetup = function(){
213 if(!$.lang.doneSetup){
214 $.parser.addMagic ( {
215 'PLURAL' : $.lang.procPLURAL
216 })
217
218 $.lang.doneSetup = true;
219 }
220
221 }
222 /**
223 * Process the PLURAL special language template key:
224 */
225 $.lang.procPLURAL = function( tObj ){
226 //setup shortcuts
227 // (gRuleSet is loaded from script-loader to contains local ruleset)
228 var rs = gRuleSet['PLURAL'];
229
230 /*
231 * Plural matchRuleTest
232 */
233 function matchRuleTest(cRule, val){
234 js_log("matchRuleTest:: " + typeof cRule + ' ' + cRule + ' == ' + val );
235
236 function checkValue(compare, val){
237 if(typeof compare == 'string'){
238 range = compare.split('-');
239 if( range.length >= 1 ){
240 if( val >= range[0] && val <= range[1] )
241 return true;
242 }
243 }
244 //else do a direct compare
245 if(compare == val){
246 return true;
247 }
248 return false;
249 }
250 //check for simple cRule type:
251 if( typeof cRule == 'number'){
252 return ( parseInt( val ) == parseInt( cRule) );
253 }else if( typeof cRule == 'object' ){
254 var cmatch = {};
255 //if a list we need to match all for rule match
256 for(var i in cRule){
257 var cr = cRule[i];
258 //set cr type
259 var crType = '';
260 for( var j in cr ){
261 if(j == 'mod')
262 crType = 'mod'
263 }
264 switch(crType){
265 case 'mod':
266 if( cr ['is'] ){
267 if( checkValue( val % cr['mod'], cr ['is'] ) )
268 cmatch[i] = true;
269 }else if( cr['not']){
270 if( ! checkValue( val % cr['mod'], cr ['not'] ) )
271 cmatch[i] = true;
272 }
273 break;
274 }
275 }
276 //check all the matches (taking into consideration "or" order)
277 for(var i in cRule){
278 if( ! cmatch[i] )
279 return false;
280 }
281 return true;
282
283 }
284 }
285 /**
286 * Maps a given rule Index to template params:
287 *
288 * if index is out of range return last param
289 * @param
290 */
291 function getTempParamFromRuleInx(tObj, ruleInx ){
292 //js_log('getTempParamFromRuleInx: ruleInx: ' + ruleInx + ' tempParamLength ' + tObj.param.length );
293 if( ruleInx >= tObj.param.length )
294 return tObj.param[ tObj.param.length -1 ];
295 //else return the requested index:
296 return tObj.param[ ruleInx ];
297 }
298 var rCount=0
299 //run the actual rule lookup:
300 for(var ruleInx in rs){
301 cRule = rs[ruleInx];
302 if( matchRuleTest( cRule, tObj.arg ) ){
303 js_log("matched rule: " + ruleInx );
304 return getTempParamFromRuleInx(tObj, rCount );
305 }
306 rCount ++;
307 }
308 js_log('no match found for: ' + tObj.arg + ' using last/other : ' + tObj.param [ tObj.param.length -1 ] );
309 //return the last /"other" template param
310 return tObj.param [ tObj.param.length -1 ];
311 }
312
313 /**
314 * gMsgLoadRemote loads remote msg strings
315 *
316 * @param mixed msgSet the set of msg to load remotely
317 * @param function callback the callback to issue once string is ready
318 */
319 $.lang.gMsgLoadRemote = function( msgSet, callback ) {
320 var ammessages = '';
321 if( typeof msgSet == 'object' ) {
322 for( var i in msgSet ) {
323 ammessages += msgSet[i] + '|';
324 }
325 } else if( typeof msgSet == 'string' ) {
326 ammessages += msgSet;
327 }
328 if( ammessages == '' ) {
329 js_log( 'gMsgLoadRemote: no message set requested' );
330 return false;
331 }
332 do_api_req({
333 'data': {
334 'meta': 'allmessages',
335 'ammessages': ammessages
336 }
337 }, function( data ) {
338 if( data.query.allmessages ) {
339 var msgs = data.query.allmessages;
340 for( var i in msgs ) {
341 var ld = {};
342 ld[ msgs[i]['name'] ] = msgs[i]['*'];
343 loadGM( ld );
344 }
345 }
346 callback();
347 });
348 }
349 /**
350 * Format a size in bytes for output, using an appropriate
351 * unit (B, KB, MB or GB) according to the magnitude in question
352 *
353 * @param size Size to format
354 * @return string Plain text (not HTML)
355 */
356 $.lang.formatSize = function ( size ) {
357 // For small sizes no decimal places are necessary
358 var round = 0;
359 var msg = '';
360 if( size > 1024 ) {
361 size = size / 1024;
362 if( size > 1024 ) {
363 size = size / 1024;
364 // For MB and bigger two decimal places are smarter
365 round = 2;
366 if( size > 1024 ) {
367 size = size / 1024;
368 msg = 'mwe-size-gigabytes';
369 } else {
370 msg = 'mwe-size-megabytes';
371 }
372 } else {
373 msg = 'mwe-size-kilobytes';
374 }
375 } else {
376 msg = 'mwe-size-bytes';
377 }
378 // JavaScript does not let you choose the precision when rounding
379 var p = Math.pow(10,round);
380 var size = Math.round( size * p ) / p;
381 //@@todo we need a formatNum and we need to request some special packaged info to deal with that case.
382 return gM( msg , size );
383 };
384
385
386 /**
387 * MediaWiki wikitext "Parser"
388 *
389 * This is not feature complete but we need a way to get at template properties
390 *
391 *
392 * @param wikiText the wikitext to be parsed
393 * @return parserObj returns a parser object that has methods for getting at
394 * things you would want
395 */
396 $.parser = {};
397 var pMagicSet = {};
398 /**
399 * parser addMagic
400 *
401 * lets you add a set of magic keys and associated callback funcions
402 * callback: @param ( Object Template )
403 * callback: @return the transformed template output
404 *
405 * @param object magicSet key:callback
406 */
407 $.parser.addMagic = function( magicSet ){
408 for(var i in magicSet)
409 pMagicSet[ i ] = magicSet[i];
410 }
411
412 //actual parse call (returns parser object)
413 $.parser.pNew = function( wikiText, opt ){
414 var parseObj = function( wikiText, opt){
415 return this.init( wikiText, opt )
416 }
417 parseObj.prototype = {
418 //the wikiText "DOM"... stores the parsed wikiText structure
419 //wtDOM : {}, (not yet supported )
420
421 pOut : '', //the parser output string container
422 init :function( wikiText ){
423 this.wikiText = wikiText;
424 },
425 updateText : function( wikiText ){
426 this.wikiText = wikiText;
427 //invalidate the output (will force a reparse)
428 this.pOut = '';
429 },
430 parse : function(){
431 this.pObj = {};
432 this.pObj.tmpl = new Array();
433
434 //refrences for swap key
435 this.pObj.tmpl_text = new Array();
436 this.pObj.tmpl_key = new Array();
437 this.pObj.tmpl_ns = '' ; // wikiText with place-holder
438
439 //get templates losly based on Magnus_Manske/tmpl.js code:
440 var tcnt = 0 ;
441 var ts = '' ;
442 var curt = 0 ;
443 var schar = 0;
444
445
446 //build out nested template holders:
447 var depth = 0;
448 var tKey = 0;
449 var ns = '';
450
451 /*
452 * quickly recursive / parse out templates with top down recurse decent
453 */
454
455 // ~ probably a better algorithm out there / should mirror php parser flow ~
456 // ... but I am having fun with recursion so here it is...
457 function rdpp ( txt ){
458 var node = {};
459 //if we should output node text
460 var ont = true;
461 //inspect each char
462 for(var a=0; a < txt.length; a++){
463 if( txt[a] == '{' && txt[a+1] == '{' ){
464 a=a+2;
465 node['p'] = node;
466 if(!node['c'])
467 node['c'] = new Array();
468
469 node['c'].push( rdpp( txt.substr( a ) ) );
470 ont=true;
471 }else if( txt[a] == '}' && txt[a+1] == '}'){
472 if( !node['p'] ){
473 return node;
474 }
475 node = node['p'];
476 ont=false;
477 a=a+2;
478 }
479 if(!node['t'])
480 node['t']='';
481
482 if( txt[a] )
483 node['t']+=txt[a];
484 }
485 return node;
486 }
487 /**
488 * parse template text as template name and named params
489 */
490 function parseTmplTxt( ts ){
491 var tObj = {};
492 //Get template name:
493 tname = ts.split('\|').shift() ;
494 tname = tname.split('\{').shift() ;
495 tname = tname.replace( /^\s+|\s+$/g, "" ); //trim
496
497 //check for arguments:
498 if( tname.split(':').length == 1 ){
499 tObj["name"] = tname;
500 }else{
501 tObj["name"] = tname.split(':').shift();
502 tObj["arg"] = tname.split(':').pop();
503 }
504
505 js_log("TNAME::" + tObj["arg"] + ' from:: ' + ts);
506
507 var pSet = ts.split('\|');
508 pSet.splice(0,1);
509 if( pSet.length ){
510 tObj.param = new Array();
511 for(var pInx in pSet){
512 var tStr = pSet[ pInx ];
513 for(var b=0 ; b < tStr.length ; b++){
514 if(tStr[b] == '=' && b>0 && b<tStr.length && tStr[b-1]!='\\'){
515 //named param
516 tObj.param[ tStr.split('=').shift() ] = tStr.split('=').pop();
517 }else{
518 //indexed param
519 tObj.param[ pInx ] = tStr;
520 }
521 }
522 }
523 }
524 return tObj;
525 }
526 function getMagicTxtFromTempNode( node ){
527 node.tObj = parseTmplTxt ( node.t );
528 //do magic swap if templet key found in pMagicSet
529 if( node.tObj.name in pMagicSet){
530 var nt = pMagicSet[ node.tObj.name ]( node.tObj );
531 return nt;
532 }else{
533 //don't swap just return text
534 return node.t;
535 }
536 }
537 /**
538 * recurse_magic_swap
539 *
540 * go last child first swap upward: (could probably be integrated above somehow)
541 */
542 var pNode = null;
543 function recurse_magic_swap( node ){
544 if( !pNode )
545 pNode = node;
546
547 if( node['c'] ){
548 //swap all the kids:
549 for(var i in node['c']){
550 var nt = recurse_magic_swap( node['c'][i] );
551 //swap it into current
552 if( node.t ){
553 node.t = node.t.replace( node['c'][i].t, nt);
554 }
555 //swap into parent
556 pNode.t = pNode.t.replace( node['c'][i].t, nt);
557 }
558 //do the current node:
559 var nt = getMagicTxtFromTempNode( node );
560 pNode.t = pNode.t.replace(node.t , nt);
561 //run the swap for the outer most node
562 return node.t;
563 }else{
564 //node.t = getMagicFromTempObj( node.t )
565 return getMagicTxtFromTempNode( node );
566 }
567 }
568 //get text node system:
569 var node = rdpp ( this.wikiText );
570 //debugger;
571 //parse out stuff:
572
573 this.pOut = recurse_magic_swap( node);
574
575 },
576 /**
577 * Returns the transformed wikitext
578 *
579 * Build output from swapable index
580 * (all transforms must be expanded in parse stage and linerarly rebuilt)
581 * Alternativly we could build output using a placeholder & replace system
582 * (this lets us be slightly more slopty with ordering and indexes, but probably slower)
583 *
584 * Ideal: we build a 'wiki DOM'
585 * When editing you update the data structure directly
586 * Then in output time you just go DOM->html-ish output without re-parsing anything
587 */
588 getHTML : function(){
589 //wikiText updates should invalidate pOut
590 if( this.pOut == ''){
591 this.parse();
592 }
593 return this.pOut;
594 }
595 };
596 //return the parserObj
597 return new parseObj( wikiText, opt) ;
598 }
599
600 })(window.$mw);
601 //setup legacy global shortcuts:
602 var loadGM = $mw.lang.loadGM;
603 var gM = $mw.lang.gM;
604
605 //if some no-js2 script defined and loaded gMsg in global space:
606 if( _global['gMsg'] ){
607 loadGM( _global['gMsg'] );
608 }
609
610 // All default messages in [English] should be overwritten by the CMS language message system.
611 $mw.lang.loadGM({
612 "mwe-loading_txt" : "loading <blink>...<\/blink>",
613 "mwe-loading_title" : "Loading...",
614 "mwe-size-gigabytes" : "$1 GB",
615 "mwe-size-megabytes" : "$1 MB",
616 "mwe-size-kilobytes" : "$1 K",
617 "mwe-size-bytes" : "$1 B",
618 "mwe-error_load_lib" : "Error: JavaScript $1 was not retrievable or does not define $2"
619 });
620
621
622
623 /**
624 * AutoLoader paths (this should mirror the file: jsAutoloadLocalClasses.php )
625 * Any file _not_ listed here won't be auto-loadable
626 * @path The path to the file (or set of files) with ending slash
627 * @gClasses The set of classes
628 * if it's an array, $j.className becomes jquery.className.js
629 * if it's an associative object then key => value pairs are used
630 */
631 if( typeof mvAutoLoadClasses == 'undefined' )
632 mvAutoLoadClasses = {};
633
634 // The script that loads the class set
635 function lcPaths( classSet ){
636 for( var i in classSet ) {
637 mvAutoLoadClasses[i] = classSet[i];
638 }
639 }
640
641 function mvGetClassPath(k){
642 if( mvAutoLoadClasses[k] ) {
643 //js_log('got class path:' + k + ' : '+ mvClassPaths[k]);
644 return mvAutoLoadClasses[k];
645 } else {
646 return js_error('could not find path for requested class ' + k );
647 }
648 }
649 if( typeof mvCssPaths == 'undefined' )
650 mvCssPaths = {};
651
652 function lcCssPath( cssSet ) {
653 for( var i in cssSet ) {
654 mvCssPaths[i] = mv_embed_path + cssSet[i];
655 }
656 }
657
658 /*
659 * -- Load Class Paths --
660 *
661 * MUST BE VALID JSON (NOT JS)
662 * This is used by the script loader to auto-load classes (so we only define
663 * this once for PHP & JavaScript)
664 *
665 * This is more verbose than the earlier version that compressed paths
666 * but it's all good, gzipping helps compress path strings
667 * grouped by directory.
668 *
669 * Right now the PHP AutoLoader only reads this mv_embed.js file.
670 * In the future we could have multiple lcPath calls that PHP reads
671 * (if our autoloading class list becomes too long) just have to add those
672 * files to the jsAutoLoader file list.
673 */
674 lcPaths({
675 "mv_embed" : "mv_embed.js",
676 "window.jQuery" : "jquery/jquery-1.3.2.js",
677 "$j.fn.pngFix" : "jquery/plugins/jquery.pngFix.js",
678 "$j.fn.autocomplete": "jquery/plugins/jquery.autocomplete.js",
679 "$j.fn.hoverIntent" : "jquery/plugins/jquery.hoverIntent.js",
680 "$j.fn.datePicker" : "jquery/plugins/jquery.datePicker.js",
681 "$j.ui" : "jquery/jquery.ui/ui/ui.core.js",
682 "$j.fn.ColorPicker" : "libClipEdit/colorpicker/js/colorpicker.js",
683 "$j.Jcrop" : "libClipEdit/Jcrop/js/jquery.Jcrop.js",
684 "$j.fn.simpleUploadForm": "libAddMedia/simpleUploadForm.js",
685
686 "ctrlBuilder" : "skins/ctrlBuilder.js",
687 "kskinConfig" : "skins/kskin/kskin.js",
688 "mvpcfConfig" : "skins/mvpcf/mvpcf.js",
689
690 "$j.secureEvalJSON" : "jquery/plugins/jquery.secureEvalJSON.js",
691 "$j.cookie" : "jquery/plugins/jquery.cookie.js",
692 "$j.contextMenu" : "jquery/plugins/jquery.contextMenu.js",
693 "$j.fn.suggestions" : "jquery/plugins/jquery.suggestions.js",
694
695 "$j.effects.blind" : "jquery/jquery.ui/ui/effects.blind.js",
696 "$j.effects.drop" : "jquery/jquery.ui/ui/effects.drop.js",
697 "$j.effects.pulsate" : "jquery/jquery.ui/ui/effects.pulsate.js",
698 "$j.effects.transfer" : "jquery/jquery.ui/ui/effects.transfer.js",
699 "$j.ui.droppable" : "jquery/jquery.ui/ui/ui.droppable.js",
700 "$j.ui.slider" : "jquery/jquery.ui/ui/ui.slider.js",
701 "$j.effects.bounce" : "jquery/jquery.ui/ui/effects.bounce.js",
702 "$j.effects.explode" : "jquery/jquery.ui/ui/effects.explode.js",
703 "$j.effects.scale" : "jquery/jquery.ui/ui/effects.scale.js",
704 "$j.ui.datepicker" : "jquery/jquery.ui/ui/ui.datepicker.js",
705 "$j.ui.progressbar" : "jquery/jquery.ui/ui/ui.progressbar.js",
706 "$j.ui.sortable" : "jquery/jquery.ui/ui/ui.sortable.js",
707 "$j.effects.clip" : "jquery/jquery.ui/ui/effects.clip.js",
708 "$j.effects.fold" : "jquery/jquery.ui/ui/effects.fold.js",
709 "$j.effects.shake" : "jquery/jquery.ui/ui/effects.shake.js",
710 "$j.ui.dialog" : "jquery/jquery.ui/ui/ui.dialog.js",
711 "$j.ui.resizable" : "jquery/jquery.ui/ui/ui.resizable.js",
712 "$j.ui.tabs" : "jquery/jquery.ui/ui/ui.tabs.js",
713 "$j.effects.core" : "jquery/jquery.ui/ui/effects.core.js",
714 "$j.effects.highlight" : "jquery/jquery.ui/ui/effects.highlight.js",
715 "$j.effects.slide" : "jquery/jquery.ui/ui/effects.slide.js",
716 "$j.ui.accordion" : "jquery/jquery.ui/ui/ui.accordion.js",
717 "$j.ui.draggable" : "jquery/jquery.ui/ui/ui.draggable.js",
718 "$j.ui.selectable" : "jquery/jquery.ui/ui/ui.selectable.js",
719
720 "mvFirefogg" : "libAddMedia/mvFirefogg.js",
721 "mvAdvFirefogg" : "libAddMedia/mvAdvFirefogg.js",
722 "mvBaseUploadInterface" : "libAddMedia/mvBaseUploadInterface.js",
723 "remoteSearchDriver" : "libAddMedia/remoteSearchDriver.js",
724 "seqRemoteSearchDriver" : "libAddMedia/seqRemoteSearchDriver.js",
725
726 "baseRemoteSearch" : "libAddMedia/searchLibs/baseRemoteSearch.js",
727 "mediaWikiSearch" : "libAddMedia/searchLibs/mediaWikiSearch.js",
728 "metavidSearch" : "libAddMedia/searchLibs/metavidSearch.js",
729 "archiveOrgSearch" : "libAddMedia/searchLibs/archiveOrgSearch.js",
730 "baseRemoteSearch" : "libAddMedia/searchLibs/baseRemoteSearch.js",
731
732 "mvClipEdit" : "libClipEdit/mvClipEdit.js",
733
734 "embedVideo" : "libEmbedVideo/embedVideo.js",
735 "flashEmbed" : "libEmbedVideo/flashEmbed.js",
736 "genericEmbed" : "libEmbedVideo/genericEmbed.js",
737 "htmlEmbed" : "libEmbedVideo/htmlEmbed.js",
738 "javaEmbed" : "libEmbedVideo/javaEmbed.js",
739 "nativeEmbed" : "libEmbedVideo/nativeEmbed.js",
740 "quicktimeEmbed" : "libEmbedVideo/quicktimeEmbed.js",
741 "vlcEmbed" : "libEmbedVideo/vlcEmbed.js",
742
743 "mvPlayList" : "libSequencer/mvPlayList.js",
744 "mvSequencer" : "libSequencer/mvSequencer.js",
745 "mvFirefoggRender" : "libSequencer/mvFirefoggRender.js",
746 "mvTimedEffectsEdit": "libSequencer/mvTimedEffectsEdit.js",
747
748 "mvTextInterface" : "libTimedText/mvTextInterface.js"
749 });
750
751 // Dependency mapping for CSS files for self-contained included plugins:
752 lcCssPath({
753 '$j.Jcrop' : 'libClipEdit/Jcrop/css/jquery.Jcrop.css',
754 '$j.fn.ColorPicker' : 'libClipEdit/colorpicker/css/colorpicker.css'
755 })
756
757
758 // Get the loading image
759 function mv_get_loading_img( style, class_attr ){
760 var style_txt = (style)?style:'';
761 var class_attr = (class_attr)?'class="'+class_attr+'"':'class="mv_loading_img"';
762 return '<div '+class_attr+' style="' + style +'"></div>';
763 }
764
765 function mv_set_loading(target, load_id){
766 var id_attr = ( load_id )?' id="' + load_id + '" ':'';
767 $j(target).append('<div '+id_attr+' style="position:absolute;top:0px;left:0px;height:100%;width:100%;'+
768 'background-color:#FFF;">' +
769 mv_get_loading_img('top:30px;left:30px') +
770 '</div>');
771 }
772
773 /**
774 * mvJsLoader class handles initialization and js file loads
775 */
776
777 // Shortcut
778 function mwLoad( loadSet, callback ) {
779 mvJsLoader.doLoad( loadSet, callback );
780 }
781 var mvJsLoader = {
782 libreq : {},
783 libs : {},
784
785 // Base lib flags
786 onReadyEvents: new Array(),
787 doneReadyEvents: false,
788 jQuerySetupFlag: false,
789
790 // To keep consistency across threads
791 ptime: 0,
792 ctime: 0,
793
794 load_error: false, // Load error flag (false by default)
795 load_time: 0,
796 callbacks: new Array(),
797 cur_path: null,
798 missing_path : null,
799 doLoad: function( loadLibs, callback ) {
800 this.ctime++;
801
802 if( loadLibs && loadLibs.length != 0 ) {
803 // Set up this.libs
804 // First check if we already have this library loaded
805 var all_libs_loaded = true;
806 for( var i = 0; i< loadLibs.length; i++ ) {
807 // Check if the library is already loaded
808 if( ! this.checkObjPath( loadLibs[i] ) ) {
809 all_libs_loaded = false;
810 }
811 }
812 if( all_libs_loaded ) {
813 //js_log( 'Libraries ( ' + loadLibs + ') already loaded... skipping load request' );
814 callback();
815 return;
816 }
817 // Do a check for any CSS we may need and get it
818 for( var i = 0; i < loadLibs.length; i++ ) {
819 if( typeof mvCssPaths[ loadLibs[i] ] != 'undefined' ) {
820 loadExternalCss( mvCssPaths[ loadLibs[i] ] );
821 }
822 }
823
824 // Check if we should use the script loader to combine all the requests into one
825 if( typeof mwSlScript != 'undefined' ) {
826 var class_set = '';
827 var last_class = '';
828 var coma = '';
829 for( var i = 0; i < loadLibs.length; i++ ) {
830 var curLib = loadLibs[i];
831 // Only add if not included yet:
832 if( ! this.checkObjPath( curLib ) ) {
833 class_set += coma + curLib;
834 last_class = curLib;
835 coma = ',';
836 }
837 }
838 //Build the url to the scriptServer striping its request paramaters:
839 var puri = parseUri( getMvEmbedURL() );
840 if( ( getMvEmbedURL().indexOf('://') != -1 )
841 && puri.host != parseUri( document.URL ).host )
842 {
843 var scriptPath = puri.protocol + '://' + puri.authority + puri.path;
844 }else{
845 var scriptPath = puri.path;
846 }
847 js_log('scriptServer Path is: ' + scriptPath + "\n host script path:" + getMvEmbedURL() );
848 var dbug_attr = ( puri.queryKey['debug'] ) ? '&debug=true' : '';
849 this.libs[ last_class ] = scriptPath + '?class=' + class_set +
850 '&urid=' + getMvUniqueReqId() + dbug_attr;
851
852 } else {
853 // Do many requests
854 for( var i = 0; i < loadLibs.length; i++ ) {
855 var curLib = loadLibs[i];
856 if( curLib ) {
857 var libLoc = mvGetClassPath( curLib );
858 // Do a direct load of the file (pass along unique request id from
859 // request or mv_embed Version )
860 var qmark = (libLoc.indexOf( '?' ) !== true) ? '?' : '&';
861 this.libs[curLib] = mv_embed_path + libLoc + qmark + 'urid=' + getMvUniqueReqId();
862 }
863 }
864 }
865 }
866 if( callback ) {
867 this.callbacks.push( callback );
868 }
869 if( this.checkLoading() ) {
870 //@@todo we should check the <script> Element .onLoad property to
871 //make sure its just not a very slow connection
872
873 if( this.load_time++ > 4000 ){ // Time out after ~80 seconds
874 js_error( gM('mwe-error_load_lib', [mvGetClassPath(this.missing_path), this.missing_path]) );
875 this.load_error = true;
876 } else {
877 setTimeout( 'mvJsLoader.doLoad()', 20 );
878 }
879 } else {
880 //js_log('checkLoading passed. Running callbacks...');
881 // Only do callbacks if we are in the same instance (weird concurrency issue)
882 var cb_count=0;
883 for( var i = 0; i < this.callbacks.length; i++ )
884 cb_count++;
885 //js_log('RESET LIBS: loading is: '+ loading + ' callback count: '+cb_count +
886 // ' p:'+ this.ptime +' c:'+ this.ctime);
887
888 // Reset the libs
889 this.libs = {};
890 //js_log('done loading, do call: ' + this.callbacks[0] );
891 while( this.callbacks.length != 0 ) {
892 if( this.ptime == this.ctime - 1 ) { // Enforce thread consistency
893 this.callbacks.pop()();
894 //func = this.callbacks.pop();
895 //js_log(' run: '+this.ctime+ ' p: ' + this.ptime + ' ' +loading+ ' :'+ func);
896 //func();
897 } else {
898 // Re-issue doLoad ( ptime will be set to ctime so we should catch up)
899 setTimeout( 'mvJsLoader.doLoad()', 25 );
900 break;
901 }
902 }
903 }
904 this.ptime = this.ctime;
905 },
906 doLoadDepMode: function( loadChain, callback ) {
907 // Firefox executes JS in the order in which it is included, so just directly issue the request
908 if( $j.browser.firefox ) {
909 var loadSet = [];
910 for( var i = 0; i < loadChain.length; i++ ) {
911 for( var j = 0; j < loadChain[i].length; j++ ) {
912 loadSet.push( loadChain[i][j] );
913 }
914 }
915 mvJsLoader.doLoad( loadSet, callback );
916 } else {
917 // Safari and IE tend to execute out of order so load with dependency checks
918 mvJsLoader.doLoad( loadChain.shift(), function() {
919 if( loadChain.length != 0 ) {
920 mvJsLoader.doLoadDepMode( loadChain, callback );
921 } else {
922 callback();
923 }
924 });
925 }
926 },
927 checkLoading: function() {
928 var loading = 0;
929 var i = null;
930 for( var i in this.libs ) { // for/in loop is OK on an object
931 if( !this.checkObjPath( i ) ) {
932 if( !this.libreq[i] ) {
933 loadExternalJs( this.libs[i] );
934 }
935 this.libreq[i] = 1;
936 //js_log("has not yet loaded: " + i);
937 loading = 1;
938 }
939 }
940 return loading;
941 },
942 checkObjPath: function( libVar ) {
943 if( !libVar )
944 return false;
945 var objPath = libVar.split( '.' )
946 var cur_path = '';
947 for( var p = 0; p < objPath.length; p++ ) {
948 cur_path = (cur_path == '') ? cur_path + objPath[p] : cur_path + '.' + objPath[p];
949 eval( 'var ptest = typeof ( '+ cur_path + ' ); ');
950 if( ptest == 'undefined' ) {
951 this.missing_path = cur_path;
952 return false;
953 }
954 }
955 this.cur_path = cur_path;
956 return true;
957 },
958 /**
959 * checks for jQuery and adds the $j noConflict var
960 */
961 jQueryCheck: function( callback ) {
962 //js_log( 'jQueryCheck::' );
963 // Skip stuff if $j is already loaded:
964 if( _global['$j'] && callback )
965 callback();
966 var _this = this;
967 // Load jQuery
968 _this.doLoad([
969 'window.jQuery'
970 ], function() {
971 //only do the $j setup once:
972 if(!_global['$j']){
973 _global['$j'] = jQuery.noConflict();
974 }
975 if( _this.jQuerySetupFlag == false){
976 js_log('setup mv_embed jQuery bindings');
977 //setup our global settings using the (jQuery helper)
978 mwConfig = $j.extend( mwDefaultConfig, mwConfig);
979
980 // Set up the skin path
981 _global['mv_jquery_skin_path'] = mv_embed_path + 'jquery/jquery.ui/themes/' +mwConfig['jui_skin'] + '/';
982 _global['mv_skin_img_path'] = mv_embed_path + 'skins/' + mwConfig['skin_name'] + '/images/';
983 _global['mv_default_thumb_url'] = mv_skin_img_path + 'vid_default_thumb.jpg';
984
985 //setup skin dependent dependencies
986 lcCssPath({'embedVideo' : 'skins/' + mwConfig['skin_name'] + '/playerSkin.css'});
987
988 // Make sure the skin/style sheets are always available:
989 loadExternalCss( mv_jquery_skin_path + 'jquery-ui-1.7.1.custom.css' );
990 loadExternalCss( mv_embed_path + 'skins/' + mwConfig['skin_name'] + '/styles.css' );
991
992 // Set up AJAX to not send dynamic URLs for loading scripts (we control that with
993 // the scriptLoader)
994 $j.ajaxSetup({
995 cache: true
996 });
997
998 js_log( 'jQuery loaded into $j' );
999 // Set up mvEmbed jQuery bindings and config based dependencies
1000 mv_jqueryBindings();
1001 _this.jQuerySetupFlag = true;
1002 }
1003 // Run the callback
1004 if( callback ) {
1005 callback();
1006 }
1007 });
1008 },
1009 embedVideoCheck:function( callback ) {
1010 var _this = this;
1011 js_log( 'embedVideoCheck:' );
1012 // Make sure we have jQuery
1013 _this.jQueryCheck( function() {
1014 //set class videonojs to hidden
1015 $j('.videonojs').html( gM('mwe-loading_txt') );
1016 //Set up the embed video player class request: (include the skin js as well)
1017 var depReq = [
1018 [
1019 '$j.ui',
1020 'embedVideo',
1021 'ctrlBuilder',
1022 '$j.cookie'
1023 ],
1024 [
1025 '$j.ui.slider'
1026 ]
1027 ];
1028 //add skin if set:
1029 if( mwConfig['skin_name'] )
1030 depReq[0].push( mwConfig['skin_name'] + 'Config' );
1031
1032 // Add PNG fix if needed:
1033 if( $j.browser.msie || $j.browser.version < 7 )
1034 depReq[0].push( '$j.fn.pngFix' );
1035
1036 _this.doLoadDepMode( depReq, function() {
1037 embedTypes.init();
1038 callback();
1039 $j('.videonojs').remove();
1040 });
1041 });
1042 },
1043 addLoadEvent: function( fn ) {
1044 this.onReadyEvents.push( fn );
1045 },
1046 // Check the jQuery flag. This way, when remote embedding, we don't load jQuery
1047 // unless js2AddOnloadHook was used or there is video on the page.
1048 runQueuedFunctions: function() {
1049 var _this = this;
1050 this.doneReadyEvents = true;
1051 this.jQueryCheck( function() {
1052 _this.runReadyEvents();
1053 });
1054 },
1055 runReadyEvents: function() {
1056 js_log( "runReadyEvents" );
1057 while( this.onReadyEvents.length ) {
1058 this.onReadyEvents.shift()();
1059 }
1060 }
1061 }
1062
1063 // Load an external JS file. Similar to jquery .require plugin,
1064 // but checks for object availability rather than load state.
1065
1066 /*********** INITIALIZATION CODE *************
1067 * This will get called when the DOM is ready
1068 *********************************************/
1069 /* jQuery .ready does not work when jQuery is loaded dynamically.
1070 * For an example of the problem see: 1.1.3 working: http://pastie.caboo.se/92588
1071 * and >= 1.1.4 not working: http://pastie.caboo.se/92595
1072 * $j(document).ready( function(){ */
1073 function mwdomReady( force ) {
1074 js_log( 'f:mwdomReady:' );
1075 if( !force && mv_init_done ) {
1076 js_log( "mv_init_done already done, do nothing..." );
1077 return false;
1078 }
1079 mv_init_done = true;
1080 // Handle the execution of queued functions with jQuery "ready"
1081
1082 // Check if this page has a video or playlist
1083 var e = [
1084 document.getElementsByTagName( "video" ),
1085 document.getElementsByTagName( "audio" ),
1086 document.getElementsByTagName( "playlist" )
1087 ];
1088 if( e[0].length != 0 || e[1].length != 0 || e[2].length != 0 ) {
1089 js_log( 'we have items to rewrite' );
1090 setSwappableToLoading( e );
1091 // Load libs and process them
1092 mvJsLoader.embedVideoCheck( function() {
1093 // Run any queued global events:
1094 mv_video_embed( function() {
1095 mvJsLoader.runQueuedFunctions();
1096 });
1097 });
1098 } else {
1099 // If we already have jQuery, make sure it's loaded into its proper context $j
1100 // Run any queued global events
1101 mvJsLoader.runQueuedFunctions();
1102 }
1103 }
1104 // A quick function that sets the initial text of swappable elements to "loading".
1105 // jQuery might not be ready. Does not destroy inner elements.
1106 function setSwappableToLoading( e ) {
1107 //for(var i =0)
1108 //for(var j = 0; i < j.length; j++){
1109 //}
1110 }
1111 //js2AddOnloadHook: ensure jQuery and the DOM are ready
1112 function js2AddOnloadHook( func ) {
1113 // If we have already run the DOM-ready function, just run the function directly:
1114 if( mvJsLoader.doneReadyEvents ) {
1115 // Make sure jQuery is there:
1116 mvJsLoader.jQueryCheck( function() {
1117 func();
1118 });
1119 } else {
1120 mvJsLoader.addLoadEvent( func );
1121 }
1122 }
1123 // Deprecated mwAddOnloadHook in favor of js2 naming (for clear separation of js2 code from old MW code
1124 var mwAddOnloadHook = js2AddOnloadHook;
1125 /*
1126 * This function allows for targeted rewriting
1127 */
1128 function rewrite_by_id( vid_id, ready_callback ) {
1129 js_log( 'f:rewrite_by_id: ' + vid_id );
1130 // Force a re-check of the DOM for playlist or video elements:
1131 mvJsLoader.embedVideoCheck( function() {
1132 mv_video_embed( ready_callback, vid_id );
1133 });
1134 }
1135
1136
1137 /*********** INITIALIZATION CODE *************
1138 * set DOM-ready callback to init_mv_embed
1139 *********************************************/
1140 // for Mozilla browsers
1141 if ( document.addEventListener ) {
1142 document.addEventListener( "DOMContentLoaded", function(){ mwdomReady() }, false );
1143 } else {
1144 // Use the onload method instead when DOMContentLoaded does not exist
1145 window.onload = function() { mwdomReady() };
1146 }
1147 /*
1148 * Should deprecate and use jquery.ui.dialog instead
1149 */
1150 function mv_write_modal( content, speed ) {
1151 $j( '#modalbox,#mv_overlay' ).remove();
1152 $j( 'body' ).append(
1153 '<div id="modalbox" style="background:#DDD;border:3px solid #666666;font-size:115%;' +
1154 'top:30px;left:20px;right:20px;bottom:30px;position:fixed;z-index:100;">' +
1155 content +
1156 '</div>' +
1157 '<div id="mv_overlay" style="background:#000;cursor:wait;height:100%;left:0;position:fixed;' +
1158 'top:0;width:100%;z-index:5;filter:alpha(opacity=60);-moz-opacity: 0.6;' +
1159 'opacity: 0.6;"/>');
1160 $j( '#modalbox,#mv_overlay' ).show( speed );
1161 }
1162 function mv_remove_modal( speed ) {
1163 $j( '#modalbox,#mv_overlay' ).remove( speed );
1164 }
1165
1166 /*
1167 * Store all the mwEmbed jQuery-specific bindings
1168 * (set up after jQuery is available).
1169 * This lets you call rewrites in a jQuery way
1170 *
1171 * @@ eventually we should refactor mwCode over to jQuery style plugins
1172 * and mv_embed.js will just handle dependency mapping and loading.
1173 *
1174 */
1175 function mv_jqueryBindings() {
1176 js_log( 'mv_jqueryBindings' );
1177 (function( $ ) {
1178 $.fn.addMediaWiz = function( iObj, callback ) {
1179 // First set the cursor for the button to "loading"
1180 $j( this.selector ).css( 'cursor', 'wait' ).attr( 'title', gM( 'mwe-loading_title' ) );
1181
1182 iObj['target_invocation'] = this.selector;
1183
1184 // Load the mv_embed_base skin:
1185 loadExternalCss( mv_jquery_skin_path + 'jquery-ui-1.7.1.custom.css' );
1186 loadExternalCss( mv_embed_path + 'skins/' + mwConfig['skin_name']+'/styles.css' );
1187 // Load all the required libs:
1188 mvJsLoader.jQueryCheck( function() {
1189 // Load with staged dependencies (for IE and Safari that don't execute in order)
1190 mvJsLoader.doLoadDepMode([
1191 [ 'remoteSearchDriver',
1192 '$j.cookie',
1193 '$j.ui'
1194 ],[
1195 '$j.ui.resizable',
1196 '$j.ui.draggable',
1197 '$j.ui.dialog',
1198 '$j.ui.tabs',
1199 '$j.ui.sortable'
1200 ]
1201 ], function() {
1202 iObj['instance_name'] = 'rsdMVRS';
1203 _global['rsdMVRS'] = new remoteSearchDriver( iObj );
1204 if( callback ) {
1205 callback( _global['rsdMVRS'] );
1206 }
1207 });
1208 });
1209 }
1210 $.fn.sequencer = function( iObj, callback ) {
1211 // Debugger
1212 iObj['target_sequence_container'] = this.selector;
1213 // Issue a request to get the CSS file (if not already included):
1214 loadExternalCss( mv_jquery_skin_path + 'jquery-ui-1.7.1.custom.css' );
1215 loadExternalCss( mv_embed_path + 'skins/' + mwConfig['skin_name'] + '/mv_sequence.css' );
1216 // Make sure we have the required mv_embed libs (they are not loaded when no video
1217 // element is on the page)
1218 mvJsLoader.embedVideoCheck( function() {
1219 // Load the playlist object and then the jQuery UI stuff:
1220 mvJsLoader.doLoadDepMode([
1221 [
1222 'mvPlayList',
1223 '$j.ui',
1224 '$j.contextMenu',
1225 '$j.secureEvalJSON',
1226 'mvSequencer'
1227 ],
1228 [
1229 '$j.ui.accordion',
1230 '$j.ui.dialog',
1231 '$j.ui.droppable',
1232 '$j.ui.draggable',
1233 '$j.ui.progressbar',
1234 '$j.ui.sortable',
1235 '$j.ui.resizable',
1236 '$j.ui.slider',
1237 '$j.ui.tabs'
1238 ]
1239 ], function() {
1240 js_log( 'calling new mvSequencer' );
1241 // Initialise the sequence object (it will take over from there)
1242 // No more than one mvSeq obj for now:
1243 if( !_global['mvSeq'] ) {
1244 _global['mvSeq'] = new mvSequencer( iObj );
1245 } else {
1246 js_log( 'mvSeq already init' );
1247 }
1248 });
1249 });
1250 }
1251 /*
1252 * The Firefogg jQuery function:
1253 * @@note This Firefogg invocation could be made to work more like real jQuery plugins
1254 */
1255 $.fn.firefogg = function( iObj, callback ) {
1256 if( !iObj )
1257 iObj = {};
1258 // Add the base theme CSS:
1259 loadExternalCss( mv_jquery_skin_path + 'jquery-ui-1.7.1.custom.css' );
1260 loadExternalCss( mv_embed_path + 'skins/'+mwConfig['skin_name'] + '/styles.css' );
1261
1262 // Check if we already have Firefogg loaded (the call just updates the element's
1263 // properties)
1264 var sElm = $j( this.selector ).get( 0 );
1265 if( sElm['firefogg'] ) {
1266 if( sElm['firefogg'] == 'loading' ) {
1267 js_log( "Error: called firefogg operations on Firefogg selector that is " +
1268 "not done loading" );
1269 return false;
1270 }
1271 // Update properties
1272 for( var i in iObj ) {
1273 js_log( "firefogg::updated: " + i + ' to '+ iObj[i] );
1274 sElm['firefogg'][i] = iObj[i];
1275 }
1276 return sElm['firefogg'];
1277 } else {
1278 // Avoid concurency
1279 sElm['firefogg'] = 'loading';
1280 }
1281 // Add the selector
1282 iObj['selector'] = this.selector;
1283
1284 var loadSet = [
1285 [
1286 'mvBaseUploadInterface',
1287 'mvFirefogg',
1288 '$j.ui'
1289 ],
1290 [
1291 '$j.ui.progressbar',
1292 '$j.ui.dialog',
1293 '$j.ui.draggable'
1294 ]
1295 ];
1296 if( iObj.encoder_interface ) {
1297 loadSet.push([
1298 'mvAdvFirefogg',
1299 '$j.cookie',
1300 '$j.ui.accordion',
1301 '$j.ui.slider',
1302 '$j.ui.datepicker'
1303 ]);
1304 }
1305 // Make sure we have everything loaded that we need:
1306 mvJsLoader.doLoadDepMode( loadSet, function() {
1307 js_log( 'firefogg libs loaded. target select:' + iObj.selector );
1308 // Select interface provider based on whether we want to include the
1309 // encoder interface or not
1310 if( iObj.encoder_interface ) {
1311 var myFogg = new mvAdvFirefogg( iObj );
1312 } else {
1313 var myFogg = new mvFirefogg( iObj );
1314 }
1315 if( myFogg ) {
1316 myFogg.doRewrite( callback );
1317 var selectorElement = $j( iObj.selector ).get( 0 );
1318 selectorElement['firefogg'] = myFogg;
1319 }
1320 });
1321 }
1322 // Take an input player as the selector and expose basic rendering controls
1323 $.fn.firefoggRender = function( iObj, callback ) {
1324 // Check if we already have render loaded then just pass on updates/actions
1325 var sElm = $j( this.selector ).get( 0 );
1326 if( sElm['fogg_render'] ) {
1327 if( sElm['fogg_render'] == 'loading' ) {
1328 js_log( "Error: called firefoggRender while loading" );
1329 return false;
1330 }
1331 // Call or update the property:
1332 }
1333 sElm['fogg_render'] = 'loading';
1334 // Add the selector
1335 iObj['player_target'] = this.selector;
1336 mvJsLoader.doLoad([
1337 'mvFirefogg',
1338 'mvFirefoggRender'
1339 ], function() {
1340 sElm['fogg_render'] = new mvFirefoggRender( iObj );
1341 if( callback && typeof callback == 'function' )
1342 callback( sElm['fogg_render'] );
1343 });
1344 }
1345
1346 $.fn.baseUploadInterface = function(iObj) {
1347 mvJsLoader.doLoadDepMode([
1348 [
1349 'mvBaseUploadInterface',
1350 '$j.ui',
1351 ],
1352 [
1353 '$j.ui.progressbar',
1354 '$j.ui.dialog'
1355 ]
1356 ], function() {
1357 myUp = new mvBaseUploadInterface( iObj );
1358 myUp.setupForm();
1359 });
1360 }
1361
1362 // Shortcut to a themed button
1363 $.btnHtml = function( msg, className, iconId, opt ) {
1364 if( !opt )
1365 opt = {};
1366 var href = (opt.href) ? opt.href : '#';
1367 var target_attr = (opt.target) ? ' target="' + opt.target + '" ' : '';
1368 var style_attr = (opt.style) ? ' style="' + opt.style + '" ' : '';
1369 return '<a href="' + href + '" ' + target_attr + style_attr +
1370 ' class="ui-state-default ui-corner-all ui-icon_link ' +
1371 className + '"><span class="ui-icon ui-icon-' + iconId + '" />' +
1372 '<span class="btnText">'+ msg +'<span></a>';
1373 }
1374 // Shortcut to bind hover state
1375 $.fn.btnBind = function() {
1376 $j( this ).hover(
1377 function() {
1378 $j( this ).addClass( 'ui-state-hover' );
1379 },
1380 function() {
1381 $j( this ).removeClass( 'ui-state-hover' );
1382 }
1383 )
1384 return this;
1385 }
1386
1387 })(jQuery);
1388 }
1389 /*
1390 * Utility functions:
1391 */
1392 // Simple URL rewriter (could probably be refactored into an inline regular exp)
1393 function getURLParamReplace( url, opt ) {
1394 var pSrc = parseUri( url );
1395 if( pSrc.protocol != '' ) {
1396 var new_url = pSrc.protocol + '://' + pSrc.authority + pSrc.path + '?';
1397 } else {
1398 var new_url = pSrc.path + '?';
1399 }
1400 var amp = '';
1401 for( var key in pSrc.queryKey ) {
1402 var val = pSrc.queryKey[ key ];
1403 // Do override if requested
1404 if( opt[ key ] )
1405 val = opt[ key ];
1406 new_url += amp + key + '=' + val;
1407 amp = '&';
1408 };
1409 // Add any vars that were not already there:
1410 for( var i in opt ) {
1411 if( !pSrc.queryKey[i] ) {
1412 new_url += amp + i + '=' + opt[i];
1413 amp = '&';
1414 }
1415 }
1416 return new_url;
1417 }
1418 /**
1419 * Given a float number of seconds, returns npt format response.
1420 *
1421 * @param float Seconds
1422 * @param boolean If we should show milliseconds or not.
1423 */
1424 function seconds2npt( sec, show_ms ) {
1425 if( isNaN( sec ) ) {
1426 // js_log("warning: trying to get npt time on NaN:" + sec);
1427 return '0:0:0';
1428 }
1429 var hours = Math.floor( sec / 3600 );
1430 var minutes = Math.floor( (sec / 60) % 60 );
1431 var seconds = sec % 60;
1432 // Round the number of seconds to the required number of significant digits
1433 if( show_ms ) {
1434 seconds = Math.round( seconds * 1000 ) / 1000;
1435 } else {
1436 seconds = Math.round( seconds );
1437 }
1438 if( seconds < 10 )
1439 seconds = '0' + seconds;
1440 if( minutes < 10 )
1441 minutes = '0' + minutes;
1442
1443 return hours + ":" + minutes + ":" + seconds;
1444 }
1445 /*
1446 * Take hh:mm:ss,ms or hh:mm:ss.ms input, return the number of seconds
1447 */
1448 function npt2seconds( npt_str ) {
1449 if( !npt_str ) {
1450 //js_log('npt2seconds:not valid ntp:'+ntp);
1451 return false;
1452 }
1453 // Strip "npt:" time definition if present
1454 npt_str = npt_str.replace( 'npt:', '' );
1455
1456 times = npt_str.split( ':' );
1457 if( times.length != 3 ){
1458 js_log( 'error: npt2seconds on ' + npt_str );
1459 return false;
1460 }
1461 // Sometimes a comma is used instead of period for ms
1462 times[2] = times[2].replace( /,\s?/, '.' );
1463 // Return seconds float
1464 return parseInt( times[0] * 3600) + parseInt( times[1] * 60 ) + parseFloat( times[2] );
1465 }
1466 /*
1467 * Simple helper to grab an edit token
1468 *
1469 * @param title The wiki page title you want to edit
1470 * @param api_url 'optional' The target API URL
1471 * @param callback The callback function to pass the token to
1472 */
1473 function get_mw_token( title, api_url, callback ) {
1474 js_log( ':get_mw_token:' );
1475 if( !title && wgUserName ) {
1476 title = 'User:' + wgUserName;
1477 }
1478 var reqObj = {
1479 'action': 'query',
1480 'prop': 'info',
1481 'intoken': 'edit',
1482 'titles': title
1483 };
1484 do_api_req( {
1485 'data': reqObj,
1486 'url' : api_url
1487 }, function(data) {
1488 for( var i in data.query.pages ) {
1489 if( data.query.pages[i]['edittoken'] ) {
1490 if( typeof callback == 'function' )
1491 callback ( data.query.pages[i]['edittoken'] );
1492 }
1493 }
1494 // No token found:
1495 return false;
1496 }
1497 );
1498 }
1499 // Do a remote or local API request based on request URL
1500 //@param options: url, data, cbParam, callback
1501 function do_api_req( options, callback ) {
1502 if( typeof options.data != 'object' ) {
1503 return js_error( 'Error: request paramaters must be an object' );
1504 }
1505 // Generate the URL if it's missing
1506 if( typeof options.url == 'undefined' || options.url === false ) {
1507 if( !wgServer || ! wgScriptPath ) {
1508 return js_error('Error: no api url for api request');
1509 }
1510 options.url = mwGetLocalApiUrl();
1511 }
1512 if( typeof options.data == 'undefined' )
1513 options.data = {};
1514
1515 // Force format to JSON
1516 options.data['format'] = 'json';
1517
1518 // If action is not set, assume query
1519 if( !options.data['action'] )
1520 options.data['action'] = 'query';
1521
1522 // js_log('do api req: ' + options.url +'?' + jQuery.param(options.data) );
1523 // Build request string
1524 if( parseUri( document.URL ).host == parseUri( options.url ).host ) {
1525 // Local request: do API request directly
1526 $j.ajax({
1527 type: "POST",
1528 url: options.url,
1529 data: options.data,
1530 dataType: 'json', // API requests _should_ always return JSON data:
1531 async: false,
1532 success: function( data ) {
1533 callback( data );
1534 },
1535 error: function( e ) {
1536 js_error( ' error' + e + ' in getting: ' + options.url );
1537 }
1538 });
1539 } else {
1540 // Remote request
1541 // Set the callback param if it's not already set
1542 if( typeof options.jsonCB == 'undefined' )
1543 options.jsonCB = 'callback';
1544
1545 var req_url = options.url;
1546 var paramAnd = ( req_url.indexOf( '?' ) == -1 ) ? '?' : '&';
1547 // Put all the parameters into the URL
1548 for( var i in options.data ) {
1549 req_url += paramAnd + encodeURIComponent( i ) + '=' + encodeURIComponent( options.data[i] );
1550 paramAnd = '&';
1551 }
1552 var fname = 'mycpfn_' + ( global_cb_count++ );
1553 _global[ fname ] = callback;
1554 req_url += '&' + options.jsonCB + '=' + fname;
1555 loadExternalJs( req_url );
1556 }
1557 }
1558 function mwGetLocalApiUrl( url ) {
1559 if ( wgServer && wgScriptPath ) {
1560 return wgServer + wgScriptPath + '/api.php';
1561 }
1562 return false;
1563 }
1564 // Grab wiki form error for wiki html page processing (should be deprecated because we use api now)
1565 function grabWikiFormError( result_page ) {
1566 var res = {};
1567 sp = result_page.indexOf( '<span class="error">' );
1568 if( sp != -1 ) {
1569 se = result_page.indexOf( '</span>', sp );
1570 res.error_txt = result_page.substr( sp, sp - se ) + '</span>';
1571 } else {
1572 // Look for warning
1573 sp = result_page.indexOf( '<ul class="warning">' )
1574 if( sp != -1 ) {
1575 se = result_page.indexOf( '</ul>', sp );
1576 res.error_txt = result_page.substr( sp, se - sp ) + '</ul>';
1577 // Try to add the ignore form item
1578 sfp = result_page.indexOf( '<form method="post"' );
1579 if( sfp != -1 ) {
1580 sfe = result_page.indexOf( '</form>', sfp );
1581 res.form_txt = result_page.substr( sfp, sfe - sfp ) + '</form>';
1582 }
1583 } else {
1584 // One more error type check
1585 sp = result_page.indexOf( 'class="mw-warning-with-logexcerpt">' )
1586 if( sp != -1 ) {
1587 se = result_page.indexOf( '</div>', sp );
1588 res.error_txt = result_page.substr( sp, se - sp ) + '</div>';
1589 }
1590 }
1591 }
1592 return res;
1593 }
1594 // Do a "normal" request
1595 function do_request( req_url, callback ) {
1596 js_log( 'do_request::req_url:' + req_url + ' != ' + parseUri( req_url ).host );
1597 // If we are doing a request to the same domain or relative link, do a normal GET
1598 if( parseUri( document.URL ).host == parseUri( req_url ).host ||
1599 req_url.indexOf('://') == -1 ) // Relative url
1600 {
1601 // Do a direct request
1602 $j.ajax({
1603 type: "GET",
1604 url: req_url,
1605 async: false,
1606 success: function( data ) {
1607 callback( data );
1608 }
1609 });
1610 } else {
1611 // Get data via DOM injection with callback
1612 global_req_cb.push( callback );
1613 // Prepend json_ to feed_format if not already requesting json format (metavid specific)
1614 if( req_url.indexOf( "feed_format=" ) != -1 && req_url.indexOf( "feed_format=json" ) == -1 )
1615 req_url = req_url.replace( /feed_format=/, 'feed_format=json_' );
1616 loadExternalJs( req_url + '&cb=mv_jsdata_cb&cb_inx=' + (global_req_cb.length - 1) );
1617 }
1618 }
1619
1620 function mv_jsdata_cb( response ) {
1621 js_log( 'f:mv_jsdata_cb:'+ response['cb_inx'] );
1622 // Run the callback from the global request callback object
1623 if( !global_req_cb[response['cb_inx']] ) {
1624 js_log( 'missing req cb index' );
1625 return false;
1626 }
1627 if( !response['pay_load'] ) {
1628 js_log( "missing pay load" );
1629 return false;
1630 }
1631 switch( response['content-type'] ) {
1632 case 'text/plain':
1633 break;
1634 case 'text/xml':
1635 if( typeof response['pay_load'] == 'string' ) {
1636 //js_log('load string:'+"\n"+ response['pay_load']);
1637 // Debugger;
1638 if( $j.browser.msie ) {
1639 // Attempt to parse as XML for IE
1640 var xmldata = new ActiveXObject("Microsoft.XMLDOM");
1641 xmldata.async = "false";
1642 xmldata.loadXML( response['pay_load'] );
1643 } else {
1644 // For others (Firefox, Safari etc.)
1645 try {
1646 var xmldata = (new DOMParser()).parseFromString( response['pay_load'], "text/xml" );
1647 } catch( e ) {
1648 js_log( 'XML parse ERROR: ' + e.message );
1649 }
1650 }
1651 //@@todo handle XML parser errors
1652 if( xmldata )response['pay_load'] = xmldata;
1653 }
1654 break
1655 default:
1656 js_log( 'bad response type' + response['content-type'] );
1657 return false;
1658 break;
1659 }
1660 global_req_cb[response['cb_inx']]( response['pay_load'] );
1661 }
1662 // Load external JS via DOM injection
1663 function loadExternalJs( url, callback ) {
1664 js_log( 'load js: '+ url );
1665 //if(window['$j']) // use jquery call:
1666 /*$j.ajax({
1667 type: "GET",
1668 url: url,
1669 dataType: 'script',
1670 cache: true
1671 });*/
1672 //else{
1673 var e = document.createElement( "script" );
1674 e.setAttribute( 'src', url );
1675 e.setAttribute( 'type', "text/javascript" );
1676 /*if(callback)
1677 e.onload = callback;
1678 */
1679 //e.setAttribute('defer', true);
1680 document.getElementsByTagName( "head" )[0].appendChild( e );
1681 // }
1682 }
1683 function styleSheetPresent( url ) {
1684 style_elements = document.getElementsByTagName( 'link' );
1685 if( style_elements.length > 0 ) {
1686 for( i = 0; i < style_elements.length; i++ ) {
1687 if( style_elements[i].href == url )
1688 return true;
1689 }
1690 }
1691 return false;
1692 }
1693 function loadExternalCss( url ) {
1694 // We could have the script loader group these CSS requests.
1695 // But it's debatable: it may hurt more than it helps with caching and all
1696 if( typeof url =='object' ) {
1697 for( var i in url ) {
1698 loadExternalCss( url[i] );
1699 }
1700 return ;
1701 }
1702
1703 if( url.indexOf('?') == -1 ) {
1704 url += '?' + getMvUniqueReqId();
1705 }
1706 if( !styleSheetPresent( url ) ) {
1707 js_log( 'load css: ' + url );
1708 var e = document.createElement( "link" );
1709 e.href = url;
1710 e.type = "text/css";
1711 e.rel = 'stylesheet';
1712 document.getElementsByTagName( "head" )[0].appendChild( e );
1713 }
1714 }
1715 function getMvEmbedURL() {
1716 if( _global['mv_embed_url'] )
1717 return _global['mv_embed_url'];
1718 var js_elements = document.getElementsByTagName( "script" );
1719 for( var i = 0; i < js_elements.length; i++ ) {
1720 // Check for mv_embed.js and/or script loader
1721 var src = js_elements[i].getAttribute( "src" );
1722 if( src ) {
1723 if( src.indexOf( 'mv_embed.js' ) != -1 || (
1724 ( src.indexOf( 'mwScriptLoader.php' ) != -1 || src.indexOf('jsScriptLoader.php') != -1 )
1725 && src.indexOf('mv_embed') != -1) ) //(check for class=mv_embed script_loader call)
1726 {
1727 _global['mv_embed_url'] = src;
1728 return src;
1729 }
1730 }
1731 }
1732 js_error( 'Error: getMvEmbedURL failed to get Embed Path' );
1733 return false;
1734 }
1735 // Get a unique request ID to ensure fresh JavaScript
1736 function getMvUniqueReqId() {
1737 if( _global['urid'] )
1738 return _global['urid'];
1739 var mv_embed_url = getMvEmbedURL();
1740 // If we have a URI, return it
1741 var urid = parseUri( mv_embed_url ).queryKey['urid']
1742 if( urid ) {
1743 _global['urid'] = urid;
1744 return urid;
1745 }
1746 // If we're in debug mode, get a fresh unique request key
1747 if( parseUri( mv_embed_url ).queryKey['debug'] == 'true' ) {
1748 var d = new Date();
1749 var urid = d.getTime();
1750 _global['urid'] = urid;
1751 return urid;
1752 }
1753 // Otherwise, just return the mv_embed version
1754 return MV_EMBED_VERSION;
1755 }
1756 /*
1757 * Set the global mv_embed path based on the script's location
1758 */
1759 function getMvEmbedPath() {
1760 if( _global['mv_embed_path'] )
1761 return _global['mv_embed_path'];
1762 var mv_embed_url = getMvEmbedURL();
1763 if( mv_embed_url.indexOf( 'mv_embed.js' ) !== -1 ) {
1764 mv_embed_path = mv_embed_url.substr( 0, mv_embed_url.indexOf( 'mv_embed.js' ) );
1765 } else if( mv_embed_url.indexOf( 'mwScriptLoader.php' ) !== -1 ) {
1766 // Script loader is in the root of MediaWiki, so include the default mv_embed extension path
1767 mv_embed_path = mv_embed_url.substr( 0, mv_embed_url.indexOf( 'mwScriptLoader.php' ) )
1768 + mediaWiki_mvEmbed_path;
1769 } else {
1770 mv_embed_path = mv_embed_url.substr( 0, mv_embed_url.indexOf( 'jsScriptLoader.php' ) );
1771 }
1772 // Make an absolute URL (if it's relative and we don't have an mv_embed path)
1773 if( mv_embed_path.indexOf( '://' ) == -1 ) {
1774 var pURL = parseUri( document.URL );
1775 if( mv_embed_path.charAt( 0 ) == '/' ) {
1776 mv_embed_path = pURL.protocol + '://' + pURL.authority + mv_embed_path;
1777 } else {
1778 // Relative
1779 if( mv_embed_path == '' ) {
1780 mv_embed_path = pURL.protocol + '://' + pURL.authority + pURL.directory + mv_embed_path;
1781 }
1782 }
1783 }
1784 _global['mv_embed_path'] = mv_embed_path;
1785 return mv_embed_path;
1786 }
1787
1788 if ( typeof DOMParser == "undefined" ) {
1789 DOMParser = function () {}
1790 DOMParser.prototype.parseFromString = function ( str, contentType ) {
1791 if ( typeof ActiveXObject != "undefined" ) {
1792 var d = new ActiveXObject( "MSXML.DomDocument" );
1793 d.loadXML( str );
1794 return d;
1795 } else if ( typeof XMLHttpRequest != "undefined" ) {
1796 var req = new XMLHttpRequest;
1797 req.open( "GET", "data:" + (contentType || "application/xml") +
1798 ";charset=utf-8," + encodeURIComponent(str), false );
1799 if ( req.overrideMimeType ) {
1800 req.overrideMimeType(contentType);
1801 }
1802 req.send( null );
1803 return req.responseXML;
1804 }
1805 }
1806 }
1807 /*
1808 * Utility functions
1809 */
1810 function js_log( string ) {
1811 if( window.console ) {
1812 window.console.log( string );
1813 } else {
1814 /*
1815 * IE and non-Firebug debug:
1816 */
1817 /*var log_elm = document.getElementById('mv_js_log');
1818 if(!log_elm){
1819 document.getElementsByTagName("body")[0].innerHTML = document.getElementsByTagName("body")[0].innerHTML +
1820 '<div style="position:absolute;z-index:500;top:0px;left:0px;right:0px;height:10px;">'+
1821 '<textarea id="mv_js_log" cols="120" rows="5"></textarea>'+
1822 '</div>';
1823
1824 var log_elm = document.getElementById('mv_js_log');
1825 }
1826 if(log_elm){
1827 log_elm.value+=string+"\n";
1828 }*/
1829 }
1830 return false;
1831 }
1832
1833 function js_error( string ) {
1834 alert( string );
1835 return false;
1836 }