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