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