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