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