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