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