* added textSelection plugin from usability
[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
18 /**
19 * AutoLoader paths
20 * @path The path to the file (or set of files) with ending slash
21 * @gClasses The set of classes
22 * if it's an array, $j.className becomes jquery.className.js
23 * if it's an associative object then key => value pairs are used
24 */
25 if( typeof mvAutoLoadClasses == 'undefined' )
26 mvAutoLoadClasses = {};
27
28 // The script that loads the class set
29 function lcPaths( classSet ){
30 for( var i in classSet ) {
31 mvAutoLoadClasses[i] = classSet[i];
32 }
33 }
34
35 function mvGetClassPath(k){
36 if( mvAutoLoadClasses[k] ) {
37 //js_log('got class path:' + k + ' : '+ mvClassPaths[k]);
38 return mvAutoLoadClasses[k];
39 } else {
40 js_log('Error:: Could not find path for requested class ' + k );
41 return false;
42 }
43 }
44 if( typeof mvCssPaths == 'undefined' )
45 mvCssPaths = {};
46
47 function lcCssPath( cssSet ) {
48 for( var i in cssSet ) {
49 mvCssPaths[i] = mv_embed_path + cssSet[i];
50 }
51 }
52
53 /*
54 * -- Load Class Paths --
55 *
56 * MUST BE VALID JSON (NOT JS)
57 * This is used by the script loader to auto-load classes (so we only define
58 * this once for PHP & JavaScript)
59 *
60 * Right now the PHP AutoLoader only reads this mv_embed.js file.
61 * In the future we could have multiple lcPath calls that PHP reads
62 * (if our autoloading class list becomes too long)
63 * or
64 * we could support direct file requests to the script loader instead
65 * of shared class names read from a central location.
66 */
67 lcPaths({
68 "mv_embed" : "mv_embed.js",
69 "window.jQuery" : "jquery/jquery-1.3.2.js",
70 "$j.fn.pngFix" : "jquery/plugins/jquery.pngFix.js",
71 "$j.fn.autocomplete": "jquery/plugins/jquery.autocomplete.js",
72 "$j.fn.hoverIntent" : "jquery/plugins/jquery.hoverIntent.js",
73 "$j.fn.datePicker" : "jquery/plugins/jquery.datePicker.js",
74 "$j.ui" : "jquery/jquery.ui/ui/ui.core.js",
75 "$j.fn.ColorPicker" : "libClipEdit/colorpicker/js/colorpicker.js",
76 "$j.Jcrop" : "libClipEdit/Jcrop/js/jquery.Jcrop.js",
77 "$j.fn.simpleUploadForm" : "libAddMedia/simpleUploadForm.js",
78
79 "$mw.proxy" : "libMwApi/mw.proxy.js",
80
81 "$mw.testLang" : "tests/testLang.js",
82
83 "ctrlBuilder" : "skins/ctrlBuilder.js",
84 "kskinConfig" : "skins/kskin/kskin.js",
85 "mvpcfConfig" : "skins/mvpcf/mvpcf.js",
86
87 "JSON" : "libMwApi/json2.js",
88 "$j.cookie" : "jquery/plugins/jquery.cookie.js",
89 "$j.contextMenu" : "jquery/plugins/jquery.contextMenu.js",
90 "$j.fn.suggestions" : "jquery/plugins/jquery.suggestions.js",
91 "$j.fn.textSelection" : "jquery/plugins/jquery.textSelection.js",
92
93 "$j.effects.blind" : "jquery/jquery.ui/ui/effects.blind.js",
94 "$j.effects.drop" : "jquery/jquery.ui/ui/effects.drop.js",
95 "$j.effects.pulsate" : "jquery/jquery.ui/ui/effects.pulsate.js",
96 "$j.effects.transfer" : "jquery/jquery.ui/ui/effects.transfer.js",
97 "$j.ui.droppable" : "jquery/jquery.ui/ui/ui.droppable.js",
98 "$j.ui.slider" : "jquery/jquery.ui/ui/ui.slider.js",
99 "$j.effects.bounce" : "jquery/jquery.ui/ui/effects.bounce.js",
100 "$j.effects.explode" : "jquery/jquery.ui/ui/effects.explode.js",
101 "$j.effects.scale" : "jquery/jquery.ui/ui/effects.scale.js",
102 "$j.ui.datepicker" : "jquery/jquery.ui/ui/ui.datepicker.js",
103 "$j.ui.progressbar" : "jquery/jquery.ui/ui/ui.progressbar.js",
104 "$j.ui.sortable" : "jquery/jquery.ui/ui/ui.sortable.js",
105 "$j.effects.clip" : "jquery/jquery.ui/ui/effects.clip.js",
106 "$j.effects.fold" : "jquery/jquery.ui/ui/effects.fold.js",
107 "$j.effects.shake" : "jquery/jquery.ui/ui/effects.shake.js",
108 "$j.ui.dialog" : "jquery/jquery.ui/ui/ui.dialog.js",
109 "$j.ui.resizable" : "jquery/jquery.ui/ui/ui.resizable.js",
110 "$j.ui.tabs" : "jquery/jquery.ui/ui/ui.tabs.js",
111 "$j.effects.core" : "jquery/jquery.ui/ui/effects.core.js",
112 "$j.effects.highlight" : "jquery/jquery.ui/ui/effects.highlight.js",
113 "$j.effects.slide" : "jquery/jquery.ui/ui/effects.slide.js",
114 "$j.ui.accordion" : "jquery/jquery.ui/ui/ui.accordion.js",
115 "$j.ui.draggable" : "jquery/jquery.ui/ui/ui.draggable.js",
116 "$j.ui.selectable" : "jquery/jquery.ui/ui/ui.selectable.js",
117
118 "$j.fn.dragDropFile" : "libAddMedia/dragDropFile.js",
119 "mvFirefogg" : "libAddMedia/mvFirefogg.js",
120 "mvAdvFirefogg" : "libAddMedia/mvAdvFirefogg.js",
121 "mvBaseUploadInterface" : "libAddMedia/mvBaseUploadInterface.js",
122 "remoteSearchDriver" : "libAddMedia/remoteSearchDriver.js",
123 "seqRemoteSearchDriver" : "libSequencer/seqRemoteSearchDriver.js",
124
125 "baseRemoteSearch" : "libAddMedia/searchLibs/baseRemoteSearch.js",
126 "mediaWikiSearch" : "libAddMedia/searchLibs/mediaWikiSearch.js",
127 "metavidSearch" : "libAddMedia/searchLibs/metavidSearch.js",
128 "archiveOrgSearch" : "libAddMedia/searchLibs/archiveOrgSearch.js",
129 "flickrSearch" : "libAddMedia/searchLibs/flickrSearch.js",
130 "baseRemoteSearch" : "libAddMedia/searchLibs/baseRemoteSearch.js",
131
132 "mvClipEdit" : "libClipEdit/mvClipEdit.js",
133
134 "embedVideo" : "libEmbedVideo/embedVideo.js",
135 "flashEmbed" : "libEmbedVideo/flashEmbed.js",
136 "genericEmbed" : "libEmbedVideo/genericEmbed.js",
137 "htmlEmbed" : "libEmbedVideo/htmlEmbed.js",
138 "javaEmbed" : "libEmbedVideo/javaEmbed.js",
139 "nativeEmbed" : "libEmbedVideo/nativeEmbed.js",
140 "quicktimeEmbed" : "libEmbedVideo/quicktimeEmbed.js",
141 "vlcEmbed" : "libEmbedVideo/vlcEmbed.js",
142
143 "mvPlayList" : "libSequencer/mvPlayList.js",
144 "mvSequencer" : "libSequencer/mvSequencer.js",
145 "mvFirefoggRender" : "libSequencer/mvFirefoggRender.js",
146 "mvTimedEffectsEdit": "libSequencer/mvTimedEffectsEdit.js",
147
148 "mvTextInterface" : "libTimedText/mvTextInterface.js",
149 "mvTimedTextEdit" : "libTimedText/mvTimedTextEdit.js"
150 });
151
152 // Dependency mapping for CSS files for self-contained included plugins:
153 lcCssPath({
154 '$j.Jcrop' : 'libClipEdit/Jcrop/css/jquery.Jcrop.css',
155 '$j.fn.ColorPicker' : 'libClipEdit/colorpicker/css/colorpicker.css'
156 })
157
158
159 // parseUri 1.2.2
160 // (c) Steven Levithan <stevenlevithan.com>
161 // MIT License
162 function parseUri (str) {
163 var o = parseUri.options,
164 m = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
165 uri = {},
166 i = 14;
167
168 while (i--) uri[o.key[i]] = m[i] || "";
169
170 uri[o.q.name] = {};
171 uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
172 if ($1) uri[o.q.name][$1] = $2;
173 });
174
175 return uri;
176 };
177 parseUri.options = {
178 strictMode: false,
179 key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
180 q: {
181 name: "queryKey",
182 parser: /(?:^|&)([^&=]*)=?([^&]*)/g
183 },
184 parser: {
185 strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
186 loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
187 }
188 };
189
190 // For use when mv_embed with script-loader is in the root MediaWiki path
191 var mediaWiki_mvEmbed_path = 'js2/mwEmbed/';
192
193 //The global scope: will be depreciated once we get everything into $mw
194 var _global = this;
195
196 /*
197 * setup the empty global $mw object
198 * will ensure all our functions and variables are properly namespaced
199 * reducing chance of conflicts
200 */
201 if(!window['$mw']){
202 window['$mw'] = {}
203 }
204
205 //@@todo move these into $mw
206 var global_req_cb = new Array(); // The global request callback array
207
208 // Get the mv_embed location if it has not been set
209 if( !mv_embed_path ) {
210 var mv_embed_path = getMvEmbedPath();
211 }
212 /**
213 * The global $mw object:
214 *
215 * Any global functions/classes that are not jQuery plugins should make
216 * there way into the $mw namespace
217 */
218 (function( $ ) {
219 /*
220 * global config
221 */
222 $.conf = {
223 'skin_name' : 'mvpcf',
224 'jui_skin' : 'redmond',
225 'video_size' : '400x300'
226 }
227 //list valid skins here:
228 $.valid_skins = ['mvpcf', 'kskin'];
229 // the version of mwEmbed
230 $.version = '1.0r21';
231
232 /*
233 * some global containers flags
234 */
235 $.skin_list = new Array();
236 $.init_done = false;
237 $.cb_count = 0;
238 $.player_list = new Array(), // The global player list per page
239 $.req_cb = new Array() // The global request callback array
240
241 /*
242 * Language classes $mw.lang
243 *
244 * Localized Language support attempts to mirror the functionality of Language.php in MediaWiki
245 * It contains methods for loading and transforming msg text
246 *
247 */
248 $.lang = {};
249 /**
250 * Setup the lang object
251 */
252 var gMsg = {};
253 var gRuleSet = {};
254
255 /**
256 * loadGM function
257 * Loads a set of json messages into the lng object.
258 *
259 * @param json msgSet The set of msgs to be loaded
260 */
261 $.lang.loadGM = function( msgSet ){
262 for( var i in msgSet ) {
263 gMsg[ i ] = msgSet[i];
264 }
265 },
266
267 /**
268 * loadRS function
269 * Loads a ruleset by given template key ie PLURAL : { //ruleSetObj }
270 *
271 * @param json ruleSet The ruleset object ( extends gRuleSet )
272 */
273 $.lang.loadRS = function( ruleSet ){
274 for( var i in ruleSet){
275 gRuleSet[ i ] = ruleSet[ i ];
276 }
277 }
278
279 /**
280 * Returns a transformed msg string
281 *
282 * it take a msg key and array of replacement values of form
283 * $1, $2 and does relevant msgkey transformation returning
284 * the user msg.
285 *
286 * @param string key The msg key as set by loadGm
287 * @param [mixed] args An array of replacement strings
288 * @return string
289 */
290 $.lang.gM = function( key , args ) {
291 if(! gMsg[ key ])
292 return '&lt;' + key + '&gt;';// Missing key placeholder
293
294 //swap in the arg values
295 var ms = $.lang.gMsgSwap( key, args) ;
296
297
298
299 //a quick check to see if we need to send the msg via the 'parser'
300 //(we can add more detailed check once we support more wiki syntax)
301 if( ms.indexOf('{{') === -1 && ms.indexOf('[') === -1){
302 return ms;
303 }
304
305 //make sure we have the lagMagic setup:
306 //@@todo move to init
307 $.lang.magicSetup();
308 //send the msg key through the parser
309 var pObj = $.parser.pNew( ms );
310 //return the transformed msg
311 return pObj.getHTML();
312 }
313 /**
314 * gMsgSwap
315 *
316 * @param string key The msg key as set by loadGm
317 * @param [mixed] args An array or string to be replaced
318 * @return string
319 */
320 $.lang.gMsgSwap = function( key , args ){
321 if(! gMsg[ key ])
322 return '&lt;' + key + '&gt;';// Missing key placeholder
323 //get the messege string:
324 var ms = gMsg[ key ];
325
326 //replace values
327 if( typeof args == 'object' || typeof args == 'array' ) {
328 for( var v in args ) {
329 // Message test replace arguments start at 1 instead of zero:
330 var rep = new RegExp('\\$'+ ( parseInt(v) + 1 ), 'g');
331 ms = ms.replace( rep, args[v] );
332 }
333 } else if( typeof args =='string' || typeof args =='number' ) {
334 ms = ms.replace(/\$1/g, args);
335 }
336 return ms;
337 }
338
339 /**
340 * gMsgNoTrans
341 *
342 * @returns string The msg key without transforming it
343 */
344 $.lang.gMsgNoTrans = function( key ){
345 if( gMsg[ key ] )
346 return gMsg[ key ]
347
348 // Missing key placeholder
349 return '&lt;' + key + '&gt;';
350 }
351 /**
352 * Add Supported Magic Words to parser
353 */
354 //set the setupflag to false:
355 $.lang.doneSetup=false;
356 $.lang.magicSetup = function(){
357 if(!$.lang.doneSetup){
358 $.parser.addMagic ( {
359 'PLURAL' : $.lang.procPLURAL
360 })
361
362 $.lang.doneSetup = true;
363 }
364
365 }
366 /**
367 * Process the PLURAL special language template key:
368 */
369 $.lang.procPLURAL = function( tObj ){
370 //setup shortcuts
371 // (gRuleSet is loaded from script-loader to contains local ruleset)
372 var rs = gRuleSet['PLURAL'];
373
374 /*
375 * Plural matchRuleTest
376 */
377 function matchRuleTest(cRule, val){
378 //js_log("matchRuleTest:: " + typeof cRule + ' ' + cRule + ' == ' + val );
379
380 function checkValue(compare, val){
381 if(typeof compare == 'string'){
382 range = compare.split('-');
383 if( range.length >= 1 ){
384 if( val >= range[0] && val <= range[1] )
385 return true;
386 }
387 }
388 //else do a direct compare
389 if(compare == val){
390 return true;
391 }
392 return false;
393 }
394 //check for simple cRule type:
395 if( typeof cRule == 'number'){
396 return ( parseInt( val ) == parseInt( cRule) );
397 }else if( typeof cRule == 'object' ){
398 var cmatch = {};
399 //if a list we need to match all for rule match
400 for(var i in cRule){
401 var cr = cRule[i];
402 //set cr type
403 var crType = '';
404 for( var j in cr ){
405 if(j == 'mod')
406 crType = 'mod'
407 }
408 switch(crType){
409 case 'mod':
410 if( cr ['is'] ){
411 if( checkValue( val % cr['mod'], cr ['is'] ) )
412 cmatch[i] = true;
413 }else if( cr['not']){
414 if( ! checkValue( val % cr['mod'], cr ['not'] ) )
415 cmatch[i] = true;
416 }
417 break;
418 }
419 }
420 //check all the matches (taking into consideration "or" order)
421 for(var i in cRule){
422 if( ! cmatch[i] )
423 return false;
424 }
425 return true;
426
427 }
428 }
429 /**
430 * Maps a given rule Index to template params:
431 *
432 * if index is out of range return last param
433 * @param
434 */
435 function getTempParamFromRuleInx(tObj, ruleInx ){
436 //js_log('getTempParamFromRuleInx: ruleInx: ' + ruleInx + ' tempParamLength ' + tObj.param.length );
437 if( ruleInx >= tObj.param.length )
438 return tObj.param[ tObj.param.length -1 ];
439 //else return the requested index:
440 return tObj.param[ ruleInx ];
441 }
442 var rCount=0
443 //run the actual rule lookup:
444 for(var ruleInx in rs){
445 cRule = rs[ruleInx];
446 if( matchRuleTest( cRule, tObj.arg ) ){
447 //js_log("matched rule: " + ruleInx );
448 return getTempParamFromRuleInx(tObj, rCount );
449 }
450 rCount ++;
451 }
452 //js_log('no match found for: ' + tObj.arg + ' using last/other : ' + tObj.param [ tObj.param.length -1 ] );
453 //return the last /"other" template param
454 return tObj.param [ tObj.param.length -1 ];
455 }
456
457 /**
458 * gMsgLoadRemote loads remote msg strings
459 *
460 * @param mixed msgSet the set of msg to load remotely
461 * @param function callback the callback to issue once string is ready
462 */
463 $.lang.gMsgLoadRemote = function( msgSet, callback ) {
464 var ammessages = '';
465 if( typeof msgSet == 'object' ) {
466 for( var i in msgSet ) {
467 ammessages += msgSet[i] + '|';
468 }
469 } else if( typeof msgSet == 'string' ) {
470 ammessages += msgSet;
471 }
472 if( ammessages == '' ) {
473 js_log( 'gMsgLoadRemote: no message set requested' );
474 return false;
475 }
476 do_api_req({
477 'data': {
478 'meta': 'allmessages',
479 'ammessages': ammessages
480 }
481 }, function( data ) {
482 if( data.query.allmessages ) {
483 var msgs = data.query.allmessages;
484 for( var i in msgs ) {
485 var ld = {};
486 ld[ msgs[i]['name'] ] = msgs[i]['*'];
487 loadGM( ld );
488 }
489 }
490 callback();
491 });
492 }
493 /**
494 * Format a size in bytes for output, using an appropriate
495 * unit (B, KB, MB or GB) according to the magnitude in question
496 *
497 * @param size Size to format
498 * @return string Plain text (not HTML)
499 */
500 $.lang.formatSize = function ( size ) {
501 // For small sizes no decimal places are necessary
502 var round = 0;
503 var msg = '';
504 if( size > 1024 ) {
505 size = size / 1024;
506 if( size > 1024 ) {
507 size = size / 1024;
508 // For MB and bigger two decimal places are smarter
509 round = 2;
510 if( size > 1024 ) {
511 size = size / 1024;
512 msg = 'mwe-size-gigabytes';
513 } else {
514 msg = 'mwe-size-megabytes';
515 }
516 } else {
517 msg = 'mwe-size-kilobytes';
518 }
519 } else {
520 msg = 'mwe-size-bytes';
521 }
522 // JavaScript does not let you choose the precision when rounding
523 var p = Math.pow(10,round);
524 var size = Math.round( size * p ) / p;
525 //@@todo we need a formatNum and we need to request some special packaged info to deal with that case.
526 return gM( msg , size );
527 };
528
529 $.lang.formatNumber = function( num ){
530 /*
531 addSeparatorsNF
532 Str: The number to be formatted, as a string or number.
533 outD: The decimal character for the output, such as ',' for the number 100,2
534 sep: The separator character for the output, such as ',' for the number 1,000.2
535 */
536 function addSeparatorsNF(nStr, outD, sep){
537 nStr += '';
538 var dpos = nStr.indexOf( '.' );
539 var nStrEnd = '';
540 if (dpos != -1) {
541 nStrEnd = outD + nStr.substring(dpos + 1, nStr.length);
542 nStr = nStr.substring(0, dpos);
543 }
544 var rgx = /(\d+)(\d{3})/;
545 while (rgx.test(nStr)) {
546 nStr = nStr.replace(rgx, '$1' + sep + '$2');
547 }
548 return nStr + nStrEnd;
549 }
550 //@@todo read language code and give periods or comas:
551 return addSeparatorsNF( num, '.', ',');
552 }
553
554
555
556 /**
557 * MediaWiki wikitext "Parser"
558 *
559 * This is not feature complete but we need a way to get at template properties
560 *
561 *
562 * @param wikiText the wikitext to be parsed
563 * @return parserObj returns a parser object that has methods for getting at
564 * things you would want
565 */
566 $.parser = {};
567 var pMagicSet = {};
568 /**
569 * parser addMagic
570 *
571 * lets you add a set of magic keys and associated callback functions
572 * callback: @param ( Object Template )
573 * callback: @return the transformed template output
574 *
575 * @param object magicSet key:callback
576 */
577 $.parser.addMagic = function( magicSet ){
578 for(var i in magicSet)
579 pMagicSet[ i ] = magicSet[i];
580 }
581
582 //actual parse call (returns parser object)
583 $.parser.pNew = function( wikiText, opt ){
584 var parseObj = function( wikiText, opt){
585 return this.init( wikiText, opt )
586 }
587 parseObj.prototype = {
588 //the wikiText "DOM"... stores the parsed wikiText structure
589 //wtDOM : {}, (not yet supported )
590
591 pOut : '', //the parser output string container
592 init :function( wikiText ){
593 this.wikiText = wikiText;
594 },
595 updateText : function( wikiText ){
596 this.wikiText = wikiText;
597 //invalidate the output (will force a re-parse )
598 this.pOut = '';
599 },
600 parse : function(){
601 /*
602 * quickly recursive / parse out templates:
603 */
604
605 // ~ probably a better algorithm out there / should mirror php parser flow ~
606 // (we are already running white-space issues ie php parse strips whitespace diffrently)
607
608 // ... but I am having fun with recursion so here it is...
609 // or at least mirror: http://www.mediawiki.org/wiki/Extension:Page_Object_Model
610 function rdpp ( txt , cn){
611 var node = {};
612 //inspect each char
613 for(var a=0; a < txt.length; a++){
614 if( txt[a] == '{' && txt[a+1] == '{' ){
615 a=a+2;
616 node['p'] = node;
617 if(!node['c'])
618 node['c'] = new Array();
619
620 node['c'].push( rdpp( txt.substr( a ), true ) );
621 }else if( txt[a] == '}' && txt[a+1] == '}'){
622 a=a+2;
623 if( !node['p'] ){
624 return node;
625 }
626 node = node['p'];
627 }
628 if(!node['t'])
629 node['t']='';
630 //dont put closures into output:
631 if( txt[a] && txt[a]!='}' )
632 node['t'] += txt[a];
633
634 }
635 return node;
636 }
637 /**
638 * parse template text as template name and named params
639 */
640 function parseTmplTxt( ts ){
641 var tObj = {};
642 //Get template name:
643 tname = ts.split('\|').shift() ;
644 tname = tname.split('\{').shift() ;
645 tname = tname.replace( /^\s+|\s+$/g, "" ); //trim
646
647 //check for arguments:
648 if( tname.split(':').length == 1 ){
649 tObj["name"] = tname;
650 }else{
651 tObj["name"] = tname.split(':').shift();
652 tObj["arg"] = tname.split(':').pop();
653 }
654
655 //js_log("TNAME::" + tObj["name"] + ' from:: ' + ts);
656 var pSet = ts.split('\|');
657 pSet.splice(0,1);
658 if( pSet.length ){
659 tObj.param = new Array();
660 for(var pInx in pSet){
661 var tStr = pSet[ pInx ];
662 //check for empty param
663 if(tStr==''){
664 tObj.param[ pInx ] = '';
665 continue;
666 }
667 for(var b=0 ; b < tStr.length ; b++){
668 if(tStr[b] == '=' && b>0 && b<tStr.length && tStr[b-1]!='\\'){
669 //named param
670 tObj.param[ tStr.split('=').shift() ] = tStr.split('=').pop();
671 }else{
672 //indexed param
673 tObj.param[ pInx ] = tStr;
674 }
675 }
676 }
677 }
678 return tObj;
679 }
680 function getMagicTxtFromTempNode( node ){
681 node.tObj = parseTmplTxt ( node.t );
682 //do magic swap if template key found in pMagicSet
683 if( node.tObj.name in pMagicSet){
684 var nt = pMagicSet[ node.tObj.name ]( node.tObj );
685 return nt;
686 }else{
687 //don't swap just return text
688 return node.t;
689 }
690 }
691 /**
692 * recurse_magic_swap
693 *
694 * go last child first swap upward: (could probably be integrated above somehow)
695 */
696 var pNode = null;
697 function recurse_magic_swap( node ){
698 if( !pNode )
699 pNode = node;
700
701 if( node['c'] ){
702 //swap all the kids:
703 for(var i in node['c']){
704 var nt = recurse_magic_swap( node['c'][i] );
705 //swap it into current
706 if( node.t ){
707 node.t = node.t.replace( node['c'][i].t, nt);
708 }
709 //swap into parent
710 pNode.t = pNode.t.replace( node['c'][i].t, nt);
711 }
712 //do the current node:
713 var nt = getMagicTxtFromTempNode( node );
714 pNode.t = pNode.t.replace(node.t , nt);
715 //run the swap for the outer most node
716 return node.t;
717 }else{
718 //node.t = getMagicFromTempObj( node.t )
719 return getMagicTxtFromTempNode( node );
720 }
721 }
722 //parse out the template node structure:
723 this.pNode = rdpp ( this.wikiText );
724 //strip out the parent from the root
725 this.pNode['p'] = null;
726
727 //do the recursive magic swap text:
728 this.pOut = recurse_magic_swap( this.pNode );
729
730 },
731
732 /*
733 * parsed template api ~losely based off of ~POM~
734 * http://www.mediawiki.org/wiki/Extension:Page_Object_Model
735 */
736
737 /**
738 * templates
739 *
740 * gets a requested template from the wikitext (if available)
741 *
742 */
743 templates: function( tname ){
744 this.parse();
745 var tmplSet = new Array();
746 function getMatchingTmpl( node ){
747 if( node['c'] ){
748 for(var i in node['c']){
749 getMatchingTmpl( node['c'] );
750 }
751 }
752 if( tname && node.tObj){
753 if( node.tObj['name'] == tname )
754 tmplSet.push( node.tObj );
755 }else if( node.tObj ){
756 tmplSet.push( node.tObj );
757 }
758 }
759 getMatchingTmpl( this.pNode );
760 return tmplSet;
761 },
762 /**
763 * Returns the transformed wikitext
764 *
765 * Build output from swappable index
766 * (all transforms must be expanded in parse stage and linearly rebuilt)
767 * Alternatively we could build output using a place-holder & replace system
768 * (this lets us be slightly more slopy with ordering and indexes, but probably slower)
769 *
770 * Ideal: we build a 'wiki DOM'
771 * When editing you update the data structure directly
772 * Then in output time you just go DOM->html-ish output without re-parsing anything
773 */
774 getHTML : function(){
775 //wikiText updates should invalidate pOut
776 if( this.pOut == ''){
777 this.parse();
778 }
779 return this.pOut;
780 }
781 };
782 //return the parserObj
783 return new parseObj( wikiText, opt) ;
784 }
785
786 })(window.$mw);
787
788 //setup legacy global shortcuts:
789 var loadGM = $mw.lang.loadGM;
790 var loadRS = $mw.lang.loadRS;
791 var gM = $mw.lang.gM;
792
793 // All default messages in [English] should be overwritten by the CMS language message system.
794 $mw.lang.loadGM({
795 "mwe-loading_txt" : "Loading ...",
796 "mwe-size-gigabytes" : "$1 GB",
797 "mwe-size-megabytes" : "$1 MB",
798 "mwe-size-kilobytes" : "$1 K",
799 "mwe-size-bytes" : "$1 B",
800 "mwe-error_load_lib" : "Error: JavaScript $1 was not retrievable or does not define $2",
801 "mwe-loading-add-media-wiz" : "Loading add media wizard",
802 "mwe-apiproxy-setup" : "Setting up API proxy",
803 "mwe-load-drag-item" : "Loading dragged item",
804 "mwe-ok" : "OK",
805 "mwe-cancel" : "Cancel"
806 });
807
808
809 // Get the loading image
810 function mv_get_loading_img( style, class_attr ){
811 var style_txt = (style)?style:'';
812 var class_attr = (class_attr) ? 'class="' + class_attr + '"' : 'class="mv_loading_img"';
813 return '<div '+class_attr+' style="' + style +'"></div>';
814 }
815
816 function mv_set_loading(target, load_id){
817 var id_attr = ( load_id )?' id="' + load_id + '" ':'';
818 $j(target).append('<div '+id_attr+' style="position:absolute;top:0px;left:0px;height:100%;width:100%;'+
819 'background-color:#FFF;">' +
820 mv_get_loading_img('top:30px;left:30px') +
821 '</div>');
822 }
823
824 /**
825 * mvJsLoader class handles initialization and js file loads
826 */
827 var mvJsLoader = {
828 libreq : {},
829 libs : {},
830
831 // Base lib flags
832 onReadyEvents: new Array(),
833 doneReadyEvents: false,
834 jQuerySetupFlag: false,
835
836 // To keep consistency across threads
837 ptime: 0,
838 ctime: 0,
839
840 load_error: false, // Load error flag (false by default)
841 load_time: 0,
842 callbacks: new Array(),
843 cur_path: null,
844 missing_path : null,
845 doLoad: function( loadLibs, callback ) {
846 this.ctime++;
847 if( loadLibs && loadLibs.length != 0 ) {
848 //js_log("doLoad setup::" + JSON.stringify( loadLibs ) );
849 // Set up this.libs
850 // First check if we already have this library loaded
851 var all_libs_loaded = true;
852 for( var i = 0; i< loadLibs.length; i++ ) {
853 // Check if the library is already loaded
854 if( ! this.checkObjPath( loadLibs[i] ) ) {
855 all_libs_loaded = false;
856 }
857 }
858 if( all_libs_loaded ) {
859 js_log( 'Libraries ( ' + loadLibs + ') already loaded... skipping load request' );
860 callback();
861 return;
862 }
863 // Do a check for any CSS we may need and get it
864 for( var i = 0; i < loadLibs.length; i++ ) {
865 if( typeof mvCssPaths[ loadLibs[i] ] != 'undefined' ) {
866 loadExternalCss( mvCssPaths[ loadLibs[i] ] );
867 }
868 }
869
870 // Check if we should use the script loader to combine all the requests into one
871 if( typeof mwSlScript != 'undefined' ) {
872 var class_set = '';
873 var last_class = '';
874 var coma = '';
875 for( var i = 0; i < loadLibs.length; i++ ) {
876 var curLib = loadLibs[i];
877 // Only add if not included yet:
878 if( ! this.checkObjPath( curLib ) ) {
879 class_set += coma + curLib;
880 last_class = curLib;
881 coma = ',';
882 }
883 }
884 //Build the url to the scriptServer striping its request parameters:
885 var puri = parseUri( getMvEmbedURL() );
886 if( ( getMvEmbedURL().indexOf('://') != -1 )
887 && puri.host != parseUri( document.URL ).host )
888 {
889 var scriptPath = puri.protocol + '://' + puri.authority + puri.path;
890 }else{
891 var scriptPath = puri.path;
892 }
893 //js_log('scriptServer Path is: ' + scriptPath + "\n host script path:" + getMvEmbedURL() );
894 this.libs[ last_class ] = scriptPath + '?class=' + class_set +
895 '&' + getMwReqParam();
896
897 } else {
898 // Do many requests
899 for( var i = 0; i < loadLibs.length; i++ ) {
900 var curLib = loadLibs[i];
901 if( curLib ) {
902 var libLoc = mvGetClassPath( curLib );
903 // Do a direct load of the file (pass along unique request id from
904 // request or mv_embed Version )
905 var qmark = (libLoc.indexOf( '?' ) !== true) ? '?' : '&';
906 this.libs[curLib] = mv_embed_path + libLoc + qmark + getMwReqParam();
907 }
908 }
909 }
910 }
911
912 if( callback ) {
913 this.callbacks.push( callback );
914 }
915 if( this.checkLoading() ) {
916 //@@todo we should check the <script> Element .onLoad property to
917 //make sure its just not a very slow connection
918 //(even though the class is not loaded)
919 if( this.load_time++ > 4000 ){ // Time out after ~80 seconds
920 js_log( gM('mwe-error_load_lib', [mvGetClassPath(this.missing_path), this.missing_path]) );
921 this.load_error = true;
922 } else {
923 setTimeout( 'mvJsLoader.doLoad()', 20 );
924 }
925 } else {
926 //js_log('checkLoading passed. Running callbacks...');
927 // Only do callbacks if we are in the same instance (weird concurrency issue)
928 var cb_count=0;
929 for( var i = 0; i < this.callbacks.length; i++ )
930 cb_count++;
931 //js_log('RESET LIBS: loading is: '+ loading + ' callback count: '+cb_count +
932 // ' p:'+ this.ptime +' c:'+ this.ctime);
933
934 // Reset the libs
935 this.libs = {};
936 //js_log('done loading, do call: ' + this.callbacks[0] );
937 while( this.callbacks.length != 0 ) {
938 if( this.ptime == this.ctime - 1 ) { // Enforce thread consistency
939 this.callbacks.pop()();
940 //func = this.callbacks.pop();
941 //js_log(' run: '+this.ctime+ ' p: ' + this.ptime + ' ' +loading+ ' :'+ func);
942 //func();
943 } else {
944 // Re-issue doLoad ( ptime will be set to ctime so we should catch up)
945 setTimeout( 'mvJsLoader.doLoad()', 25 );
946 break;
947 }
948 }
949 }
950 this.ptime = this.ctime;
951 },
952 doLoadDepMode: function( loadChain, callback ) {
953 // Firefox executes JS in the order in which it is included, so just directly issue the request
954 if( $j.browser.firefox ) {
955 var loadSet = [];
956 for( var i = 0; i < loadChain.length; i++ ) {
957 for( var j = 0; j < loadChain[i].length; j++ ) {
958 loadSet.push( loadChain[i][j] );
959 }
960 }
961 mvJsLoader.doLoad( loadSet, callback );
962 } else {
963 // Safari and IE tend to execute out of order so load with dependency checks
964 mvJsLoader.doLoad( loadChain.shift(), function() {
965 if( loadChain.length != 0 ) {
966 mvJsLoader.doLoadDepMode( loadChain, callback );
967 } else {
968 callback();
969 }
970 });
971 }
972 },
973 checkLoading: function() {
974 var loading = 0;
975 var i = null;
976 for( var i in this.libs ) { // for/in loop is OK on an object
977 if( !this.checkObjPath( i ) ) {
978 if( !this.libreq[i] ) {
979 loadExternalJs( this.libs[i] );
980 }
981 this.libreq[i] = 1;
982 //js_log("has not yet loaded: " + i);
983 loading = 1;
984 }
985 }
986 return loading;
987 },
988 checkObjPath: function( libVar ) {
989 if( !libVar )
990 return false;
991 var objPath = libVar.split( '.' )
992 var cur_path = '';
993 for( var p = 0; p < objPath.length; p++ ) {
994 cur_path = (cur_path == '') ? cur_path + objPath[p] : cur_path + '.' + objPath[p];
995 eval( 'var ptest = typeof ( '+ cur_path + ' ); ');
996 if( ptest == 'undefined' ) {
997 this.missing_path = cur_path;
998 return false;
999 }
1000 }
1001 this.cur_path = cur_path;
1002 return true;
1003 },
1004 /**
1005 * checks for jQuery and adds the $j noConflict var
1006 */
1007 jQueryCheck: function( callback ) {
1008 //js_log( 'jQueryCheck::' );
1009 var _this = this;
1010 // Skip stuff if $j is already loaded:
1011 if( _global['$j'] && callback ){
1012 callback();
1013 if( _this.jQuerySetupFlag )
1014 return ;
1015 }
1016 // Load jQuery
1017 _this.doLoad([
1018 'window.jQuery'
1019 ], function() {
1020 //only do the $j setup once:
1021 if(!_global['$j']){
1022 _global['$j'] = jQuery.noConflict();
1023 }
1024 if( _this.jQuerySetupFlag == false){
1025 //js_log('setup mv_embed jQuery bindings');
1026 //setup our global settings using the (jQuery helper)
1027
1028 // Set up the skin path
1029 _global['mv_jquery_skin_path'] = mv_embed_path + 'jquery/jquery.ui/themes/' + $mw.conf['jui_skin'] + '/';
1030 _global['mv_skin_img_path'] = mv_embed_path + 'skins/' + $mw.conf['skin_name'] + '/images/';
1031 _global['mv_default_thumb_url'] = mv_skin_img_path + 'vid_default_thumb.jpg';
1032
1033 // Make sure the skin/style sheets are always available:
1034 loadExternalCss( mv_jquery_skin_path + 'jquery-ui-1.7.1.custom.css' );
1035 loadExternalCss( mv_embed_path + 'skins/' + $mw.conf['skin_name'] + '/styles.css' );
1036
1037 // Set up AJAX to not send dynamic URLs for loading scripts (we control that with
1038 // the scriptLoader)
1039 $j.ajaxSetup({
1040 cache: true
1041 });
1042
1043 js_log( 'jQuery loaded into $j' );
1044 // Set up mvEmbed jQuery bindings and config based dependencies
1045 mv_jqueryBindings();
1046 _this.jQuerySetupFlag = true;
1047 }
1048 // Run the callback
1049 if( callback ) {
1050 callback();
1051 }
1052 });
1053 },
1054 embedVideoCheck:function( callback ) {
1055 var _this = this;
1056 js_log( 'embedVideoCheck:' );
1057 // Make sure we have jQuery
1058 _this.jQueryCheck( function() {
1059 //set class videonojs to loading
1060 $j('.videonojs').html( gM('mwe-loading_txt') );
1061 //Set up the embed video player class request: (include the skin js as well)
1062 var depReq = [
1063 [
1064 '$j.ui',
1065 'embedVideo',
1066 'ctrlBuilder',
1067 '$j.cookie'
1068 ],
1069 [
1070 '$j.ui.slider'
1071 ]
1072 ];
1073
1074 //add any requested skins (suports multiple skins per single page)
1075 if( $mw.skin_list ){
1076 for(var i in $mw.skin_list ){
1077 depReq[0].push( $mw.skin_list[i] + 'Config' );
1078 }
1079 }
1080
1081
1082 // Add PNG fix if needed:
1083 if( $j.browser.msie || $j.browser.version < 7 )
1084 depReq[0].push( '$j.fn.pngFix' );
1085
1086 //load the video libs:
1087 _this.doLoadDepMode( depReq, function() {
1088 embedTypes.init();
1089 callback();
1090 $j('.videonojs').remove();
1091 });
1092 });
1093 },
1094 addLoadEvent: function( fn ) {
1095 this.onReadyEvents.push( fn );
1096 },
1097 // Check the jQuery flag. This way, when remote embedding, we don't load jQuery
1098 // unless js2AddOnloadHook was used or there is video on the page.
1099 runQueuedFunctions: function() {
1100 js_log("runQueuedFunctions");
1101 var _this = this;
1102 this.jQueryCheck( function() {
1103 _this.runReadyEvents();
1104 _this.doneReadyEvents = true;
1105 });
1106 },
1107 runReadyEvents: function() {
1108 js_log( "runReadyEvents" );
1109 while( this.onReadyEvents.length ) {
1110 var func = this.onReadyEvents.shift();
1111 //js_log('run onReady:: ' + func );
1112 func();
1113 }
1114 }
1115 }
1116
1117 // Shortcut ( @@todo consolidate shortcuts & re-factor mvJsLoader )
1118 function mwLoad( loadSet, callback ) {
1119 mvJsLoader.doLoad( loadSet, callback );
1120 }
1121 //$mw.shortcut
1122 $mw.load = mwLoad;
1123
1124 // Load an external JS file. Similar to jquery .require plugin,
1125 // but checks for object availability rather than load state.
1126
1127 /*********** INITIALIZATION CODE *************
1128 * This will get called when the DOM is ready
1129 *********************************************/
1130 /* jQuery .ready does not work when jQuery is loaded dynamically.
1131 * For an example of the problem see: 1.1.3 working: http://pastie.caboo.se/92588
1132 * and >= 1.1.4 not working: http://pastie.caboo.se/92595
1133 * $j(document).ready( function(){ */
1134 function mwdomReady( force ) {
1135 js_log( 'f:mwdomReady:' );
1136 if( !force && $mw.init_done ) {
1137 js_log( "mw done, do nothing..." );
1138 return false;
1139 }
1140 $mw.init_done = true;
1141 // Handle the execution of queued functions with jQuery "ready"
1142
1143 // Check if this page has a video, audio or playlist tag
1144 var e = [
1145 document.getElementsByTagName( "video" ),
1146 document.getElementsByTagName( "audio" ),
1147 document.getElementsByTagName( "playlist" )
1148 ];
1149 if( e[0].length != 0 || e[1].length != 0 || e[2].length != 0 ) {
1150 //look for any skin classes we have to load:
1151 for(var j in e){
1152 for(var k in e[j]){
1153 if(e[j][k] && typeof( e[j][k]) == 'object'){
1154 var sn = e[j][k].getAttribute('class');
1155 if( sn && sn != ''){
1156 for(var n=0;n< $mw.valid_skins.length;n++){
1157 if( sn.indexOf($mw.valid_skins[n]) !== -1){
1158 $mw.skin_list.push( $mw.valid_skins[n] );
1159 }
1160 }
1161 }
1162 }
1163 }
1164 }
1165 // Load libs and process videos
1166 mvJsLoader.embedVideoCheck( function() {
1167 // Run any queued global events:
1168 mv_video_embed( function() {
1169 mvJsLoader.runQueuedFunctions();
1170 });
1171 });
1172 } else {
1173 mvJsLoader.runQueuedFunctions();
1174 }
1175 }
1176
1177 //js2AddOnloadHook: ensure jQuery and the DOM are ready
1178 function js2AddOnloadHook( func ) {
1179 //js_log('js2AddOnloadHook:: jquery:' + typeof window.jQuery + ' $j: ' + typeof $j);
1180 //check for jQuery then add the load event (to run after video tag rewrites (if present)
1181 mvJsLoader.jQueryCheck( function() {
1182 if( mvJsLoader.doneReadyEvents ) {
1183 //js_log('run queued event: ' + func);
1184 func();
1185 } else {
1186 mvJsLoader.addLoadEvent( func );
1187 }
1188 });
1189 }
1190 // Deprecated mwAddOnloadHook in favor of js2 naming (for clear separation of js2 code from old MW code
1191 var mwAddOnloadHook = js2AddOnloadHook;
1192 /*
1193 * This function allows for targeted rewriting
1194 */
1195 function rewrite_by_id( vid_id, ready_callback ) {
1196 js_log( 'f:rewrite_by_id: ' + vid_id );
1197 // Force a re-check of the DOM for playlist or video elements:
1198 mvJsLoader.embedVideoCheck( function() {
1199 mv_video_embed( ready_callback, vid_id );
1200 });
1201 }
1202
1203
1204 /*********** INITIALIZATION CODE *************
1205 * set DOM-ready callback to init_mv_embed
1206 *********************************************/
1207 // for Mozilla / modern browsers
1208 if ( document.addEventListener ) {
1209 document.addEventListener( "DOMContentLoaded", mwdomReady, false );
1210 }
1211 var temp_f;
1212 if( window.onload ) {
1213 temp_f = window.onload;
1214 }
1215 // Use the onload method as a backup
1216 window.onload = function () {
1217 if( temp_f )
1218 temp_f();
1219 mwdomReady();
1220 }
1221
1222 /*
1223 * Store all the mwEmbed jQuery-specific bindings
1224 * (set up after jQuery is available).
1225 *
1226 * These functions are genneraly are loaders that do the dynamic mapping of
1227 * dependencies for a given commponet
1228 *
1229 *
1230 */
1231 function mv_jqueryBindings() {
1232 js_log( 'mv_jqueryBindings' );
1233 (function( $ ) {
1234 /*
1235 * dragDrop file loader
1236 */
1237 $.fn.dragFileUpload = function ( conf ){
1238 if( this.selector ){
1239 var _this = this;
1240 //load the dragger and "setup"
1241 $mw.load( ['$j.fn.dragDropFile'], function(){
1242 $j(_this.selector).dragDropFile();
1243 });
1244 }
1245 }
1246 /*
1247 * apiProxy Loader loader:
1248 *
1249 * @param mode is either 'server' or 'client'
1250 */
1251 $.apiProxy = function( mode, pConf, callback ){
1252 js_log('do apiProxy setup');
1253 mvJsLoader.doLoad( [
1254 '$mw.proxy',
1255 'JSON'
1256 ], function(){
1257 //do the proxy setup or
1258 if( mode == 'client'){
1259 //just do the setup (no callbcak for client setup)
1260 $mw.proxy.client( pConf );
1261 if( callback )
1262 callback();
1263 }else if( mode=='server' ){
1264 //do the request with the callback
1265 $mw.proxy.server( pConf , callback );
1266 }
1267 });
1268 }
1269
1270 //non selector based add-media-wizard direct invocation with loader
1271 $.addMediaWiz = function( iObj, callback ){
1272 js_log(".addMediaWiz call");
1273 //check if already loaded:
1274 if( _global['rsdMVRS'] ){
1275 _global['rsdMVRS'].doReDisplay();
1276 if( callback )
1277 callback( _global['rsdMVRS'] );
1278 return ;
1279 }
1280 //display a loader:
1281 $.addLoaderDialog( gM('mwe-loading-add-media-wiz') );
1282 //load the addMedia wizard without a target:
1283 $.fn.addMediaWiz ( iObj, function( amwObj ){
1284 //close the dialog
1285 $.closeLoaderDialog();
1286 //do the add-media-wizard display
1287 amwObj.doInitDisplay();
1288 //call the parent callback:
1289 if( callback )
1290 callback( _global['rsdMVRS'] );
1291 });
1292 }
1293 $.fn.addMediaWiz = function( iObj, callback ) {
1294 if( this.selector ){
1295 // First set the cursor for the button to "loading"
1296 $j( this.selector ).css( 'cursor', 'wait' ).attr( 'title', gM( 'mwe-loading_txt' ) );
1297 //set the target:
1298 iObj['target_invocation'] = this.selector;
1299 }
1300
1301 // Load the mv_embed_base skin:
1302 loadExternalCss( mv_jquery_skin_path + 'jquery-ui-1.7.1.custom.css' );
1303 loadExternalCss( mv_embed_path + 'skins/' + $mw.conf['skin_name'] + '/styles.css' );
1304 // Load all the required libs:
1305 mvJsLoader.jQueryCheck( function() {
1306 // Load with staged dependencies (for IE that does not execute in order)
1307 mvJsLoader.doLoadDepMode([
1308 [ 'remoteSearchDriver',
1309 '$j.cookie',
1310 '$j.fn.textSelection',
1311 '$j.ui'
1312 ],[
1313 '$j.ui.resizable',
1314 '$j.ui.draggable',
1315 '$j.ui.dialog',
1316 '$j.ui.tabs',
1317 '$j.ui.sortable'
1318 ]
1319 ], function() {
1320 iObj['instance_name'] = 'rsdMVRS';
1321 if( ! _global['rsdMVRS'] )
1322 _global['rsdMVRS'] = new remoteSearchDriver( iObj );
1323 if( callback ) {
1324 callback( _global['rsdMVRS'] );
1325 }
1326 });
1327 });
1328 }
1329 /*
1330 * Sequencer loader
1331 */
1332 $.fn.sequencer = function( iObj, callback ) {
1333 // Debugger
1334 iObj['target_sequence_container'] = this.selector;
1335 // Issue a request to get the CSS file (if not already included):
1336 loadExternalCss( mv_jquery_skin_path + 'jquery-ui-1.7.1.custom.css' );
1337 loadExternalCss( mv_embed_path + 'skins/' + $mw.conf['skin_name'] + '/mv_sequence.css' );
1338 // Make sure we have the required mv_embed libs (they are not loaded when no video
1339 // element is on the page)
1340 mvJsLoader.embedVideoCheck( function() {
1341 // Load the playlist object and then the jQuery UI stuff:
1342 mvJsLoader.doLoadDepMode([
1343 [
1344 'mvPlayList',
1345 '$j.ui',
1346 '$j.contextMenu',
1347 'JSON',
1348 'mvSequencer'
1349 ],
1350 [
1351 '$j.ui.accordion',
1352 '$j.ui.dialog',
1353 '$j.ui.droppable',
1354 '$j.ui.draggable',
1355 '$j.ui.progressbar',
1356 '$j.ui.sortable',
1357 '$j.ui.resizable',
1358 '$j.ui.slider',
1359 '$j.ui.tabs'
1360 ]
1361 ], function() {
1362 js_log( 'calling new mvSequencer' );
1363 // Initialise the sequence object (it will take over from there)
1364 // No more than one mvSeq obj for now:
1365 if( !_global['mvSeq'] ) {
1366 _global['mvSeq'] = new mvSequencer( iObj );
1367 } else {
1368 js_log( 'mvSeq already init' );
1369 }
1370 });
1371 });
1372 }
1373 /*
1374 * The Firefogg jQuery function:
1375 * @@note This Firefogg invocation could be made to work more like real jQuery plugins
1376 */
1377 var queuedFirefoggConf = {};
1378 $.fn.firefogg = function( iObj, callback ) {
1379 if( !iObj )
1380 iObj = {};
1381 // Add the base theme CSS:
1382 loadExternalCss( mv_jquery_skin_path + 'jquery-ui-1.7.1.custom.css' );
1383 loadExternalCss( mv_embed_path + 'skins/' + $mw.conf['skin_name'] + '/styles.css' );
1384
1385 // Check if we already have Firefogg loaded (the call just updates the element's
1386 // properties)
1387 var sElm = $j( this.selector ).get( 0 );
1388 if( sElm['firefogg'] ) {
1389 if( sElm['firefogg'] == 'loading' ) {
1390 js_log( "Queued firefogg operations ( firefogg " +
1391 "not done loading ) " );
1392 $j.extend( queuedFirefoggConf, iObj );
1393 return false;
1394 }
1395 // Update properties
1396 for( var i in iObj ) {
1397 js_log( "firefogg::updated: " + i + ' to '+ iObj[i] );
1398 sElm['firefogg'][i] = iObj[i];
1399 }
1400 return sElm['firefogg'];
1401 } else {
1402 // Avoid concurency
1403 sElm['firefogg'] = 'loading';
1404 }
1405 // Add the selector
1406 iObj['selector'] = this.selector;
1407
1408 var loadSet = [
1409 [
1410 'mvBaseUploadInterface',
1411 'mvFirefogg',
1412 '$j.ui'
1413 ],
1414 [
1415 '$j.ui.progressbar',
1416 '$j.ui.dialog',
1417 '$j.ui.draggable'
1418 ]
1419 ];
1420 if( iObj.encoder_interface ) {
1421 loadSet.push([
1422 'mvAdvFirefogg',
1423 '$j.cookie',
1424 '$j.ui.accordion',
1425 '$j.ui.slider',
1426 '$j.ui.datepicker'
1427 ]);
1428 }
1429 // Make sure we have everything loaded that we need:
1430 mvJsLoader.doLoadDepMode( loadSet, function() {
1431 js_log( 'firefogg libs loaded. target select:' + iObj.selector );
1432 // Select interface provider based on whether we want to include the
1433 // encoder interface or not
1434 if( iObj.encoder_interface ) {
1435 var myFogg = new mvAdvFirefogg( iObj );
1436 } else {
1437 var myFogg = new mvFirefogg( iObj );
1438 }
1439 if( myFogg ) {
1440 myFogg.doRewrite( callback );
1441 var selectorElement = $j( iObj.selector ).get( 0 );
1442 selectorElement['firefogg'] = myFogg;
1443
1444 js_log('pre:'+ selectorElement['firefogg']['firefogg_form_action'])
1445 if(queuedFirefoggConf)
1446 $j.extend(selectorElement['firefogg'], queuedFirefoggConf);
1447 js_log('post:'+ selectorElement['firefogg']['firefogg_form_action'])
1448 }
1449 });
1450 }
1451 // Take an input player as the selector and expose basic rendering controls
1452 $.fn.firefoggRender = function( iObj, callback ) {
1453 // Check if we already have render loaded then just pass on updates/actions
1454 var sElm = $j( this.selector ).get( 0 );
1455 if( sElm['fogg_render'] ) {
1456 if( sElm['fogg_render'] == 'loading' ) {
1457 js_log( "Error: called firefoggRender while loading" );
1458 return false;
1459 }
1460 // Call or update the property:
1461 }
1462 sElm['fogg_render'] = 'loading';
1463 // Add the selector
1464 iObj['player_target'] = this.selector;
1465 mvJsLoader.doLoad([
1466 'mvFirefogg',
1467 'mvFirefoggRender'
1468 ], function() {
1469 sElm['fogg_render'] = new mvFirefoggRender( iObj );
1470 if( callback && typeof callback == 'function' )
1471 callback( sElm['fogg_render'] );
1472 });
1473 }
1474
1475 $.fn.baseUploadInterface = function(iObj) {
1476 mvJsLoader.doLoadDepMode([
1477 [
1478 'mvBaseUploadInterface',
1479 '$j.ui',
1480 ],
1481 [
1482 '$j.ui.progressbar',
1483 '$j.ui.dialog'
1484 ]
1485 ], function() {
1486 myUp = new mvBaseUploadInterface( iObj );
1487 myUp.setupForm();
1488 });
1489 }
1490
1491 // Shortcut to a themed button
1492 $.btnHtml = function( msg, className, iconId, opt ) {
1493 if( !opt )
1494 opt = {};
1495 var href = (opt.href) ? opt.href : '#';
1496 var target_attr = (opt.target) ? ' target="' + opt.target + '" ' : '';
1497 var style_attr = (opt.style) ? ' style="' + opt.style + '" ' : '';
1498 return '<a href="' + href + '" ' + target_attr + style_attr +
1499 ' class="ui-state-default ui-corner-all ui-icon_link ' +
1500 className + '"><span class="ui-icon ui-icon-' + iconId + '" />' +
1501 '<span class="btnText">'+ msg +'<span></a>';
1502 }
1503 // Shortcut to bind hover state
1504 $.fn.btnBind = function() {
1505 $j( this ).hover(
1506 function() {
1507 $j( this ).addClass( 'ui-state-hover' );
1508 },
1509 function() {
1510 $j( this ).removeClass( 'ui-state-hover' );
1511 }
1512 )
1513 return this;
1514 }
1515 /**
1516 * resize the dialog to fit the window
1517 */
1518 $.fn.dialogFitWindow = function(opt){
1519 var opt_default = {'hspace':50,'vspace':50};
1520 if(!opt)
1521 var opt={};
1522 $j.extend(opt, opt_default);
1523 $j( this.selector).dialog('option', 'width', $j(window).width() - opt.hspace );
1524 $j( this.selector).dialog('option', 'height', $j(window).height() - opt.vspace );
1525 $j( this.selector).dialog('option', 'position','center');
1526 //update the child position: (some of this should be pushed up-stream via dialog config options
1527 $j( this.selector +'~ .ui-dialog-buttonpane').css({
1528 'position':'absolute',
1529 'left':'0px',
1530 'right':'0px',
1531 'bottom':'0px',
1532 });
1533 }
1534
1535 /**
1536 * addLoaderDialog
1537 * small helper for putting a loading dialog box on top of everything
1538 * (helps block for request that
1539 *
1540 * @param msg text text of the loader msg
1541 */
1542 $.addLoaderDialog = function( msg_txt ){
1543 $.addDialog( msg_txt, msg_txt + '<br>' + mv_get_loading_img() );
1544 }
1545
1546 $.addDialog = function ( title, msg_txt, btn ){
1547 $('#mwe_tmp_loader').remove();
1548 //append the style free loader ontop:
1549 $('body').append('<div id="mwe_tmp_loader" style="display:none" title="' + title + '" >' +
1550 msg_txt +
1551 '</div>');
1552 //special btn == ok gives empty give a single "oky" -> "close"
1553 if( btn == 'ok' ){
1554 btn[ gM('mwe-ok') ] = function(){
1555 $j('#mwe_tmp_loader').close();
1556 }
1557 }
1558 //turn the loader into a real dialog loader:
1559 mvJsLoader.doLoadDepMode([
1560 [
1561 '$j.ui'
1562 ],
1563 [
1564 '$j.ui.dialog'
1565 ]
1566 ], function() {
1567 $('#mwe_tmp_loader').dialog({
1568 bgiframe: true,
1569 draggable: false,
1570 resizable: false,
1571 modal: true,
1572 width:400,
1573 buttons: btn
1574 });
1575 });
1576 }
1577 $.closeLoaderDialog = function(){
1578 mvJsLoader.doLoadDepMode([
1579 [
1580 '$j.ui'
1581 ],
1582 [
1583 '$j.ui.dialog'
1584 ]
1585 ], function() {
1586 $j('#mwe_tmp_loader').dialog('close');
1587 });
1588 }
1589
1590 $.mwProxy = function( apiConf ){
1591 mvJsLoader.doLoad( ['$mw.apiProxy'],
1592 function(){
1593 $mw.apiProxy( apiConf );
1594 });
1595 }
1596 })(jQuery);
1597 }
1598 /*
1599 * Utility functions:
1600 */
1601 // Simple URL rewriter (could probably be refactored into an inline regular exp)
1602 function getURLParamReplace( url, opt ) {
1603 var pSrc = parseUri( url );
1604 if( pSrc.protocol != '' ) {
1605 var new_url = pSrc.protocol + '://' + pSrc.authority + pSrc.path + '?';
1606 } else {
1607 var new_url = pSrc.path + '?';
1608 }
1609 var amp = '';
1610 for( var key in pSrc.queryKey ) {
1611 var val = pSrc.queryKey[ key ];
1612 // Do override if requested
1613 if( opt[ key ] )
1614 val = opt[ key ];
1615 new_url += amp + key + '=' + val;
1616 amp = '&';
1617 };
1618 // Add any vars that were not already there:
1619 for( var i in opt ) {
1620 if( !pSrc.queryKey[i] ) {
1621 new_url += amp + i + '=' + opt[i];
1622 amp = '&';
1623 }
1624 }
1625 return new_url;
1626 }
1627 /**
1628 * Given a float number of seconds, returns npt format response.
1629 *
1630 * @param float Seconds
1631 * @param boolean If we should show milliseconds or not.
1632 */
1633 function seconds2npt( sec, show_ms ) {
1634 if( isNaN( sec ) ) {
1635 // js_log("warning: trying to get npt time on NaN:" + sec);
1636 return '0:0:0';
1637 }
1638 var hours = Math.floor( sec / 3600 );
1639 var minutes = Math.floor( (sec / 60) % 60 );
1640 var seconds = sec % 60;
1641 // Round the number of seconds to the required number of significant digits
1642 if( show_ms ) {
1643 seconds = Math.round( seconds * 1000 ) / 1000;
1644 } else {
1645 seconds = Math.round( seconds );
1646 }
1647 if( seconds < 10 )
1648 seconds = '0' + seconds;
1649 if( minutes < 10 )
1650 minutes = '0' + minutes;
1651
1652 return hours + ":" + minutes + ":" + seconds;
1653 }
1654 /*
1655 * Take hh:mm:ss,ms or hh:mm:ss.ms input, return the number of seconds
1656 */
1657 function npt2seconds( npt_str ) {
1658 if( !npt_str ) {
1659 //js_log('npt2seconds:not valid ntp:'+ntp);
1660 return false;
1661 }
1662 // Strip "npt:" time definition if present
1663 npt_str = npt_str.replace( 'npt:', '' );
1664
1665 times = npt_str.split( ':' );
1666 if( times.length != 3 ){
1667 js_log( 'error: npt2seconds on ' + npt_str );
1668 return false;
1669 }
1670 // Sometimes a comma is used instead of period for ms
1671 times[2] = times[2].replace( /,\s?/, '.' );
1672 // Return seconds float
1673 return parseInt( times[0] * 3600) + parseInt( times[1] * 60 ) + parseFloat( times[2] );
1674 }
1675 /*
1676 * Simple helper to grab an edit token
1677 *
1678 * @param title The wiki page title you want to edit
1679 * @param api_url 'optional' The target API URL
1680 * @param callback The callback function to pass the token to
1681 */
1682 function get_mw_token( title, api_url, callback ) {
1683 js_log( ':get_mw_token:' );
1684 if( !title && wgUserName ) {
1685 title = 'User:' + wgUserName;
1686 }
1687 var reqObj = {
1688 'action': 'query',
1689 'prop': 'info',
1690 'intoken': 'edit',
1691 'titles': title
1692 };
1693 do_api_req( {
1694 'data': reqObj,
1695 'url' : api_url
1696 }, function(data) {
1697 for( var i in data.query.pages ) {
1698 if( data.query.pages[i]['edittoken'] ) {
1699 if( typeof callback == 'function' )
1700 callback ( data.query.pages[i]['edittoken'] );
1701 }
1702 }
1703 // No token found:
1704 return false;
1705 }
1706 );
1707 }
1708 // Do a remote or local API request based on request URL
1709 //@param options: url, data, cbParam, callback
1710 function do_api_req( options, callback ) {
1711 if( typeof options.data != 'object' ) {
1712 return js_error( 'Error: request paramaters must be an object' );
1713 }
1714 // Generate the URL if it's missing
1715 if( typeof options.url == 'undefined' || !options.url ) {
1716 if( !wgServer || ! wgScriptPath ) {
1717 return js_error('Error: no api url for api request');
1718 }
1719 options.url = mwGetLocalApiUrl();
1720 }
1721 if( typeof options.data == 'undefined' )
1722 options.data = {};
1723
1724 // Force format to JSON
1725 options.data['format'] = 'json';
1726
1727 // If action is not set, assume query
1728 if( ! options.data['action'] )
1729 options.data['action'] = 'query';
1730
1731 // js_log('do api req: ' + options.url +'?' + jQuery.param(options.data) );
1732 if( options.url == 'proxy' && $mw.proxy){
1733 //assume the proxy is already "setup" since $mw.proxy is defined.
1734 // @@todo we probably integrate that setup into the api call
1735 $mw.proxy.doRequest( options.data, callback);
1736 }else if( parseUri( document.URL ).host == parseUri( options.url ).host ) {
1737 // Local request: do API request directly
1738 $j.ajax({
1739 type: "POST",
1740 url: options.url,
1741 data: options.data,
1742 dataType: 'json', // API requests _should_ always return JSON data:
1743 async: false,
1744 success: function( data ) {
1745 callback( data );
1746 },
1747 error: function( e ) {
1748 js_error( ' error' + e + ' in getting: ' + options.url );
1749 }
1750 });
1751 } else {
1752 // Remote request
1753 // Set the callback param if it's not already set
1754 if( typeof options.jsonCB == 'undefined' )
1755 options.jsonCB = 'callback';
1756
1757 var req_url = options.url;
1758 var paramAnd = ( req_url.indexOf( '?' ) == -1 ) ? '?' : '&';
1759 // Put all the parameters into the URL
1760 for( var i in options.data ) {
1761 req_url += paramAnd + encodeURIComponent( i ) + '=' + encodeURIComponent( options.data[i] );
1762 paramAnd = '&';
1763 }
1764 var fname = 'mycpfn_' + ( $mw.cb_count++ );
1765 _global[ fname ] = callback;
1766 req_url += '&' + options.jsonCB + '=' + fname;
1767 loadExternalJs( req_url );
1768 }
1769 }
1770 function mwGetLocalApiUrl( url ) {
1771 if ( wgServer && wgScriptPath ) {
1772 return wgServer + wgScriptPath + '/api.php';
1773 }
1774 return false;
1775 }
1776 // Do a "normal" request
1777 function do_request( req_url, callback ) {
1778 js_log( 'do_request::req_url:' + req_url + ' != ' + parseUri( req_url ).host );
1779 // If we are doing a request to the same domain or relative link, do a normal GET
1780 if( parseUri( document.URL ).host == parseUri( req_url ).host ||
1781 req_url.indexOf('://') == -1 ) // Relative url
1782 {
1783 // Do a direct request
1784 $j.ajax({
1785 type: "GET",
1786 url: req_url,
1787 async: false,
1788 success: function( data ) {
1789 callback( data );
1790 }
1791 });
1792 } else {
1793 // Get data via DOM injection with callback
1794 global_req_cb.push( callback );
1795 // Prepend json_ to feed_format if not already requesting json format (metavid specific)
1796 if( req_url.indexOf( "feed_format=" ) != -1 && req_url.indexOf( "feed_format=json" ) == -1 )
1797 req_url = req_url.replace( /feed_format=/, 'feed_format=json_' );
1798
1799 loadExternalJs( req_url + '&cb=mv_jsdata_cb&cb_inx=' + (global_req_cb.length - 1) );
1800 }
1801 }
1802
1803 function mv_jsdata_cb( response ) {
1804 js_log( 'f:mv_jsdata_cb:'+ response['cb_inx'] );
1805 // Run the callback from the global request callback object
1806 if( !global_req_cb[response['cb_inx']] ) {
1807 js_log( 'missing req cb index' );
1808 return false;
1809 }
1810 if( !response['pay_load'] ) {
1811 js_log( "missing pay load" );
1812 return false;
1813 }
1814 switch( response['content-type'] ) {
1815 case 'text/plain':
1816 break;
1817 case 'text/xml':
1818 if( typeof response['pay_load'] == 'string' ) {
1819 //js_log('load string:'+"\n"+ response['pay_load']);
1820 // Debugger;
1821 if( $j.browser.msie ) {
1822 // Attempt to parse as XML for IE
1823 var xmldata = new ActiveXObject("Microsoft.XMLDOM");
1824 xmldata.async = "false";
1825 xmldata.loadXML( response['pay_load'] );
1826 } else {
1827 // For others (Firefox, Safari etc.)
1828 try {
1829 var xmldata = (new DOMParser()).parseFromString( response['pay_load'], "text/xml" );
1830 } catch( e ) {
1831 js_log( 'XML parse ERROR: ' + e.message );
1832 }
1833 }
1834 //@@todo handle XML parser errors
1835 if( xmldata )response['pay_load'] = xmldata;
1836 }
1837 break
1838 default:
1839 js_log( 'bad response type' + response['content-type'] );
1840 return false;
1841 break;
1842 }
1843 global_req_cb[response['cb_inx']]( response['pay_load'] );
1844 }
1845 // Load external JS via DOM injection
1846 function loadExternalJs( url, callback ) {
1847 js_log( 'load js: '+ url );
1848 //if(window['$j']) // use jquery call:
1849 /*$j.ajax({
1850 type: "GET",
1851 url: url,
1852 dataType: 'script',
1853 cache: true
1854 });*/
1855 //else{
1856 var e = document.createElement( "script" );
1857 e.setAttribute( 'src', url );
1858 e.setAttribute( 'type', "text/javascript" );
1859 /*if(callback)
1860 e.onload = callback;
1861 */
1862 //e.setAttribute('defer', true);
1863 document.getElementsByTagName( "head" )[0].appendChild( e );
1864 // }
1865 }
1866 function styleSheetPresent( url ) {
1867 style_elements = document.getElementsByTagName( 'link' );
1868 if( style_elements.length > 0 ) {
1869 for( i = 0; i < style_elements.length; i++ ) {
1870 if( style_elements[i].href == url )
1871 return true;
1872 }
1873 }
1874 return false;
1875 }
1876 function loadExternalCss( url ) {
1877 // We could have the script loader group these CSS requests.
1878 // But it's debatable: it may hurt more than it helps with caching and all
1879 if( typeof url =='object' ) {
1880 for( var i in url ) {
1881 loadExternalCss( url[i] );
1882 }
1883 return ;
1884 }
1885
1886 if( url.indexOf('?') == -1 ) {
1887 url += '?' + getMwReqParam();
1888 }
1889 if( !styleSheetPresent( url ) ) {
1890 js_log( 'load css: ' + url );
1891 var e = document.createElement( "link" );
1892 e.href = url;
1893 e.type = "text/css";
1894 e.rel = 'stylesheet';
1895 document.getElementsByTagName( "head" )[0].appendChild( e );
1896 }
1897 }
1898 function getMvEmbedURL() {
1899 if( _global['mv_embed_url'] )
1900 return _global['mv_embed_url'];
1901 var js_elements = document.getElementsByTagName( "script" );
1902 for( var i = 0; i < js_elements.length; i++ ) {
1903 // Check for mv_embed.js and/or script loader
1904 var src = js_elements[i].getAttribute( "src" );
1905 if( src ) {
1906 if( src.indexOf( 'mv_embed.js' ) != -1 || (
1907 ( src.indexOf( 'mwScriptLoader.php' ) != -1 || src.indexOf('jsScriptLoader.php') != -1 )
1908 && src.indexOf('mv_embed') != -1) ) //(check for class=mv_embed script_loader call)
1909 {
1910 _global['mv_embed_url'] = src;
1911 return src;
1912 }
1913 }
1914 }
1915 js_error( 'Error: getMvEmbedURL failed to get Embed Path' );
1916 return false;
1917 }
1918 // Get a unique request ID to ensure fresh JavaScript
1919 function getMwReqParam() {
1920 if( _global['req_param'] )
1921 return _global['req_param'];
1922 var mv_embed_url = getMvEmbedURL();
1923
1924 var req_param = '';
1925
1926 // If we have a URI, add it to the req
1927 var urid = parseUri( mv_embed_url ).queryKey['urid']
1928 // If we're in debug mode, get a fresh unique request key and pass on "debug" param
1929 if( parseUri( mv_embed_url ).queryKey['debug'] == 'true' ){
1930 var d = new Date();
1931 req_param += 'urid=' + d.getTime() + '&debug=true';
1932 }else if( urid ) {
1933 // Set from request urid:
1934 req_param += 'urid=' + urid;
1935 }else{
1936 // Otherwise, just use the mv_embed version
1937 req_param += 'urid=' + $mw.version;
1938 }
1939 //add the lang param:
1940 var langKey = parseUri( mv_embed_url ).queryKey['uselang'];
1941 if( langKey )
1942 req_param += '&uselang=' + langKey;
1943
1944 _global['req_param'] = req_param;
1945
1946 return _global['req_param'];
1947 }
1948 /*
1949 * Set the global mv_embed path based on the script's location
1950 */
1951 function getMvEmbedPath() {
1952 if( _global['mv_embed_path'] )
1953 return _global['mv_embed_path'];
1954 var mv_embed_url = getMvEmbedURL();
1955 if( mv_embed_url.indexOf( 'mv_embed.js' ) !== -1 ) {
1956 mv_embed_path = mv_embed_url.substr( 0, mv_embed_url.indexOf( 'mv_embed.js' ) );
1957 } else if( mv_embed_url.indexOf( 'mwScriptLoader.php' ) !== -1 ) {
1958 // Script loader is in the root of MediaWiki, so include the default mv_embed extension path
1959 mv_embed_path = mv_embed_url.substr( 0, mv_embed_url.indexOf( 'mwScriptLoader.php' ) )
1960 + mediaWiki_mvEmbed_path;
1961 } else {
1962 mv_embed_path = mv_embed_url.substr( 0, mv_embed_url.indexOf( 'jsScriptLoader.php' ) );
1963 }
1964 // Make an absolute URL (if it's relative and we don't have an mv_embed path)
1965 if( mv_embed_path.indexOf( '://' ) == -1 ) {
1966 var pURL = parseUri( document.URL );
1967 if( mv_embed_path.charAt( 0 ) == '/' ) {
1968 mv_embed_path = pURL.protocol + '://' + pURL.authority + mv_embed_path;
1969 } else {
1970 // Relative
1971 if( mv_embed_path == '' ) {
1972 mv_embed_path = pURL.protocol + '://' + pURL.authority + pURL.directory + mv_embed_path;
1973 }
1974 }
1975 }
1976 _global['mv_embed_path'] = mv_embed_path;
1977 return mv_embed_path;
1978 }
1979
1980 if ( typeof DOMParser == "undefined" ) {
1981 DOMParser = function () {}
1982 DOMParser.prototype.parseFromString = function ( str, contentType ) {
1983 if ( typeof ActiveXObject != "undefined" ) {
1984 var d = new ActiveXObject( "MSXML.DomDocument" );
1985 d.loadXML( str );
1986 return d;
1987 } else if ( typeof XMLHttpRequest != "undefined" ) {
1988 var req = new XMLHttpRequest;
1989 req.open( "GET", "data:" + (contentType || "application/xml") +
1990 ";charset=utf-8," + encodeURIComponent(str), false );
1991 if ( req.overrideMimeType ) {
1992 req.overrideMimeType(contentType);
1993 }
1994 req.send( null );
1995 return req.responseXML;
1996 }
1997 }
1998 }
1999 /*
2000 * Utility functions
2001 */
2002 function js_log( string ) {
2003 ///add any prepend debug strings if nessesary (used for cross browser)
2004 if( $mw.conf['debug_pre'] )
2005 string = $mw.conf['debug_pre']+ string;
2006
2007 if( window.console ) {
2008 window.console.log( string );
2009 } else {
2010 /*
2011 * IE and non-Firebug debug:
2012 */
2013 /*var log_elm = document.getElementById('mv_js_log');
2014 if(!log_elm){
2015 document.getElementsByTagName("body")[0].innerHTML = document.getElementsByTagName("body")[0].innerHTML +
2016 '<div style="position:absolute;z-index:500;top:0px;left:0px;right:0px;height:10px;">'+
2017 '<textarea id="mv_js_log" cols="120" rows="5"></textarea>'+
2018 '</div>';
2019
2020 var log_elm = document.getElementById('mv_js_log');
2021 }
2022 if(log_elm){
2023 log_elm.value+=string+"\n";
2024 }*/
2025 }
2026 return false;
2027 }
2028
2029 function js_error( string ) {
2030 alert( string );
2031 return false;
2032 }