3 * For details see: http://metavid.org/wiki/index.php/Mv_embed
5 * All Metavid Wiki code is released under the GPL2.
6 * For more information visit http://metavid.org/wiki/Code
8 * @url http://metavid.org
11 * http://stevenlevithan.com/demo/parseuri/js/
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)
17 // Fix multiple instances of mv_embed (i.e. include twice from two different servers)
19 if( MV_EMBED_VERSION
){
22 // Used to grab fresh copies of scripts.
23 var MV_EMBED_VERSION
= '1.0r20';
26 * Configuration variables should be set by extending mwConfigOptions
27 * here is the default config:
29 var mwDefaultConfig
= {
31 'jui_skin': 'redmond',
32 'video_size':'400x300'
35 // (We install the default config values for anything not set in mwConfig once we know we have jquery)
40 // (c) Steven Levithan <stevenlevithan.com>
42 function parseUri (str
) {
43 var o
= parseUri
.options
,
44 m
= o
.parser
[o
.strictMode
? "strict" : "loose"].exec(str
),
48 while (i
--) uri
[o
.key
[i
]] = m
[i
] || "";
51 uri
[o
.key
[12]].replace(o
.q
.parser
, function ($0, $1, $2) {
52 if ($1) uri
[o
.q
.name
][$1] = $2;
59 key
: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
62 parser
: /(?:^|&)([^&=]*)=?([^&]*)/g
65 strict
: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
66 loose
: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
70 // For use when mv_embed with script-loader is in the root MediaWiki path
71 var mediaWiki_mvEmbed_path
= 'js2/mwEmbed/';
73 var _global
= this; // Global obj (depreciate use window)
76 * setup the empty global $mw object
77 * will ensure all our functions are properly namespaced
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
89 // Get the mv_embed location if it has not been set
90 if( !mv_embed_path
) {
91 var mv_embed_path
= getMvEmbedPath();
94 * wrap the global $mw object here:
96 * Any global functions/classes that are not jQuery plugins should make
97 * there way into the $mw namespace
101 * Language classes $mw.lang
103 * Localized Language support attempts to mirror the functionality of Language.php in MediaWiki
104 * It contains methods for loading and transforming msg text
109 * Setup the lang object
116 * Loads a set of json messages into the lng object.
118 * @param json msgSet The set of msgs to be loaded
120 $.lang
.loadGM = function( msgSet
){
121 for( var i
in msgSet
) {
122 gMsg
[ i
] = msgSet
[i
];
127 * Loads a ruleset by given template key ie PLURAL : { //ruleSetObj }
129 * @param json ruleSet The ruleset object ( extends gRuleSet )
131 $.lang
.loadRS = function( ruleSet
){
132 for( var i
in ruleSet
){
133 gRuleSet
[ i
] = ruleSet
[ i
];
138 * Returns a transformed msg string
140 * it take a msg key and array of replacement values of form
141 * $1, $2 and does relevant msgkey transformation returning
144 * @param string key The msg key as set by loadGm
145 * @param [mixed] args An array of replacement strings
148 $.lang
.gM = function( key
, args
) {
150 return '<' + key
+ '>';// Missing key placeholder
152 //swap in the arg values
153 var ms
= $.lang
.gMsgSwap( key
, args
) ;
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){
162 //make sure we have the lagMagic setup:
164 //send the msg key through the parser
165 pObj
= $.parser
.pNew( ms
);
166 //return the transformed msg
167 return pObj
.getHTML();
172 * @param string key The msg key as set by loadGm
173 * @param [mixed] args An array or string to be replaced
176 $.lang
.gMsgSwap = function( key
, args
){
178 return '<' + key
+ '>';// Missing key placeholder
179 //get the messege string:
180 var ms
= gMsg
[ key
];
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
] );
189 } else if( typeof args
=='string' || typeof args
=='number' ) {
190 ms
= ms
.replace(/\$1/g, args
);
198 * @returns string The msg key without transforming it
200 $.lang
.gMsgNoTrans = function( key
){
204 // Missing key placeholder
205 return '<' + key
+ '>';
208 * Add Supported Magic Words to parser
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
218 $.lang
.doneSetup
= true;
223 * Process the PLURAL special language template key:
225 $.lang
.procPLURAL = function( tObj
){
227 // (gRuleSet is loaded from script-loader to contains local ruleset)
228 var rs
= gRuleSet
['PLURAL'];
231 * Plural matchRuleTest
233 function matchRuleTest(cRule
, val
){
234 js_log("matchRuleTest:: " + typeof cRule
+ ' ' + cRule
+ ' == ' + val
);
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] )
244 //else do a direct compare
250 //check for simple cRule type:
251 if( typeof cRule
== 'number'){
252 return ( parseInt( val
) == parseInt( cRule
) );
253 }else if( typeof cRule
== 'object' ){
255 //if a list we need to match all for rule match
267 if( checkValue( val
% cr
['mod'], cr
['is'] ) )
269 }else if( cr
['not']){
270 if( ! checkValue( val
% cr
['mod'], cr
['not'] ) )
276 //check all the matches (taking into consideration "or" order)
286 * Maps a given rule Index to template params:
288 * if index is out of range return last param
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
];
299 //run the actual rule lookup:
300 for(var ruleInx
in rs
){
302 if( matchRuleTest( cRule
, tObj
.arg
) ){
303 js_log("matched rule: " + ruleInx
);
304 return getTempParamFromRuleInx(tObj
, rCount
);
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 ];
314 * gMsgLoadRemote loads remote msg strings
316 * @param mixed msgSet the set of msg to load remotely
317 * @param function callback the callback to issue once string is ready
319 $.lang
.gMsgLoadRemote = function( msgSet
, callback
) {
321 if( typeof msgSet
== 'object' ) {
322 for( var i
in msgSet
) {
323 ammessages
+= msgSet
[i
] + '|';
325 } else if( typeof msgSet
== 'string' ) {
326 ammessages
+= msgSet
;
328 if( ammessages
== '' ) {
329 js_log( 'gMsgLoadRemote: no message set requested' );
334 'meta': 'allmessages',
335 'ammessages': ammessages
337 }, function( data
) {
338 if( data
.query
.allmessages
) {
339 var msgs
= data
.query
.allmessages
;
340 for( var i
in msgs
) {
342 ld
[ msgs
[i
]['name'] ] = msgs
[i
]['*'];
350 * Format a size in bytes for output, using an appropriate
351 * unit (B, KB, MB or GB) according to the magnitude in question
353 * @param size Size to format
354 * @return string Plain text (not HTML)
356 $.lang
.formatSize = function ( size
) {
357 // For small sizes no decimal places are necessary
364 // For MB and bigger two decimal places are smarter
368 msg
= 'mwe-size-gigabytes';
370 msg
= 'mwe-size-megabytes';
373 msg
= 'mwe-size-kilobytes';
376 msg
= 'mwe-size-bytes';
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
);
387 * MediaWiki wikitext "Parser"
389 * This is not feature complete but we need a way to get at template properties
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
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
405 * @param object magicSet key:callback
407 $.parser
.addMagic = function( magicSet
){
408 for(var i
in magicSet
)
409 pMagicSet
[ i
] = magicSet
[i
];
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
)
417 parseObj
.prototype = {
418 //the wikiText "DOM"... stores the parsed wikiText structure
419 //wtDOM : {}, (not yet supported )
421 pOut
: '', //the parser output string container
422 init :function( wikiText
){
423 this.wikiText
= wikiText
;
425 updateText : function( wikiText
){
426 this.wikiText
= wikiText
;
427 //invalidate the output (will force a reparse)
432 this.pObj
.tmpl
= new Array();
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
439 //get templates losly based on Magnus_Manske/tmpl.js code:
446 //build out nested template holders:
452 * quickly recursive / parse out templates with top down recurse decent
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
){
459 //if we should output node text
462 for(var a
=0; a
< txt
.length
; a
++){
463 if( txt
[a
] == '{' && txt
[a
+1] == '{' ){
467 node
['c'] = new Array();
469 node
['c'].push( rdpp( txt
.substr( a
) ) );
471 }else if( txt
[a
] == '}' && txt
[a
+1] == '}'){
488 * parse template text as template name and named params
490 function parseTmplTxt( ts
){
493 tname
= ts
.split('\|').shift() ;
494 tname
= tname
.split('\{').shift() ;
495 tname
= tname
.replace( /^\s+|\s+$/g, "" ); //trim
497 //check for arguments:
498 if( tname
.split(':').length
== 1 ){
499 tObj
["name"] = tname
;
501 tObj
["name"] = tname
.split(':').shift();
502 tObj
["arg"] = tname
.split(':').pop();
505 js_log("TNAME::" + tObj
["arg"] + ' from:: ' + ts
);
507 var pSet
= ts
.split('\|');
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]!='\\'){
516 tObj
.param
[ tStr
.split('=').shift() ] = tStr
.split('=').pop();
519 tObj
.param
[ pInx
] = tStr
;
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
);
533 //don't swap just return text
540 * go last child first swap upward: (could probably be integrated above somehow)
543 function recurse_magic_swap( node
){
549 for(var i
in node
['c']){
550 var nt
= recurse_magic_swap( node
['c'][i
] );
551 //swap it into current
553 node
.t
= node
.t
.replace( node
['c'][i
].t
, nt
);
556 pNode
.t
= pNode
.t
.replace( node
['c'][i
].t
, nt
);
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
564 //node.t = getMagicFromTempObj( node.t )
565 return getMagicTxtFromTempNode( node
);
568 //get text node system:
569 var node
= rdpp ( this.wikiText
);
573 this.pOut
= recurse_magic_swap( node
);
577 * Returns the transformed wikitext
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)
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
588 getHTML : function(){
589 //wikiText updates should invalidate pOut
590 if( this.pOut
== ''){
596 //return the parserObj
597 return new parseObj( wikiText
, opt
) ;
601 //setup legacy global shortcuts:
602 var loadGM
= $mw
.lang
.loadGM
;
603 var gM
= $mw
.lang
.gM
;
605 //if some no-js2 script defined and loaded gMsg in global space:
606 if( _global
['gMsg'] ){
607 loadGM( _global
['gMsg'] );
610 // All default messages in [English] should be overwritten by the CMS language message system.
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"
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
631 if( typeof mvAutoLoadClasses
== 'undefined' )
632 mvAutoLoadClasses
= {};
634 // The script that loads the class set
635 function lcPaths( classSet
){
636 for( var i
in classSet
) {
637 mvAutoLoadClasses
[i
] = classSet
[i
];
641 function mvGetClassPath(k
){
642 if( mvAutoLoadClasses
[k
] ) {
643 //js_log('got class path:' + k + ' : '+ mvClassPaths[k]);
644 return mvAutoLoadClasses
[k
];
646 return js_error('could not find path for requested class ' + k
);
649 if( typeof mvCssPaths
== 'undefined' )
652 function lcCssPath( cssSet
) {
653 for( var i
in cssSet
) {
654 mvCssPaths
[i
] = mv_embed_path
+ cssSet
[i
];
659 * -- Load Class Paths --
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)
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.
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.
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",
686 "ctrlBuilder" : "skins/ctrlBuilder.js",
687 "kskinConfig" : "skins/kskin/kskin.js",
688 "mvpcfConfig" : "skins/mvpcf/mvpcf.js",
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",
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",
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",
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",
732 "mvClipEdit" : "libClipEdit/mvClipEdit.js",
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",
743 "mvPlayList" : "libSequencer/mvPlayList.js",
744 "mvSequencer" : "libSequencer/mvSequencer.js",
745 "mvFirefoggRender" : "libSequencer/mvFirefoggRender.js",
746 "mvTimedEffectsEdit": "libSequencer/mvTimedEffectsEdit.js",
748 "mvTextInterface" : "libTimedText/mvTextInterface.js"
751 // Dependency mapping for CSS files for self-contained included plugins:
753 '$j.Jcrop' : 'libClipEdit/Jcrop/css/jquery.Jcrop.css',
754 '$j.fn.ColorPicker' : 'libClipEdit/colorpicker/css/colorpicker.css'
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>';
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') +
774 * mvJsLoader class handles initialization and js file loads
778 function mwLoad( loadSet
, callback
) {
779 mvJsLoader
.doLoad( loadSet
, callback
);
786 onReadyEvents
: new Array(),
787 doneReadyEvents
: false,
788 jQuerySetupFlag
: false,
790 // To keep consistency across threads
794 load_error
: false, // Load error flag (false by default)
796 callbacks
: new Array(),
799 doLoad: function( loadLibs
, callback
) {
802 if( loadLibs
&& loadLibs
.length
!= 0 ) {
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;
812 if( all_libs_loaded
) {
813 //js_log( 'Libraries ( ' + loadLibs + ') already loaded... skipping load request' );
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
] ] );
824 // Check if we should use the script loader to combine all the requests into one
825 if( typeof mwSlScript
!= 'undefined' ) {
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
;
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
)
843 var scriptPath
= puri
.protocol
+ '://' + puri
.authority
+ puri
.path
;
845 var scriptPath
= puri
.path
;
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
;
854 for( var i
= 0; i
< loadLibs
.length
; i
++ ) {
855 var curLib
= loadLibs
[i
];
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();
867 this.callbacks
.push( callback
);
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
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;
877 setTimeout( 'mvJsLoader.doLoad()', 20 );
880 //js_log('checkLoading passed. Running callbacks...');
881 // Only do callbacks if we are in the same instance (weird concurrency issue)
883 for( var i
= 0; i
< this.callbacks
.length
; i
++ )
885 //js_log('RESET LIBS: loading is: '+ loading + ' callback count: '+cb_count +
886 // ' p:'+ this.ptime +' c:'+ this.ctime);
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);
898 // Re-issue doLoad ( ptime will be set to ctime so we should catch up)
899 setTimeout( 'mvJsLoader.doLoad()', 25 );
904 this.ptime
= this.ctime
;
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
) {
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
] );
915 mvJsLoader
.doLoad( loadSet
, callback
);
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
);
927 checkLoading: function() {
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
] );
936 //js_log("has not yet loaded: " + i);
942 checkObjPath: function( libVar
) {
945 var objPath
= libVar
.split( '.' )
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
;
955 this.cur_path
= cur_path
;
959 * checks for jQuery and adds the $j noConflict var
961 jQueryCheck: function( callback
) {
962 //js_log( 'jQueryCheck::' );
963 // Skip stuff if $j is already loaded:
964 if( _global
['$j'] && callback
)
971 //only do the $j setup once:
973 _global
['$j'] = jQuery
.noConflict();
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
);
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';
985 //setup skin dependent dependencies
986 lcCssPath({'embedVideo' : 'skins/' + mwConfig
['skin_name'] + '/playerSkin.css'});
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' );
992 // Set up AJAX to not send dynamic URLs for loading scripts (we control that with
998 js_log( 'jQuery loaded into $j' );
999 // Set up mvEmbed jQuery bindings and config based dependencies
1000 mv_jqueryBindings();
1001 _this
.jQuerySetupFlag
= true;
1009 embedVideoCheck:function( callback
) {
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)
1029 if( mwConfig
['skin_name'] )
1030 depReq
[0].push( mwConfig
['skin_name'] + 'Config' );
1032 // Add PNG fix if needed:
1033 if( $j
.browser
.msie
|| $j
.browser
.version
< 7 )
1034 depReq
[0].push( '$j.fn.pngFix' );
1036 _this
.doLoadDepMode( depReq
, function() {
1039 $j('.videonojs').remove();
1043 addLoadEvent: function( fn
) {
1044 this.onReadyEvents
.push( fn
);
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() {
1050 this.doneReadyEvents
= true;
1051 this.jQueryCheck( function() {
1052 _this
.runReadyEvents();
1055 runReadyEvents: function() {
1056 js_log( "runReadyEvents" );
1057 while( this.onReadyEvents
.length
) {
1058 this.onReadyEvents
.shift()();
1063 // Load an external JS file. Similar to jquery .require plugin,
1064 // but checks for object availability rather than load state.
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..." );
1079 mv_init_done
= true;
1080 // Handle the execution of queued functions with jQuery "ready"
1082 // Check if this page has a video or playlist
1084 document
.getElementsByTagName( "video" ),
1085 document
.getElementsByTagName( "audio" ),
1086 document
.getElementsByTagName( "playlist" )
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();
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();
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
) {
1108 //for(var j = 0; i < j.length; j++){
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() {
1120 mvJsLoader
.addLoadEvent( func
);
1123 // Deprecated mwAddOnloadHook in favor of js2 naming (for clear separation of js2 code from old MW code
1124 var mwAddOnloadHook
= js2AddOnloadHook
;
1126 * This function allows for targeted rewriting
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
);
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 );
1144 // Use the onload method instead when DOMContentLoaded does not exist
1145 window
.onload = function() { mwdomReady() };
1148 * Should deprecate and use jquery.ui.dialog instead
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;">' +
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
);
1162 function mv_remove_modal( speed
) {
1163 $j( '#modalbox,#mv_overlay' ).remove( speed
);
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
1171 * @@ eventually we should refactor mwCode over to jQuery style plugins
1172 * and mv_embed.js will just handle dependency mapping and loading.
1175 function mv_jqueryBindings() {
1176 js_log( 'mv_jqueryBindings' );
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' ) );
1182 iObj
['target_invocation'] = this.selector
;
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',
1202 iObj
['instance_name'] = 'rsdMVRS';
1203 _global
['rsdMVRS'] = new remoteSearchDriver( iObj
);
1205 callback( _global
['rsdMVRS'] );
1210 $.fn
.sequencer = function( iObj
, callback
) {
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([
1225 '$j.secureEvalJSON',
1233 '$j.ui.progressbar',
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
);
1246 js_log( 'mvSeq already init' );
1252 * The Firefogg jQuery function:
1253 * @@note This Firefogg invocation could be made to work more like real jQuery plugins
1255 $.fn
.firefogg = function( iObj
, callback
) {
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' );
1262 // Check if we already have Firefogg loaded (the call just updates the element's
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" );
1271 // Update properties
1272 for( var i
in iObj
) {
1273 js_log( "firefogg::updated: " + i
+ ' to '+ iObj
[i
] );
1274 sElm
['firefogg'][i
] = iObj
[i
];
1276 return sElm
['firefogg'];
1279 sElm
['firefogg'] = 'loading';
1282 iObj
['selector'] = this.selector
;
1286 'mvBaseUploadInterface',
1291 '$j.ui.progressbar',
1296 if( iObj
.encoder_interface
) {
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
);
1313 var myFogg
= new mvFirefogg( iObj
);
1316 myFogg
.doRewrite( callback
);
1317 var selectorElement
= $j( iObj
.selector
).get( 0 );
1318 selectorElement
['firefogg'] = myFogg
;
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" );
1331 // Call or update the property:
1333 sElm
['fogg_render'] = 'loading';
1335 iObj
['player_target'] = this.selector
;
1340 sElm
['fogg_render'] = new mvFirefoggRender( iObj
);
1341 if( callback
&& typeof callback
== 'function' )
1342 callback( sElm
['fogg_render'] );
1346 $.fn
.baseUploadInterface = function(iObj
) {
1347 mvJsLoader
.doLoadDepMode([
1349 'mvBaseUploadInterface',
1353 '$j.ui.progressbar',
1357 myUp
= new mvBaseUploadInterface( iObj
);
1362 // Shortcut to a themed button
1363 $.btnHtml = function( msg
, className
, iconId
, 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>';
1374 // Shortcut to bind hover state
1375 $.fn
.btnBind = function() {
1378 $j( this ).addClass( 'ui-state-hover' );
1381 $j( this ).removeClass( 'ui-state-hover' );
1390 * Utility functions:
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
+ '?';
1398 var new_url
= pSrc
.path
+ '?';
1401 for( var key
in pSrc
.queryKey
) {
1402 var val
= pSrc
.queryKey
[ key
];
1403 // Do override if requested
1406 new_url
+= amp
+ key
+ '=' + val
;
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
];
1419 * Given a float number of seconds, returns npt format response.
1421 * @param float Seconds
1422 * @param boolean If we should show milliseconds or not.
1424 function seconds2npt( sec
, show_ms
) {
1425 if( isNaN( sec
) ) {
1426 // js_log("warning: trying to get npt time on NaN:" + sec);
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
1434 seconds
= Math
.round( seconds
* 1000 ) / 1000;
1436 seconds
= Math
.round( seconds
);
1439 seconds
= '0' + seconds
;
1441 minutes
= '0' + minutes
;
1443 return hours
+ ":" + minutes
+ ":" + seconds
;
1446 * Take hh:mm:ss,ms or hh:mm:ss.ms input, return the number of seconds
1448 function npt2seconds( npt_str
) {
1450 //js_log('npt2seconds:not valid ntp:'+ntp);
1453 // Strip "npt:" time definition if present
1454 npt_str
= npt_str
.replace( 'npt:', '' );
1456 times
= npt_str
.split( ':' );
1457 if( times
.length
!= 3 ){
1458 js_log( 'error: npt2seconds on ' + npt_str
);
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] );
1467 * Simple helper to grab an edit token
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
1473 function get_mw_token( title
, api_url
, callback
) {
1474 js_log( ':get_mw_token:' );
1475 if( !title
&& wgUserName
) {
1476 title
= 'User:' + wgUserName
;
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'] );
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' );
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');
1510 options
.url
= mwGetLocalApiUrl();
1512 if( typeof options
.data
== 'undefined' )
1515 // Force format to JSON
1516 options
.data
['format'] = 'json';
1518 // If action is not set, assume query
1519 if( !options
.data
['action'] )
1520 options
.data
['action'] = 'query';
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
1530 dataType
: 'json', // API requests _should_ always return JSON data:
1532 success: function( data
) {
1535 error: function( e
) {
1536 js_error( ' error' + e
+ ' in getting: ' + options
.url
);
1541 // Set the callback param if it's not already set
1542 if( typeof options
.jsonCB
== 'undefined' )
1543 options
.jsonCB
= 'callback';
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
] );
1552 var fname
= 'mycpfn_' + ( global_cb_count
++ );
1553 _global
[ fname
] = callback
;
1554 req_url
+= '&' + options
.jsonCB
+ '=' + fname
;
1555 loadExternalJs( req_url
);
1558 function mwGetLocalApiUrl( url
) {
1559 if ( wgServer
&& wgScriptPath
) {
1560 return wgServer
+ wgScriptPath
+ '/api.php';
1564 // Grab wiki form error for wiki html page processing (should be deprecated because we use api now)
1565 function grabWikiFormError( result_page
) {
1567 sp
= result_page
.indexOf( '<span class="error">' );
1569 se
= result_page
.indexOf( '</span>', sp
);
1570 res
.error_txt
= result_page
.substr( sp
, sp
- se
) + '</span>';
1573 sp
= result_page
.indexOf( '<ul class="warning">' )
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"' );
1580 sfe
= result_page
.indexOf( '</form>', sfp
);
1581 res
.form_txt
= result_page
.substr( sfp
, sfe
- sfp
) + '</form>';
1584 // One more error type check
1585 sp
= result_page
.indexOf( 'class="mw-warning-with-logexcerpt">' )
1587 se
= result_page
.indexOf( '</div>', sp
);
1588 res
.error_txt
= result_page
.substr( sp
, se
- sp
) + '</div>';
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
1601 // Do a direct request
1606 success: function( data
) {
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) );
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' );
1627 if( !response
['pay_load'] ) {
1628 js_log( "missing pay load" );
1631 switch( response
['content-type'] ) {
1635 if( typeof response
['pay_load'] == 'string' ) {
1636 //js_log('load string:'+"\n"+ response['pay_load']);
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'] );
1644 // For others (Firefox, Safari etc.)
1646 var xmldata
= (new DOMParser()).parseFromString( response
['pay_load'], "text/xml" );
1648 js_log( 'XML parse ERROR: ' + e
.message
);
1651 //@@todo handle XML parser errors
1652 if( xmldata
)response
['pay_load'] = xmldata
;
1656 js_log( 'bad response type' + response
['content-type'] );
1660 global_req_cb
[response
['cb_inx']]( response
['pay_load'] );
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:
1673 var e
= document
.createElement( "script" );
1674 e
.setAttribute( 'src', url
);
1675 e
.setAttribute( 'type', "text/javascript" );
1677 e.onload = callback;
1679 //e.setAttribute('defer', true);
1680 document
.getElementsByTagName( "head" )[0].appendChild( e
);
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
)
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
] );
1703 if( url
.indexOf('?') == -1 ) {
1704 url
+= '?' + getMvUniqueReqId();
1706 if( !styleSheetPresent( url
) ) {
1707 js_log( 'load css: ' + url
);
1708 var e
= document
.createElement( "link" );
1710 e
.type
= "text/css";
1711 e
.rel
= 'stylesheet';
1712 document
.getElementsByTagName( "head" )[0].appendChild( e
);
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" );
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)
1727 _global
['mv_embed_url'] = src
;
1732 js_error( 'Error: getMvEmbedURL failed to get Embed Path' );
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']
1743 _global
['urid'] = urid
;
1746 // If we're in debug mode, get a fresh unique request key
1747 if( parseUri( mv_embed_url
).queryKey
['debug'] == 'true' ) {
1749 var urid
= d
.getTime();
1750 _global
['urid'] = urid
;
1753 // Otherwise, just return the mv_embed version
1754 return MV_EMBED_VERSION
;
1757 * Set the global mv_embed path based on the script's location
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
;
1770 mv_embed_path
= mv_embed_url
.substr( 0, mv_embed_url
.indexOf( 'jsScriptLoader.php' ) );
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
;
1779 if( mv_embed_path
== '' ) {
1780 mv_embed_path
= pURL
.protocol
+ '://' + pURL
.authority
+ pURL
.directory
+ mv_embed_path
;
1784 _global
['mv_embed_path'] = mv_embed_path
;
1785 return mv_embed_path
;
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" );
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
);
1803 return req
.responseXML
;
1810 function js_log( string
) {
1811 if( window
.console
) {
1812 window
.console
.log( string
);
1815 * IE and non-Firebug debug:
1817 /*var log_elm = document.getElementById('mv_js_log');
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>'+
1824 var log_elm = document.getElementById('mv_js_log');
1827 log_elm.value+=string+"\n";
1833 function js_error( string
) {