Bug 20489 Configure illegal file characters https://bugzilla.wikimedia.org/show_bug...
[mediawiki.git] / js2 / mwEmbed / mv_embed.js
blob9fb5584917325b31eeb2b9c85d828d4b58c2719d
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 info visit http://metavid.org/wiki/Code
7  *
8  * @url http://metavid.org
9  *
10  * parseUri:
11  * http://stevenlevithan.com/demo/parseuri/js/
12  *
13  * config values you can manually set the location of the mv_embed folder here
14  * (in cases where media will be hosted in a different place than the embedding page)
15  *
16  */
17 //fix multiple instances of mv_embed (ie include twice from two different servers)
18 var MV_DO_INIT=true;
19 if( MV_EMBED_VERSION ){
20         MV_DO_INIT=false;
22 //used to grab fresh copies of scripts. (should be changed on commit)
23 var MV_EMBED_VERSION = '1.0r19';
26  * Configuration variables (can be set from some precceding script)
27  * set up mwConfig global overide any of the defaultMwConfig values:
28  * @@ more config valuse on the way ;)
29  */
30 var defaultMwConfig = {
31         'skin_name': 'mvpcf',
32         'jui_skin': 'redmond',
33         'video_size':'400x300'
36 if(!mwConfig)
37           var mwConfig = {};
39 //install the default config values for anything not set in  mwConfig
40 checkDefaultMwConfig();
42 //whether or not to load java from an iframe.
43 //note: this is necessary for remote embedding because of java security model)
44 if(!mv_java_iframe)
45         var mv_java_iframe = true;
47 //for when useing mv_embed with script-loader in root mediawiki path
48 var mediaWiki_mvEmbed_path = 'js2/mwEmbed/';
50 var global_player_list = new Array(); //the global player list per page
51 var global_req_cb = new Array(); //the global request callback array
52 var _global = this; //global obj
53 var mv_init_done = false;
54 var global_cb_count =0;
56 /*parseUri class parses URIs:*/
57 var parseUri=function(d){var o=parseUri.options,value=o.parser[o.strictMode?"strict":"loose"].exec(d);for(var i=0,uri={};i<14;i++){uri[o.key[i]]=value[i]||""}uri[o.q.name]={};uri[o.key[12]].replace(o.q.parser,function(a,b,c){if(b)uri[o.q.name][b]=c});return uri};parseUri.options={strictMode:false,key:["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],q:{name:"queryKey",parser:/(?:^|&)([^&=]*)=?([^&]*)/g},parser:{strict:/^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,loose:/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/}};
60 //get mv_embed location if it has not been set
61 if( !mv_embed_path ){
62         var mv_embed_path = getMvEmbedPath();
65 //setup the skin path:
66 var mv_jquery_skin_path = mv_embed_path + 'jquery/jquery.ui/themes/' +mwConfig['jui_skin'] + '/';
67 var mv_skin_img_path = mv_embed_path + 'skins/' + mwConfig['skin_name'] + '/images/';
68 var mv_default_thumb_url = mv_skin_img_path + 'vid_default_thumb.jpg';
71 //init the global Msg if not already
72 if(!gMsg){var gMsg={};}
74 //laguage msg loader:
75 function loadGM( msgSet ){
76         for(var i in msgSet){
77                 gMsg[ i ] = msgSet[i];
78         }
81 //all default msg in [English] should be overwritten by the CMS language msg system.
82 loadGM({
83         "mwe-loading_txt" : "loading <blink>...<\/blink>",
84         "mwe-loading_title" : "Loading...",
85         "mwe-size-gigabytes" : "$1 GB",
86         "mwe-size-megabytes" : "$1 MB",
87         "mwe-size-kilobytes" : "$1 K",
88         "mwe-size-bytes" : "$1 B"
89 });
91 /**
92  * AutoLoader paths (this should mirror the file: jsAutoloadLocalClasses.php )
93  * any file _not_ listed here won't be auto-loadable
94  * @path the path to the file (or set of files) with ending slash
95  * @gClasses the set of classes
96  *              if an array $j.className become jquery.className.js
97  *              if an asssociative object then key => value paris are used
98  */
99 if(typeof mvAutoLoadClasses == 'undefined')
100         mvAutoLoadClasses = {};
102 //the script that loads the classet
103 function lcPaths( classSet){
104         for(var i in classSet){
105                 mvAutoLoadClasses[i] = classSet[i];
106         }
109 function mvGetClassPath(k){
110         if( mvAutoLoadClasses[k] ){
111                 //js_log('got classpath:' + k +  ' : '+ mvClassPaths[k]);
112                 return mvAutoLoadClasses[k];
113         }else{
114                 return js_error('could not find path for requested class ' + k );
115         }
117 if(typeof mvCssPaths == 'undefined')
118         mvCssPaths = {};
120 function lcCssPath(cssSet){
121         for(var i in cssSet){
122                 mvCssPaths[i]= mv_embed_path + cssSet[i];
123         }
127  * --  Load Class Paths --
129  * MUST BE VALID JSON (NOT JS)
130  * is used by the scriptloader to autoLoad classes (so we only define this once for php & javascript)
132  * this is more verbose than earlier version that compressed paths
133  * but its all good gziping help compress repetative path strings
134  * grouped by
136  * right now php AutoLoader only reads this mv_embed.js file
137  * in the future we could have multiple lcPath calls that php reads
138  * (if our autoloading class list becomes too long) just have to add thouse
139  * files to the jsAutoLoader file list.
140  */
141 lcPaths({
142         "mv_embed"                      : "mv_embed.js",
143         "window.jQuery"         : "jquery/jquery-1.3.2.js",
144         "$j.fn.pngFix"          : "jquery/plugins/jquery.pngFix.js",
145         "$j.fn.autocomplete": "jquery/plugins/jquery.autocomplete.js",
146         "$j.fn.hoverIntent"     : "jquery/plugins/jquery.hoverIntent.js",
147         "$j.fn.datePicker"      : "jquery/plugins/jquery.datePicker.js",
148         "$j.ui"                         : "jquery/jquery.ui/ui/ui.core.js",
149         "$j.fn.ColorPicker"     : "libClipEdit/colorpicker/js/colorpicker.js",
150         "$j.Jcrop"                      : "libClipEdit/Jcrop/js/jquery.Jcrop.js",
151         "$j.fn.simpleUploadForm": "libAddMedia/simpleUploadForm.js",
153         "ctrlBuilder"   : "skins/ctrlBuilder.js",
154         "kskin"                 : "skins/kskin/kskin.js",
155         "mvpcf"                 : "skins/mvpcf/mvpcf.js",
157         "$j.secureEvalJSON"     : "jquery/plugins/jquery.secureEvalJSON.js",
158         "$j.cookie"                     : "jquery/plugins/jquery.cookie.js",
159         "$j.contextMenu"        : "jquery/plugins/jquery.contextMenu.js",
161         "$j.effects.blind"              : "jquery/jquery.ui/ui/effects.blind.js",
162         "$j.effects.drop"               : "jquery/jquery.ui/ui/effects.drop.js",
163         "$j.effects.pulsate"    : "jquery/jquery.ui/ui/effects.pulsate.js",
164         "$j.effects.transfer"   : "jquery/jquery.ui/ui/effects.transfer.js",
165         "$j.ui.droppable"               : "jquery/jquery.ui/ui/ui.droppable.js",
166         "$j.ui.slider"                  : "jquery/jquery.ui/ui/ui.slider.js",
167         "$j.effects.bounce"             : "jquery/jquery.ui/ui/effects.bounce.js",
168         "$j.effects.explode"    : "jquery/jquery.ui/ui/effects.explode.js",
169         "$j.effects.scale"              : "jquery/jquery.ui/ui/effects.scale.js",
170         "$j.ui.datepicker"              : "jquery/jquery.ui/ui/ui.datepicker.js",
171         "$j.ui.progressbar"             : "jquery/jquery.ui/ui/ui.progressbar.js",
172         "$j.ui.sortable"                : "jquery/jquery.ui/ui/ui.sortable.js",
173         "$j.effects.clip"               : "jquery/jquery.ui/ui/effects.clip.js",
174         "$j.effects.fold"               : "jquery/jquery.ui/ui/effects.fold.js",
175         "$j.effects.shake"              : "jquery/jquery.ui/ui/effects.shake.js",
176         "$j.ui.dialog"                  : "jquery/jquery.ui/ui/ui.dialog.js",
177         "$j.ui.resizable"               : "jquery/jquery.ui/ui/ui.resizable.js",
178         "$j.ui.tabs"                    : "jquery/jquery.ui/ui/ui.tabs.js",
179         "$j.effects.core"               : "jquery/jquery.ui/ui/effects.core.js",
180         "$j.effects.highlight"  : "jquery/jquery.ui/ui/effects.highlight.js",
181         "$j.effects.slide"              : "jquery/jquery.ui/ui/effects.slide.js",
182         "$j.ui.accordion"               : "jquery/jquery.ui/ui/ui.accordion.js",
183         "$j.ui.draggable"               : "jquery/jquery.ui/ui/ui.draggable.js",
184         "$j.ui.selectable"              : "jquery/jquery.ui/ui/ui.selectable.js",
186         "mvFirefogg"                    : "libAddMedia/mvFirefogg.js",
187         "mvAdvFirefogg"                 : "libAddMedia/mvAdvFirefogg.js",
188     "mvBaseUploadInterface"     : "libAddMedia/mvBaseUploadInterface.js",
189         "remoteSearchDriver"    : "libAddMedia/remoteSearchDriver.js",
190         "seqRemoteSearchDriver" : "libAddMedia/seqRemoteSearchDriver.js",
192         "baseRemoteSearch"              : "libAddMedia/searchLibs/baseRemoteSearch.js",
193         "mediaWikiSearch"               : "libAddMedia/searchLibs/mediaWikiSearch.js",
194         "metavidSearch"                 : "libAddMedia/searchLibs/metavidSearch.js",
195         "archiveOrgSearch"              : "libAddMedia/searchLibs/archiveOrgSearch.js",
196         "baseRemoteSearch"              : "libAddMedia/searchLibs/baseRemoteSearch.js",
198         "mvClipEdit"                    : "libClipEdit/mvClipEdit.js",
200         "embedVideo"            : "libEmbedVideo/embedVideo.js",
201         "flashEmbed"            : "libEmbedVideo/flashEmbed.js",
202         "genericEmbed"          : "libEmbedVideo/genericEmbed.js",
203         "htmlEmbed"                     : "libEmbedVideo/htmlEmbed.js",
204         "javaEmbed"                     : "libEmbedVideo/javaEmbed.js",
205         "nativeEmbed"           : "libEmbedVideo/nativeEmbed.js",
206         "quicktimeEmbed"        : "libEmbedVideo/quicktimeEmbed.js",
207         "vlcEmbed"                      : "libEmbedVideo/vlcEmbed.js",
209         "mvPlayList"            : "libSequencer/mvPlayList.js",
210         "mvSequencer"           : "libSequencer/mvSequencer.js",
211         "mvFirefoggRender"      : "libSequencer/mvFirefoggRender.js",
212         "mvTimedEffectsEdit": "libSequencer/mvTimedEffectsEdit.js",
214         "mvTextInterface"       : "libTimedText/mvTextInterface.js"
218 //depencency mapping for css files for self contained included plugins:
219 lcCssPath({
220         '$j.Jcrop'                      : 'libClipEdit/Jcrop/css/jquery.Jcrop.css',
221         '$j.fn.ColorPicker'     : 'libClipEdit/colorpicker/css/colorpicker.css'
225  * Language Functions:
227  * These functions try to losely mirro the functionality of Language.php in mediaWiki
228  */
229 function gM( key , args ) {
230         var ms ='';
231         if ( key in gMsg ) {
232                 ms = gMsg[ key ];
233                 if(typeof args == 'object' || typeof args == 'array'){
234                          for(var v in args){
235                                 //msg test replace arguments start at 1 insted of zero:
236                                  var rep = '\$'+ ( parseInt(v) + 1 );
237                                  ms = ms.replace( rep, args[v]);
238                          }
239                 }else if(typeof args =='string' || typeof args =='number'){
240                         ms = ms.replace(/\$1/, args);
241                  }
242                  return ms;
243         } else{
244                 //key is missing return indication:
245                 return '&lt;' + key + '&gt;';
246         }
249  * msgSet is either a string corresponding to a single msg to load
250  * or msgSet is an array with set of msg to load
251  */
252 function gMsgLoadRemote(msgSet, callback){
253         var ammessages = '';
254         if(typeof msgSet == 'object' ){
255                 for(var i in msgSet){
256                         ammessages +=  msgSet[i] + '|';
257                 }
258         }else if(typeof msgSet == 'string'){
259                 ammessages += msgSet;
260         }
261         if(ammessages == ''){
262                 js_log('gMsgLoadRemote::no msg set requested');
263                 return false;
264         }
265         do_api_req({
266                 'data':{
267                         'meta':'allmessages',
268                         'ammessages':ammessages
269                 }
270         },function(data){
271                 if(data.query.allmessages){
272                         var msgs = data.query.allmessages;
273                         for(var i in msgs){
274                                 var ld = {};
275                                 ld[ msgs[i]['name'] ] =  msgs[i]['*'];
276                                 loadGM( ld );
277                         }
278                 }
279                 //load the result into local msg var
280                 callback();
281         });
285  * Format a size in bytes for output, using an appropriate
286  * unit (B, KB, MB or GB) according to the magnitude in question
288  * @param size Size to format
289  * @return string Plain text (not HTML)
290  */
291 function formatSize( size ) {
292         // For small sizes no decimal places necessary
293         var round = 0;
294         var msg = '';
295         if( size > 1024 ) {
296                 size = size / 1024;
297                 if( size > 1024 ) {
298                         size = size / 1024;
299                         // For MB and bigger two decimal places are smarter
300                         round = 2;
301                         if( size > 1024 ) {
302                                 size = size / 1024;
303                                 msg = 'mwe-size-gigabytes';
304                         } else {
305                                 msg = 'mwe-size-megabytes';
306                         }
307                 } else {
308                         msg = 'mwe-size-kilobytes';
309                 }
310         } else {
311                 msg = 'mwe-size-bytes';
312         }
313         //javascript does not let you do precession points in rounding
314         var p =  Math.pow(10,round);
315         var size = Math.round( size * p  ) / p;
316         //@@todo we need a formatNum and we need to request some special packaged info to deal with that case.
317         return  gM( msg , size );
320 //gets the loading image:
321 function mv_get_loading_img( style , class_attr ){
322         var style_txt = (style)?style:'';
323         var class_attr = (class_attr)?'class="'+class_attr+'"':'class="mv_loading_img"';
324         return '<div '+class_attr+' style="' + style +'"></div>';
327 function mv_set_loading(target, load_id){
328         var id_attr = ( load_id )?' id="' + load_id + '" ':'';
329         $j(target).append('<div '+id_attr+' style="position:absolute;top:0px;left:0px;height:100%;width:100%;'+
330                 'background-color:#FFF;">' +
331                         mv_get_loading_img('top:30px;left:30px') +
332                 '</div>');
336   * mvJsLoader class handles initialization and js file loads
337   */
338 var mvJsLoader = {
339          libreq : {},
340          libs : {},
341          //base lib flags:
342          onReadyEvents:new Array(),
343          doneReadyEvents:false,
344          jQueryCheckFlag:false,
345          //to keep consistency across threads:
346          ptime:0,
347          ctime:0,
348          load_error:false, //load error flag (false by default)
349          load_time:0,
350          callbacks:new Array(),
351          cur_path: null,
352          missing_path : null,
353          doLoad:function(loadLibs, callback){
354                  this.ctime++;
355                  if( loadLibs && loadLibs.length!=0 ){ //setup this.libs:
357                          //first check if we already have this lib loaded
358                          var all_libs_loaded=true;
359                          for(var i=0; i< loadLibs.length; i++){
360                                  //check if the lib is already loaded:
361                                 if( ! this.checkObjPath( loadLibs[i] ) ){
362                                         all_libs_loaded=false;
363                                 }
364                          }
365                          if( all_libs_loaded ){
366                                 js_log('all libs already loaded skipping... load req');
367                                 callback();
368                                 return ;
369                         }
370                         //do a check for any css we may need and get it:
371                         for(var i=0; i< loadLibs.length; i++){
372                                 if( typeof mvCssPaths[ loadLibs[i] ] != 'undefined' ){
373                                         loadExternalCss(  mvCssPaths[ loadLibs[i] ]);
374                                 }
375                         }
377                          //check if we should use the script loader to combine all the requests into one:
378                          if( typeof mwSlScript != 'undefined' ){
379                                 var class_set = '';
380                                   var last_class = '';
381                                   var coma = '';
382                                   for(var i=0; i< loadLibs.length; i++){
383                                           var curLib = loadLibs[i];
384                                           //only add if not included yet:
385                                           if( ! this.checkObjPath( curLib ) ){
386                                                   class_set+=coma + curLib ;
387                                                   last_class=curLib;
388                                                   coma=',';
389                                           }
390                                   }
391                                   var puri = parseUri( getMvEmbedURL() );
392                                   if( (getMvEmbedURL().indexOf('://')!=-1) && puri.host != parseUri( document.URL).host){
393                                         mwSlScript =  puri.protocol + '://' + puri.authority + mwSlScript;
394                                   }
396                                   var dbug_attr = (puri.queryKey['debug'])?'&debug=true':'';
397                                   this.libs[ last_class ] = mwSlScript + '?class=' + class_set +
398                                                                           '&urid=' + getMvUniqueReqId() + dbug_attr;
400                          }else{
401                                 //do many requests:
402                                 for(var i=0; i< loadLibs.length; i++){
403                                      var curLib = loadLibs[i];
404                                      if(curLib){
405                                              var libLoc = mvGetClassPath(curLib);
406                                                  // do a direct load of the file (pass along unique request id from request or mv_embed Version )
407                                                  var qmark = (libLoc.indexOf('?')!==true)?'?':'&';
408                                                  this.libs[curLib] =  mv_embed_path + libLoc + qmark + 'urid='+ getMvUniqueReqId();
409                                      }
410                                  }
411                         }
412                 }
413                 if( callback ){
414                         this.callbacks.push(callback);
415                 }
416                 if( this.checkLoading() ){
417                          if( this.load_time++ > 2000){ //time out after ~80seconds
418                                  js_error( gM('mwe-error_load_lib') +  this.missing_path );
419                                  this.load_error = true;
420                          }else{
421                                 setTimeout( 'mvJsLoader.doLoad()', 20 );
422                          }
423                 }else{
424                          //js_log('checkLoading passed run callbacks');
425                          //only do callback if we are in the same instance (weird concurency issue)
426                          var cb_count=0;
427                          for(var i=0; i < this.callbacks.length; i++)
428                                  cb_count++;
429                          //js_log('REST LIBS: loading is: '+ loading + ' run callbacks: '+cb_count +' p:'+ this.ptime +' c:'+ this.ctime);
430                          //reset the libs
431                          this.libs={};
432                          //js_log('done loading do call: ' + this.callbacks[0] );
433                          while( this.callbacks.length !=0 ){
434                                  if( this.ptime== ( this.ctime-1) ){ //enforce thread consistency
435                                          this.callbacks.pop()();
436                                          //func = this.callbacks.pop();
437                                          //js_log(' run: '+this.ctime+ ' p: ' + this.ptime + ' ' +loading+ ' :'+ func);
438                                         //func();
439                                  }else{
440                                          //re-issue doLoad ( ptime will be set to ctime so we should catch up)
441                                          setTimeout( 'mvJsLoader.doLoad()', 25 );
442                                          break;
443                                  }
444                          }
445                  }
446                  this.ptime=this.ctime;
447          },
448          doLoadFullPaths:function(loadObj, callback){
450          },
451          doLoadDepMode:function(loadChain, callback){
452                 //firefox executes js ~in-order of it being included~ so just directly issue request:
453                 if( $j.browser.firefox ){
454                         var loadSet = [];
455                         for(var i=0; i< loadChain.length;i++){
456                                 for(var j=0;j<loadChain[i].length;j++){
457                                         loadSet.push(loadChain[i][j]);
458                                 }
459                         }
460                         mvJsLoader.doLoad(loadSet, callback);
461                 }else{
462                         //safari and IE tend to execute out of order so load with dependenciy checks
463                         mvJsLoader.doLoad(loadChain.shift(),function(){
464                                 if(loadChain.length!=0){
465                                         mvJsLoader.doLoadDepMode(loadChain, callback);
466                                 }else{
467                                         callback();
468                                 }
469                         });
470                 }                },
471          checkLoading:function(){
472                  var loading=0;
473                  var i=null;
474                  for(var i in this.libs){ //for in loop oky on object
475                          if( !this.checkObjPath( i ) ){
476                                  if(!this.libreq[i]){
477                                         loadExternalJs( this.libs[i] );
478                                  }
480                                  this.libreq[i]=1;
481                                  //js_log("has not yet loaded: " + i);
482                                  loading=1;
483                          }
484                  }
485                  return loading;
486         },
487         checkObjPath:function( libVar ){
488                 if(!libVar)
489                         return false;
490                 var objPath = libVar.split('.')
491                 var cur_path ='';
492                 for(var p=0; p < objPath.length; p++){
493                          cur_path = (cur_path=='')?cur_path+objPath[p]:cur_path+'.'+objPath[p];
494                          eval( 'var ptest = typeof ( '+ cur_path + ' ); ');
495                          if( ptest == 'undefined'){
496                                   this.missing_path = cur_path;
497                                  return false;
498                          }
499                 }
500                 this.cur_path = cur_path;
501                 return true;
502         },
503         /**
504          * checks for jQuery and adds the $j noConflict var
505          */
506         jQueryCheck:function(callback){
507                 //skip stuff if $j is already loaded:
508                 if(_global['$j'] && callback)
509                         callback();
510                 var _this = this;
511                 //load jquery
512                 _this.doLoad([
513                          'window.jQuery'
514                 ],function(){
515                         _global['$j'] = jQuery.noConflict();
516                         //set up ajax to not send dynamic urls for loading scripts (we control that with the scriptLoader)
517                         $j.ajaxSetup({
518                                   cache: true
519                         });
520                         js_log('jquery loaded');
521                         //setup mvEmbed jquery bindigns:
522                         mv_jqueryBindings();
523                         //run the callback
524                         if(callback){
525                                 callback();
526                         }
527                 });
528         },
529         embedVideoCheck:function( callback ){
530                 var _this = this;
531                 js_log('embedVideoCheck:');
532                 //set videonojs to loading
533                 //issue a style sheet request get both mv_embed and jquery styles:
534                 loadExternalCss( mv_jquery_skin_path + 'jquery-ui-1.7.1.custom.css' );
535                 loadExternalCss( mv_embed_path  + 'skins/'+mwConfig['skin_name']+'/styles.css');
537                 //make sure we have jQuery
538                 _this.jQueryCheck(function(){
539                         $j('.videonojs').html( gM('mwe-loading_txt') );
540                         var depReq = [
541                                 [
542                                         '$j.ui',
543                                         'embedVideo',
544                                         'ctrlBuilder',
545                                         '$j.cookie'
546                                 ],
547                                 [
548                                         '$j.ui.slider'
549                                 ]
550                         ];
551                         //add png fix if needed:
552                         if($j.browser.msie || $j.browser.version < 7)
553                                 depReq[0].push( '$j.fn.pngFix' );
555                         _this.doLoadDepMode(depReq,function(){
556                                 embedTypes.init();
557                                 callback();
558                                 $j('.videonojs').remove();
559                         });
560                 });
561         },
562         addLoadEvent:function(fn){
563                  this.onReadyEvents.push(fn);
564         },
565         //checks the jQuery flag (this way when remote embeding we don't load jQuery
566         // unless js2AddOnloadHook was used or there is video on the page
567         runQuededFunctions:function(){
568                 var _this = this;
569                 this.doneReadyEvents=true;
570                 if(this.jQueryCheckFlag){
571                         this.jQueryCheck(function(){
572                                 _this.runReadyEvents();
573                         });
574                 }else{
575                         this.runReadyEvents();
576                 }
577         },
578         runReadyEvents:function(){
579                 js_log("runReadyEvents");
580                 while( this.onReadyEvents.length ){
581                         this.onReadyEvents.shift()();
582                 }
583         }
586 //load an external JS (similar to jquery .require plugin)
587 //but checks for object availability rather than load state
589 /*********** INITIALIZATION CODE *************
590  * this will get called when DOM is ready
591  *********************************************/
592 /* jQuery .ready does not work when jQuery is loaded dynamically
593  * for an example of the problem see:1.1.3 working:http://pastie.caboo.se/92588
594  * and >= 1.1.4 not working: http://pastie.caboo.se/92595
595  * $j(document).ready( function(){ */
596 function mwdomReady(force){
597         js_log('f:mwdomReady:');
598         if( !force && mv_init_done  ){
599                 js_log("mv_init_done already done do nothing...");
600                 return false;
601         }
602         mv_init_done=true;
603         //handle the execution of Queded function with jQuery "ready"
605         //check if this page does have video or playlist
606         var e = [
607                 document.getElementsByTagName("video"),
608                 document.getElementsByTagName("audio"),
609                 document.getElementsByTagName("playlist")
610         ];
611         if(e[0].length!=0 || e[1].length!=0 || e[2].length!=0){
612                 js_log('we have items to rewrite');
613                 setSwapableToLoading(e);
614                 //load libs and proccess:
615                 mvJsLoader.embedVideoCheck(function(){
616                         //run any queded global events:
617                         mv_video_embed( function(){
618                                 mvJsLoader.runQuededFunctions();
619                         });
620                 });
621         }else{
622                 //if we already have jQuery make sure its loaded into its proper context $j
623                 //run any queded global events:
624                 mvJsLoader.runQuededFunctions();
625         }
627 //a quick function that sets the intial text of swapable elements to "loading"
628 // (jquery might not be ready)
629 //(does not destroy inner elements)
630 function setSwapableToLoading(e){
631         //for(var i =0)
632         //for(var j = 0; i < j.length; j++){
633         //}
635 //js2AddOnloadHook: ensure jQuery and the DOM are ready:
636 function js2AddOnloadHook( func ) {
637         //make sure the skin/style sheets are avaliable always:
638         loadExternalCss( mv_jquery_skin_path + 'jquery-ui-1.7.1.custom.css' );
639         loadExternalCss( mv_embed_path  + 'skins/'+mwConfig['skin_name']+'/styles.css');
641         //if we have already run the dom ready just run the function directly:
642         if( mvJsLoader.doneReadyEvents ){
643                 //make sure jQuery is there:
644                 mvJsLoader.jQueryCheck(function(){
645                         func();
646                 });
647         }else{
648                 //if using js2AddOnloadHook we need to get jQuery into place (if its not already included)
649                 mvJsLoader.jQueryCheckFlag = true;
650                 mvJsLoader.addLoadEvent( func );
651         };
653 //depreciated mwAddOnloadHook in favor of js2 naming (for clear seperation of js2 code from old mw code
654 var mwAddOnloadHook = js2AddOnloadHook;
656  * this function allows for targeted rewriting
657  */
658 function rewrite_by_id( vid_id, ready_callback ){
659         js_log('f:rewrite_by_id: ' + vid_id);
660         //force a recheck of the dom for playlist or video element:
661         mvJsLoader.embedVideoCheck(function(){
662                  mv_video_embed(ready_callback, vid_id );
663         });
665 //depricated in favor of updates to oggHanlder
666 function rewrite_for_oggHanlder( vidIdList ){
667         for(var i = 0; i < vidIdList.length ; i++){
668                 var vidId = vidIdList[i];
669                 js_log('looking at vid: ' + i +' ' + vidId);
670                 //grab the thumbnail and src video
671                 var pimg = $j('#'+vidId + ' img');
672                 var poster_attr = 'poster = "' + pimg.attr('src') + '" ';
673                 var pwidth = pimg.attr('width');
674                 var pheight = pimg.attr('height');
676                 var type_attr = '';
677                 //check for audio
678                 if( pwidth=='22' && pheight=='22'){
679                         pwidth='400';
680                         pheight='300';
681                         type_attr = 'type="audio/ogg"';
682                         poster_attr = '';
683                 }
685                 //parsed values:
686                 var src = '';
687                 var duration = '';
689                 var re = new RegExp( /videoUrl(&quot;:?\s*)*([^&]*)/ );
690                 src  = re.exec( $j('#'+vidId).html() )[2];
692                 var re = new RegExp( /length(&quot;:?\s*)*([^&]*)/ );
693                 duration = re.exec( $j('#'+vidId).html() )[2];
695                 var re = new RegExp( /offset(&quot;:?\s*)*([^&]*)/ );
696                 offset = re.exec( $j('#'+vidId).html() )[2];
697                 var offset_attr = (offset)? 'startOffset="'+ offset + '"': '';
699                 if( src ){
700                         //replace the top div with mv_embed based player:
701                         var vid_html = '<video id="vid_' + i +'" '+
702                                          'src="' + src + '" ' +
703                                          poster_attr + ' ' +
704                                          type_attr + ' ' +
705                                          offset_attr + ' ' +
706                                          'duration="' + duration + '" ' +
707                                          'style="width:' + pwidth + 'px;height:' +
708                                                  pheight + 'px;"></video>';
709                         //js_log("video html: " + vid_html);
710                          $j('#'+vidId).html( vid_html );
711                 }
713                 //rewrite that video id:
714                 rewrite_by_id('vid_' + i);
715         }
719 /*********** INITIALIZATION CODE *************
720  * set DOM ready callback to init_mv_embed
721  *********************************************/
722 // for Mozilla browsers
723 if (document.addEventListener ) {
724         document.addEventListener("DOMContentLoaded", function(){mwdomReady()}, false);
725 }else{
726         //backup "onload" method in case on DOMContentLoaded does not exist
727         window.onload = function(){ mwdomReady() };
730  * should depreciate and use jquery.ui.dialog instead
731  */
732 function mv_write_modal(content, speed){
733         $j('#modalbox,#mv_overlay').remove();
734         $j('body').append('<div id="modalbox" style="background:#DDD;border:3px solid #666666;font-size:115%;'+
735                                 'top:30px;left:20px;right:20px;bottom:30px;position:fixed;z-index:100;">'+
736                                 content +
737                         '</div>'+
738                         '<div id="mv_overlay" style="background:#000;cursor:wait;height:100%;left:0;position:fixed;'+
739                                 'top:0;width:100%;z-index:5;filter:alpha(opacity=60);-moz-opacity: 0.6;'+
740                                 'opacity: 0.6;"/>');
741         $j('#modalbox,#mv_overlay').show( speed );
743 function mv_remove_modal(speed){
744         $j('#modalbox,#mv_overlay').remove( speed);
748  * stores all the mwEmbed jQuery specific bindings
749  * (setup after jQuery is avaliable)
750  * lets you call rewrites in a jquery "way"
752  * @@ eventually we should refactor mwCode over to jQuery style plugins
753  *        and mv_embed.js will just hanndle dependency mapping and loading.
755  */
756 function mv_jqueryBindings(){
757         js_log('mv_jqueryBindings');
758         (function($) {
759                 $.fn.addMediaWiz = function( iObj, callback ){
760                         //first set the cursor for the button to "loading"
761                         $j(this.selector).css('cursor','wait').attr('title', gM('mwe-loading_title'));
763                         iObj['target_invocation'] = this.selector;
765                         //load the mv_embed_base skin:
766                         loadExternalCss( mv_jquery_skin_path + 'jquery-ui-1.7.1.custom.css' );
767                         loadExternalCss( mv_embed_path  + 'skins/'+mwConfig['skin_name']+'/styles.css' );
768                         //load all the req libs:
769                         mvJsLoader.jQueryCheck(function(){
770                                 //load with staged dependeinces (for ie and safari that don't execute in order)
771                                 mvJsLoader.doLoadDepMode([
772                                         [       'remoteSearchDriver',
773                                                 '$j.cookie',
774                                                 '$j.ui'
775                                         ],[
776                                                 '$j.ui.resizable',
777                                                 '$j.ui.draggable',
778                                                 '$j.ui.dialog',
779                                                 '$j.ui.tabs',
780                                                 '$j.ui.sortable'
781                                         ]
782                                 ], function(){
783                                         iObj['instance_name']= 'rsdMVRS';
784                                         _global['rsdMVRS'] = new remoteSearchDriver( iObj );
785                                         if( callback ){
786                                            callback( _global['rsdMVRS'] );
787                                         }
788                                 });
789                         });
790                 }
791                 $.fn.sequencer = function( iObj, callback){
792                         //debugger;
793                         iObj['target_sequence_container'] = this.selector;
794                 //issue a request to get the css file (if not already included):
795                 loadExternalCss( mv_jquery_skin_path + 'jquery-ui-1.7.1.custom.css');
796                 loadExternalCss( mv_embed_path+'skins/'+mwConfig['skin_name']+'/mv_sequence.css');
797                 //make sure we have the required mv_embed libs (they are not loaded when no video element is on the page)
798                 mvJsLoader.embedVideoCheck(function(){
799                         //load playlist object and then jquery ui stuff:
800                         mvJsLoader.doLoadDepMode([
801                                 [
802                                         'mvPlayList',
803                                         '$j.ui',
804                                         '$j.contextMenu',
805                                         '$j.secureEvalJSON',
806                                         'mvSequencer'
807                                 ],
808                                 [
809                                         '$j.ui.accordion',
810                                         '$j.ui.dialog',
811                                         '$j.ui.droppable',
812                                         '$j.ui.draggable',
813                                         '$j.ui.progressbar',
814                                         '$j.ui.sortable',
815                                         '$j.ui.resizable',
816                                         '$j.ui.slider',
817                                         '$j.ui.tabs'
818                                 ]
819                         ], function(){
820                                 js_log('calling new mvSequencer');
821                                 //init the sequence object (it will take over from there) no more than one mvSeq obj for now:
822                                 if(!_global['mvSeq']){
823                                         _global['mvSeq'] = new mvSequencer(iObj);
824                                 }else{
825                                         js_log('mvSeq already init');
826                                 }
827                         });
828                 });
829                 }
830                 /*
831                  * the firefogg jquery function:
832                  * @@note this firefogg envocation could be made to work more like real jquery plugins
833                  */
834                 $.fn.firefogg = function( iObj, callback ) {
835                         if(!iObj)
836                                 iObj={};
837                         //add base theme css:
838                         loadExternalCss( mv_jquery_skin_path + 'jquery-ui-1.7.1.custom.css');
839                         loadExternalCss( mv_embed_path  + 'skins/'+mwConfig['skin_name']+'/styles.css' );
841                         //check if we already have firefogg loaded (the call just updates properties for that element)
842                         var sElm = $j(this.selector).get(0);
843                         if(sElm['firefogg']){
844                                 if(sElm['firefogg']=='loading'){
845                                         js_log("Error: called firefogg operations on Firefogg selector that is not done loading");                                      
846                                         return false;
847                                 }
848                                 //update properties:
849                                 for(var i in iObj){
850                                         js_log("firefogg::updated: "+ i + ' to '+ iObj[i]);
851                                         sElm['firefogg'][i] = iObj[i];
852                                 }
853                                 return sElm['firefogg'];
854                         }else{
855                                 //avoid concurency
856                                 sElm['firefogg'] = 'loading';
857                         }
858                         //add the selector:
859                         iObj['selector'] = this.selector;
861                         var loadSet = [
862                                 [
863                                         'mvBaseUploadInterface',
864                                         'mvFirefogg',
865                                         '$j.ui'
866                                 ],
867                                 [
868                                         '$j.ui.progressbar',
869                                         '$j.ui.dialog'
870                                 ]
871                         ];
872                         if( iObj.encoder_interface ){
873                                 loadSet.push([
874                                         'mvAdvFirefogg',
875                                         '$j.cookie',
876                                         '$j.ui.accordion',
877                                         '$j.ui.slider',
878                                         '$j.ui.datepicker'
879                                 ]);
880                         }
881                         //make sure we have everything loaded that we need:
882                         mvJsLoader.doLoadDepMode( loadSet, function(){
883                                         js_log('firefogg libs loaded. target select:' + iObj.selector);
884                                         //select interface provicer based on if we want to include the encoder interface or not:
885                                         if(iObj.encoder_interface){
886                                                 var myFogg = new mvAdvFirefogg( iObj );
887                                         }else{
888                                                 var myFogg = new mvFirefogg( iObj );
889                                         }
890                                         if(myFogg){                                             
891                                                 myFogg.doRewrite( callback );
892                                                 var selectorElement = $j( iObj.selector ).get(0);
893                                                 selectorElement['firefogg']=myFogg;
894                                         }
895                         });
896                 }
897                 //takes a input player as the selector and exposes basic rendering controls
898                 $.fn.firefoggRender = function( iObj, callback ){
899                         //check if we already have render loaded then just pass on updates/actions
900                         var sElm = $j(this.selector).get(0);
901                         if(sElm['fogg_render']){
902                                 if(sElm['fogg_render']=='loading'){
903                                         js_log("Error: called firefoggRender while loading");
904                                         return false;
905                                 }
906                                 //call or update the property:
907                         }
908                         sElm['fogg_render']='loading';
909                         //add the selector:
910                         iObj['player_target'] = this.selector;
911                         mvJsLoader.doLoad([
912                                 'mvFirefogg',
913                                 'mvFirefoggRender'
914                         ],function(){
915                                 sElm['fogg_render']= new mvFirefoggRender( iObj );
916                                 if( callback && typeof callback == 'function' )
917                                         callback( sElm['fogg_render'] );
918                         });
919                 }
921                 $.fn.baseUploadInterface = function(iObj){
922                         mvJsLoader.doLoadDepMode([
923                                 [
924                                         'mvBaseUploadInterface',
925                                         '$j.ui',
926                                 ],
927                                 [
928                                   '$j.ui.progressbar',
929                                   '$j.ui.dialog'
930                                 ]
931                         ],function(){
932                                 myUp = new mvBaseUploadInterface( iObj );
933                                 myUp.setupForm();
934                         });
935                 }
937                 //shortcut to a themed button:
938                 $.btnHtml = function(msg, className, iconId, opt){
939                    if(!opt)
940                       opt = {};
941                    var href = (opt.href)?opt.href:'#';
942                    var target_attr = (opt.target)?' target="' + opt.target + '" ':'';
943                    var style_attr = (opt.style)?' style="'+opt.style +'" ':'';
944                    return '<a href="' + href + '" ' + target_attr + style_attr +' class="ui-state-default ui-corner-all ui-icon_link ' +
945                                    className + '"><span class="ui-icon ui-icon-' + iconId + '" />' +
946                                    msg + '</a>';
947                 }
948                 //shortcut to bind hover state:
949                 $.fn.btnBind = function(){
950                         $j(this).hover(
951                                 function(){
952                                         $j(this).addClass('ui-state-hover');
953                                 },
954                                 function(){
955                                         $j(this).removeClass('ui-state-hover');
956                                 }
957                         )
958                         return this;
959                 }
961         })(jQuery);
964 * utility functions:
966 //simple url re-writer for rewriting urls (could probably be refactored into an inline regular expresion)
967 function getURLParamReplace( url, opt ){
968         var pSrc = parseUri( url );
969         if(pSrc.protocol != '' ){
970                 var new_url = pSrc.protocol +'://'+ pSrc.authority + pSrc.path +'?';
971         }else{
972                 var new_url = pSrc.path +'?';
973         }
974         var amp = '';
975         for(var key in pSrc.queryKey){
976                 var val = pSrc.queryKey[ key ];
977                 //do override if requested
978                 if( opt[ key ] )
979                         val = opt[ key ];
980                 new_url+= amp + key + '=' + val;
981                 amp = '&';
982         };
983         //add any vars that did were not originally there:
984         for(var i in opt){
985                 if(!pSrc.queryKey[i]){
986                   new_url+=amp + i + '=' + opt[i];
987                   amp = '&';
988                 }
989         }
990         return new_url;
993  * seconds2npt given a float seconds returns npt format response:
994  * @param float seconds
995  * @param boolean if we should show ms or not.
996  */
997 function seconds2npt(sec, show_ms){
998         if( isNaN( sec ) ){
999                 //js_log("warning: trying to get npt time on NaN:" + sec);
1000                 return '0:0:0';
1001         }
1002         var hours = Math.floor(sec/ 3600);
1003         var minutes = Math.floor((sec/60) % 60);
1004         var seconds = sec % 60;
1005         //round the second amount requested significant digits
1006         if(show_ms){
1007                 seconds = Math.round( seconds * 1000 ) / 1000;
1008         }else{
1009                 seconds = Math.round( seconds );
1010         }
1011         if(seconds <10 )
1012                 seconds = '0'+  seconds;
1013         if(minutes < 10 )
1014                 minutes = '0' + minutes;
1016         return hours+":"+minutes+":"+seconds;
1019  * takes hh:mm:ss,ms or  hh:mm:ss.ms input returns number of seconds
1020  */
1021 function npt2seconds( npt_str ){
1022         if(!npt_str){
1023                 //js_log('npt2seconds:not valid ntp:'+ntp);
1024                 return false;
1025         }
1026         //strip npt: time definition if present
1027         npt_str = npt_str.replace('npt:', '');
1029         times = npt_str.split(':');
1030         if(times.length!=3){
1031                 js_log('error: npt2seconds on ' + npt_str);
1032                 return false;
1033         }
1034         //sometimes the comma is used inplace of pereid for ms
1035         times[2] = times[2].replace(/,\s?/,'.');
1036         //return seconds float (ie take seconds float value if present):
1037         return parseInt(times[0]*3600)+parseInt(times[1]*60)+parseFloat(times[2]);
1040  * simple helper to grab a edit token
1042  * @param title the wiki page title you want to edit )
1043  * @param api_url 'optional' the target api url
1044  * @param callback the callback function to pass the token or "false" to
1045  */
1046 function get_mw_token( title, api_url, callback){
1047         js_log(':get_mw_token:');
1048         if(!title && wgUserName){
1049                 title = 'User:' + wgUserName;
1050         }
1051         var reqObj = {
1052                         'action':'query',
1053                         'prop':'info',
1054                         'intoken':'edit',
1055                         'titles':title
1056                 };
1057                 do_api_req( {
1058                         'data': reqObj,
1059                         'url' : api_url
1060                         },function(data){
1061                                 for(var i in data.query.pages){
1062                                         if(data.query.pages[i]['edittoken']){
1063                                                 if(typeof callback == 'function')
1064                                                         callback ( data.query.pages[i]['edittoken'] );
1065                                         }
1066                                 }
1067                                 //no token found:
1068                                 return false;
1069                         }
1070                 );
1072 //does a remote or local api request based on request url
1073 //@param options: url, data, cbParam, callback
1074 function do_api_req( options, callback ){
1075         if(typeof options.data != 'object'){
1076                 return js_error('Error: request paramaters must be an object');;
1077         }
1078         //gennerate the url if its missing:
1079         if( typeof options.url == 'undefined' ||  options.url === false){
1080                 if(!wgServer || ! wgScriptPath){
1081                         return js_error('Error: no api url for api request');;
1082                 }
1083                 //update to api.php (if index.php was in the wgScript path):
1084                  options.url =  mwGetLocalApiUrl();
1085         }
1086         if( typeof options.data == 'undefined' )
1087                 options.data = {};
1089         //force format to json (if not already set)
1090         options.data['format'] = 'json';
1092         //if action not set assume query
1093         if(!options.data['action'])
1094                 options.data['action']='query';
1096         //js_log('do api req: ' + options.url +'?' +  jQuery.param(options.data) );
1097         //build request string:
1098         if( parseUri( document.URL ).host == parseUri( options.url ).host ){
1099                 //local request do api request directly
1100                 $j.ajax({
1101                         type: "POST",
1102                         url: options.url,
1103                         data: options.data,
1104                         dataType:'json', //api requests _should_ always return JSON data:
1105                         async: false,
1106                         success:function(data){
1107                                 callback(  data );
1108                         },
1109                         error:function(e){
1110                                 js_error( ' error' + e +' in getting: ' + options.url);
1111                         }
1112                 });
1113         }else{
1114                 //set the callback param if not already set:
1115                 if( typeof options.jsonCB == 'undefined')
1116                         options.jsonCB = 'callback';
1118                 var req_url = options.url;
1119                 var paramAnd = (req_url.indexOf('?')==-1)?'?':'&';
1120                 //put all the values into the GET req:
1121                 for(var i in options.data){
1122                         req_url += paramAnd + encodeURIComponent( i ) + '=' + encodeURIComponent( options.data[i] );
1123                         paramAnd ='&';
1124                 }
1125                 var fname = 'mycpfn_' + ( global_cb_count++ );
1126                 _global[ fname ]  =  callback;
1127                 req_url += '&' + options.jsonCB + '=' + fname;
1128                 loadExternalJs( req_url );
1129         }
1131 function mwGetLocalApiUrl(url){
1132         if (wgServer && wgScriptPath){
1133                 return wgServer + wgScriptPath + '/api.php';
1134         }
1135         return false;
1137 //grab wiki form error for wiki html page proccessing (should be depricated)
1138 function grabWikiFormError ( result_page ){
1139                 var res = {};
1140                 sp = result_page.indexOf('<span class="error">');
1141                 if(sp!=-1){
1142                         se = result_page.indexOf('</span>', sp);
1143                         res.error_txt = result_page.substr(sp, (sp-se)) + '</span>';
1144                 }else{
1145                         //look for warning:
1146                         sp = result_page.indexOf('<ul class="warning">')
1147                         if(sp != -1){
1148                                 se = result_page.indexOf('</ul>', sp);
1149                                 res.error_txt = result_page.substr(sp, (se-sp)) + '</ul>';
1150                                 //try and add the ignore form item:
1151                                 sfp = result_page.indexOf('<form method="post"');
1152                                 if(sfp!=-1){
1153                                         sfe = result_page.indexOf('</form>', sfp);
1154                                         res.form_txt = result_page.substr(sfp, ( sfe - sfp )) + '</form>';
1155                                 }
1156                         }else{
1157                                 //one more error type check:
1158                                 sp = result_page.indexOf('class="mw-warning-with-logexcerpt">')
1159                                 if(sp!=-1){
1160                                         se = result_page.indexOf('</div>', sp);
1161                                         res.error_txt = result_page.substr(sp, ( se - sp )) + '</div>';
1162                                 }
1163                         }
1164                 }
1165                 return res;
1167 //do a "normal" request
1168 function do_request(req_url, callback){
1169         js_log('do_request::req_url:' + req_url + ' != ' +  parseUri( req_url).host);
1170         //if we are doing a request to the same domain or relative link do a normal GET:
1171         if( parseUri(document.URL).host == parseUri(req_url).host ||
1172                 req_url.indexOf('://') == -1 ){ //relative url
1173                 //do a direct request:
1174                 $j.ajax({
1175                         type: "GET",
1176                         url:req_url,
1177                            async: false,
1178                         success:function(data){
1179                                 callback( data );
1180                         }
1181                 });
1182         }else{
1183                 //get data via DOM injection with callback
1184                 global_req_cb.push(callback);
1185                 //prepend json_ to feed_format if not already requesting json format
1186                 if( req_url.indexOf("feed_format=")!=-1 &&  req_url.indexOf("feed_format=json")==-1)
1187                         req_url = req_url.replace(/feed_format=/, 'feed_format=json_');
1188                 loadExternalJs( req_url + '&cb=mv_jsdata_cb&cb_inx=' + (global_req_cb.length-1));
1189         }
1192 function mv_jsdata_cb(response){
1193         js_log('f:mv_jsdata_cb:'+ response['cb_inx']);
1194         //run the callback from the global req cb object:
1195         if( !global_req_cb[response['cb_inx']] ){
1196                 js_log('missing req cb index');
1197                 return false;
1198         }
1199         if( !response['pay_load'] ){
1200                 js_log("missing pay load");
1201                 return false;
1202         }
1203         //switch on content type:
1204         switch(response['content-type']){
1205                 case 'text/plain':
1206                 break;
1207                 case 'text/xml':
1208                         if(typeof response['pay_load'] == 'string'){
1209                                 //js_log('load string:'+"\n"+ response['pay_load']);
1210                                 //debugger;
1211                                 //attempt to parse as xml for IE
1212                                 if( $j.browser.msie ){
1213                                         var xmldata=new ActiveXObject("Microsoft.XMLDOM");
1214                                         xmldata.async="false";
1215                                         xmldata.loadXML(response['pay_load']);
1216                                 }else{ //for others (firefox, safari etc)
1217                                         try{
1218                                                 var xmldata = (new DOMParser()).parseFromString(response['pay_load'], "text/xml");
1219                                         }catch(e) {
1220                                                           js_log('XML parse ERROR: ' + e.message);
1221                                           }
1222                                 }
1223                                 //@@todo hanndle xml parser errors
1224                                 if(xmldata)response['pay_load']=xmldata;
1225                         }
1226                 break
1227                 default:
1228                         js_log('bad response type' + response['content-type']);
1229                         return false;
1230                 break;
1231         }
1232         global_req_cb[response['cb_inx']]( response['pay_load'] );
1234 //load external js via dom injection
1235 function loadExternalJs( url, callback ){
1236           js_log('load js: '+ url);
1237         //if(window['$j']) //use jquery call:
1238            /*$j.ajax({
1239                         type: "GET",
1240                         url: url,
1241                         dataType: 'script',
1242                         cache: true
1243                 });*/
1244   //  else{
1245                 var e = document.createElement("script");
1246                 e.setAttribute('src', url);
1247                 e.setAttribute('type', "text/javascript");
1248                 /*if(callback)
1249                         e.onload = callback;
1250                 */
1251                 //e.setAttribute('defer', true);
1252                 document.getElementsByTagName("head")[0].appendChild(e);
1253    // }
1255 function styleSheetPresent(url){
1256         style_elements = document.getElementsByTagName('link');
1257         if( style_elements.length > 0) {
1258                 for(i = 0; i < style_elements.length; i++) {
1259                         if(style_elements[i].href == url)
1260                                 return true;
1261                 }
1262         }
1263         return false;
1265 function loadExternalCss(url){
1266         //if could have script loader group thes css request
1267         //but debatable it may hurt more than it helps with caching and all
1268         if(typeof url =='object'){
1269                 for(var i in url){
1270                         loadExternalCss ( url[i] );
1271                 }
1272                 return ;
1273         }
1275         if( url.indexOf('?') == -1 ){
1276                 url+='?'+getMvUniqueReqId();
1277         }
1278         if(!styleSheetPresent(url) ){
1279            js_log('load css: ' + url);
1280            var e = document.createElement("link");
1281            e.href = url;
1282            e.type = "text/css";
1283            e.rel = 'stylesheet';
1284            document.getElementsByTagName("head")[0].appendChild(e);
1285         }
1287 function getMvEmbedURL(){
1288         if( _global['mv_embed_url'] )
1289                 return _global['mv_embed_url'];
1290         var js_elements = document.getElementsByTagName("script");
1291         for(var i=0; i < js_elements.length; i++){
1292                 //check for normal mv_embed.js and or script loader
1293                 var src = js_elements[i].getAttribute("src");
1294                 if( src ){
1295                         if( src.indexOf('mv_embed.js') !=-1 || (
1296                                 ( src.indexOf('mwScriptLoader.php') != -1 || src.indexOf('jsScriptLoader.php') != -1 )
1297                                         && src.indexOf('mv_embed') != -1) ){ //(check for class=mv_embed script_loader call)
1298                                 _global['mv_embed_url'] = src;
1299                                 return  src;
1300                         }
1301                 }
1302         }
1303         js_error('Error: getMvEmbedURL failed to get Embed Path');
1304         return false;
1306 //gets a unique request id to ensure fresh javascript
1307 function getMvUniqueReqId(){
1308         if( _global['urid'] )
1309                 return _global['urid'];
1310         var mv_embed_url = getMvEmbedURL();
1311         //if we have a uri retun that:
1312         var urid = parseUri( mv_embed_url).queryKey['urid']
1313         if( urid ){
1314                 _global['urid'] = urid;
1315                 return urid;
1316         }
1317         //if in debug mode get a fresh unique request key:
1318         if(  parseUri( mv_embed_url ).queryKey['debug'] == 'true'){
1319                 var d = new Date();
1320                 var urid = d.getTime();
1321                 _global['urid'] = urid;
1322                 return urid;
1323         }
1324         //else just return the mv_embed version;
1325         return MV_EMBED_VERSION;
1328  * sets the global mv_embed path based on the scripts location
1329  */
1330 function getMvEmbedPath(){
1331         if( _global['mv_embed_path'])
1332                 return _global['mv_embed_path'];
1333         var mv_embed_url = getMvEmbedURL();
1334         if( mv_embed_url.indexOf('mv_embed.js') !== -1 ){
1335                 mv_embed_path = mv_embed_url.substr(0, mv_embed_url.indexOf('mv_embed.js'));
1336         }else if(mv_embed_url.indexOf('mwScriptLoader.php')!==-1){
1337                 //script load is in the root of mediaWiki so include the default mv_embed extention path (if using the script loader)
1338                 mv_embed_path = mv_embed_url.substr(0, mv_embed_url.indexOf('mwScriptLoader.php'))  + mediaWiki_mvEmbed_path ;
1339         }else{
1340                 mv_embed_path = mv_embed_url.substr(0, mv_embed_url.indexOf('jsScriptLoader.php'));
1341         }
1342         //absolute the url (if relative) (if we don't have mv_embed path)
1343         if( mv_embed_path.indexOf('://') == -1){
1344                 var pURL = parseUri( document.URL );
1345                 if(mv_embed_path.charAt(0)=='/'){
1346                         mv_embed_path = pURL.protocol + '://' + pURL.authority + mv_embed_path;
1347                 }else{
1348                         //relative:
1349                         if(mv_embed_path==''){
1350                                 mv_embed_path = pURL.protocol + '://' + pURL.authority + pURL.directory + mv_embed_path;
1351                         }
1352                 }
1353         }
1354         _global['mv_embed_path'] = mv_embed_path;
1355         return mv_embed_path;
1358 if (typeof DOMParser == "undefined") {
1359    DOMParser = function () {}
1360    DOMParser.prototype.parseFromString = function (str, contentType) {
1361           if (typeof ActiveXObject != "undefined") {
1362                  var d = new ActiveXObject("MSXML.DomDocument");
1363                  d.loadXML(str);
1364                  return d;
1365           } else if (typeof XMLHttpRequest != "undefined") {
1366                  var req = new XMLHttpRequest;
1367                  req.open("GET", "data:" + (contentType || "application/xml") +
1368                                                  ";charset=utf-8," + encodeURIComponent(str), false);
1369                  if (req.overrideMimeType) {
1370                         req.overrideMimeType(contentType);
1371                  }
1372                  req.send(null);
1373                  return req.responseXML;
1374           }
1375    }
1378 * utility functions:
1380 function js_log(string){
1381   if( window.console ){
1382            window.console.log(string);
1383   }else{
1384          /*
1385           * IE and non-firebug debug:
1386           */
1387          /*var log_elm = document.getElementById('mv_js_log');
1388          if(!log_elm){
1389                  document.getElementsByTagName("body")[0].innerHTML = document.getElementsByTagName("body")[0].innerHTML +
1390                                          '<div style="position:absolute;z-index:500;top:0px;left:0px;right:0px;height:10px;">'+
1391                                                  '<textarea id="mv_js_log" cols="120" rows="5"></textarea>'+
1392                                          '</div>';
1394                  var log_elm = document.getElementById('mv_js_log');
1395          }
1396          if(log_elm){
1397                  log_elm.value+=string+"\n";
1398          }*/
1399   }
1400   return false;
1403 function checkDefaultMwConfig(){
1404         for(var i in defaultMwConfig){
1405              if(typeof(mwConfig[i])=='undefined'){
1406                   mwConfig[i] =defaultMwConfig[i];
1407              }
1408           }
1409         }
1411 function js_error(string){
1412         alert(string);
1413         return false;