3 * For details see: http://metavid.org/wiki/index.php/Mv_embed
5 * All Metavid Wiki code is released under the GPL2.
6 * For more information visit http://metavid.org/wiki/Code
8 * @url http://metavid.org
11 * http://stevenlevithan.com/demo/parseuri/js/
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)
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
25 if( typeof mvAutoLoadClasses
== 'undefined' )
26 mvAutoLoadClasses
= {};
28 // The script that loads the class set
29 function lcPaths( classSet
){
30 for( var i
in classSet
) {
31 mvAutoLoadClasses
[i
] = classSet
[i
];
35 function mvGetClassPath(k
){
36 if( mvAutoLoadClasses
[k
] ) {
37 //js_log('got class path:' + k + ' : '+ mvClassPaths[k]);
38 return mvAutoLoadClasses
[k
];
40 js_log('Error:: Could not find path for requested class ' + k
);
44 if( typeof mvCssPaths
== 'undefined' )
47 function lcCssPath( cssSet
) {
48 for( var i
in cssSet
) {
49 mvCssPaths
[i
] = mv_embed_path
+ cssSet
[i
];
54 * -- Load Class Paths --
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)
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)
64 * we could support direct file requests to the script loader instead
65 * of shared class names read from a central location.
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",
79 "$mw.proxy" : "libMwApi/mw.proxy.js",
81 "$mw.testLang" : "tests/testLang.js",
83 "ctrlBuilder" : "skins/ctrlBuilder.js",
84 "kskinConfig" : "skins/kskin/kskin.js",
85 "mvpcfConfig" : "skins/mvpcf/mvpcf.js",
87 "JSON" : "libMwApi/json2.js",
88 "$j.cookie" : "jquery/plugins/jquery.cookie.js",
89 "$j.contextMenu" : "jquery/plugins/jquery.contextMenu.js",
90 "$j.fn.suggestions" : "jquery/plugins/jquery.suggestions.js",
91 "$j.fn.textSelection" : "jquery/plugins/jquery.textSelection.js",
93 "$j.effects.blind" : "jquery/jquery.ui/ui/effects.blind.js",
94 "$j.effects.drop" : "jquery/jquery.ui/ui/effects.drop.js",
95 "$j.effects.pulsate" : "jquery/jquery.ui/ui/effects.pulsate.js",
96 "$j.effects.transfer" : "jquery/jquery.ui/ui/effects.transfer.js",
97 "$j.ui.droppable" : "jquery/jquery.ui/ui/ui.droppable.js",
98 "$j.ui.slider" : "jquery/jquery.ui/ui/ui.slider.js",
99 "$j.effects.bounce" : "jquery/jquery.ui/ui/effects.bounce.js",
100 "$j.effects.explode" : "jquery/jquery.ui/ui/effects.explode.js",
101 "$j.effects.scale" : "jquery/jquery.ui/ui/effects.scale.js",
102 "$j.ui.datepicker" : "jquery/jquery.ui/ui/ui.datepicker.js",
103 "$j.ui.progressbar" : "jquery/jquery.ui/ui/ui.progressbar.js",
104 "$j.ui.sortable" : "jquery/jquery.ui/ui/ui.sortable.js",
105 "$j.effects.clip" : "jquery/jquery.ui/ui/effects.clip.js",
106 "$j.effects.fold" : "jquery/jquery.ui/ui/effects.fold.js",
107 "$j.effects.shake" : "jquery/jquery.ui/ui/effects.shake.js",
108 "$j.ui.dialog" : "jquery/jquery.ui/ui/ui.dialog.js",
109 "$j.ui.resizable" : "jquery/jquery.ui/ui/ui.resizable.js",
110 "$j.ui.tabs" : "jquery/jquery.ui/ui/ui.tabs.js",
111 "$j.effects.core" : "jquery/jquery.ui/ui/effects.core.js",
112 "$j.effects.highlight" : "jquery/jquery.ui/ui/effects.highlight.js",
113 "$j.effects.slide" : "jquery/jquery.ui/ui/effects.slide.js",
114 "$j.ui.accordion" : "jquery/jquery.ui/ui/ui.accordion.js",
115 "$j.ui.draggable" : "jquery/jquery.ui/ui/ui.draggable.js",
116 "$j.ui.selectable" : "jquery/jquery.ui/ui/ui.selectable.js",
118 "$j.fn.dragDropFile" : "libAddMedia/dragDropFile.js",
119 "mvFirefogg" : "libAddMedia/mvFirefogg.js",
120 "mvAdvFirefogg" : "libAddMedia/mvAdvFirefogg.js",
121 "mvBaseUploadInterface" : "libAddMedia/mvBaseUploadInterface.js",
122 "remoteSearchDriver" : "libAddMedia/remoteSearchDriver.js",
123 "seqRemoteSearchDriver" : "libSequencer/seqRemoteSearchDriver.js",
125 "baseRemoteSearch" : "libAddMedia/searchLibs/baseRemoteSearch.js",
126 "mediaWikiSearch" : "libAddMedia/searchLibs/mediaWikiSearch.js",
127 "metavidSearch" : "libAddMedia/searchLibs/metavidSearch.js",
128 "archiveOrgSearch" : "libAddMedia/searchLibs/archiveOrgSearch.js",
129 "flickrSearch" : "libAddMedia/searchLibs/flickrSearch.js",
130 "baseRemoteSearch" : "libAddMedia/searchLibs/baseRemoteSearch.js",
132 "mvClipEdit" : "libClipEdit/mvClipEdit.js",
134 "embedVideo" : "libEmbedVideo/embedVideo.js",
135 "flashEmbed" : "libEmbedVideo/flashEmbed.js",
136 "genericEmbed" : "libEmbedVideo/genericEmbed.js",
137 "htmlEmbed" : "libEmbedVideo/htmlEmbed.js",
138 "javaEmbed" : "libEmbedVideo/javaEmbed.js",
139 "nativeEmbed" : "libEmbedVideo/nativeEmbed.js",
140 "quicktimeEmbed" : "libEmbedVideo/quicktimeEmbed.js",
141 "vlcEmbed" : "libEmbedVideo/vlcEmbed.js",
143 "mvPlayList" : "libSequencer/mvPlayList.js",
144 "mvSequencer" : "libSequencer/mvSequencer.js",
145 "mvFirefoggRender" : "libSequencer/mvFirefoggRender.js",
146 "mvTimedEffectsEdit": "libSequencer/mvTimedEffectsEdit.js",
148 "mvTextInterface" : "libTimedText/mvTextInterface.js",
149 "mvTimedTextEdit" : "libTimedText/mvTimedTextEdit.js"
152 // Dependency mapping for CSS files for self-contained included plugins:
154 '$j.Jcrop' : 'libClipEdit/Jcrop/css/jquery.Jcrop.css',
155 '$j.fn.ColorPicker' : 'libClipEdit/colorpicker/css/colorpicker.css'
160 // (c) Steven Levithan <stevenlevithan.com>
162 function parseUri (str
) {
163 var o
= parseUri
.options
,
164 m
= o
.parser
[o
.strictMode
? "strict" : "loose"].exec(str
),
168 while (i
--) uri
[o
.key
[i
]] = m
[i
] || "";
171 uri
[o
.key
[12]].replace(o
.q
.parser
, function ($0, $1, $2) {
172 if ($1) uri
[o
.q
.name
][$1] = $2;
179 key
: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
182 parser
: /(?:^|&)([^&=]*)=?([^&]*)/g
185 strict
: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
186 loose
: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
190 // For use when mv_embed with script-loader is in the root MediaWiki path
191 var mediaWiki_mvEmbed_path
= 'js2/mwEmbed/';
193 //The global scope: will be depreciated once we get everything into $mw
197 * setup the empty global $mw object
198 * will ensure all our functions and variables are properly namespaced
199 * reducing chance of conflicts
205 //@@todo move these into $mw
206 var global_req_cb
= new Array(); // The global request callback array
208 // Get the mv_embed location if it has not been set
209 if( !mv_embed_path
) {
210 var mv_embed_path
= getMvEmbedPath();
213 * The global $mw object:
215 * Any global functions/classes that are not jQuery plugins should make
216 * there way into the $mw namespace
223 'skin_name' : 'mvpcf',
224 'jui_skin' : 'redmond',
225 'video_size' : '400x300'
227 //list valid skins here:
228 $.valid_skins
= ['mvpcf', 'kskin'];
229 // the version of mwEmbed
230 $.version
= '1.0r21';
233 * some global containers flags
235 $.skin_list
= new Array();
238 $.player_list
= new Array(), // The global player list per page
239 $.req_cb
= new Array() // The global request callback array
242 * Language classes $mw.lang
244 * Localized Language support attempts to mirror the functionality of Language.php in MediaWiki
245 * It contains methods for loading and transforming msg text
250 * Setup the lang object
257 * Loads a set of json messages into the lng object.
259 * @param json msgSet The set of msgs to be loaded
261 $.lang
.loadGM = function( msgSet
){
262 for( var i
in msgSet
) {
263 gMsg
[ i
] = msgSet
[i
];
269 * Loads a ruleset by given template key ie PLURAL : { //ruleSetObj }
271 * @param json ruleSet The ruleset object ( extends gRuleSet )
273 $.lang
.loadRS = function( ruleSet
){
274 for( var i
in ruleSet
){
275 gRuleSet
[ i
] = ruleSet
[ i
];
280 * Returns a transformed msg string
282 * it take a msg key and array of replacement values of form
283 * $1, $2 and does relevant msgkey transformation returning
286 * @param string key The msg key as set by loadGm
287 * @param [mixed] args An array of replacement strings
290 $.lang
.gM = function( key
, args
) {
292 return '<' + key
+ '>';// Missing key placeholder
294 //swap in the arg values
295 var ms
= $.lang
.gMsgSwap( key
, args
) ;
299 //a quick check to see if we need to send the msg via the 'parser'
300 //(we can add more detailed check once we support more wiki syntax)
301 if( ms
.indexOf('{{') === -1 && ms
.indexOf('[') === -1){
305 //make sure we have the lagMagic setup:
306 //@@todo move to init
308 //send the msg key through the parser
309 var pObj
= $.parser
.pNew( ms
);
310 //return the transformed msg
311 return pObj
.getHTML();
316 * @param string key The msg key as set by loadGm
317 * @param [mixed] args An array or string to be replaced
320 $.lang
.gMsgSwap = function( key
, args
){
322 return '<' + key
+ '>';// Missing key placeholder
323 //get the messege string:
324 var ms
= gMsg
[ key
];
327 if( typeof args
== 'object' || typeof args
== 'array' ) {
328 for( var v
in args
) {
329 // Message test replace arguments start at 1 instead of zero:
330 var rep
= new RegExp('\\$'+ ( parseInt(v
) + 1 ), 'g');
331 ms
= ms
.replace( rep
, args
[v
] );
333 } else if( typeof args
=='string' || typeof args
=='number' ) {
334 ms
= ms
.replace(/\$1/g, args
);
342 * @returns string The msg key without transforming it
344 $.lang
.gMsgNoTrans = function( key
){
348 // Missing key placeholder
349 return '<' + key
+ '>';
352 * Add Supported Magic Words to parser
354 //set the setupflag to false:
355 $.lang
.doneSetup
=false;
356 $.lang
.magicSetup = function(){
357 if(!$.lang
.doneSetup
){
358 $.parser
.addMagic ( {
359 'PLURAL' : $.lang
.procPLURAL
362 $.lang
.doneSetup
= true;
367 * Process the PLURAL special language template key:
369 $.lang
.procPLURAL = function( tObj
){
371 // (gRuleSet is loaded from script-loader to contains local ruleset)
372 var rs
= gRuleSet
['PLURAL'];
375 * Plural matchRuleTest
377 function matchRuleTest(cRule
, val
){
378 //js_log("matchRuleTest:: " + typeof cRule + ' ' + cRule + ' == ' + val );
380 function checkValue(compare
, val
){
381 if(typeof compare
== 'string'){
382 range
= compare
.split('-');
383 if( range
.length
>= 1 ){
384 if( val
>= range
[0] && val
<= range
[1] )
388 //else do a direct compare
394 //check for simple cRule type:
395 if( typeof cRule
== 'number'){
396 return ( parseInt( val
) == parseInt( cRule
) );
397 }else if( typeof cRule
== 'object' ){
399 //if a list we need to match all for rule match
411 if( checkValue( val
% cr
['mod'], cr
['is'] ) )
413 }else if( cr
['not']){
414 if( ! checkValue( val
% cr
['mod'], cr
['not'] ) )
420 //check all the matches (taking into consideration "or" order)
430 * Maps a given rule Index to template params:
432 * if index is out of range return last param
435 function getTempParamFromRuleInx(tObj
, ruleInx
){
436 //js_log('getTempParamFromRuleInx: ruleInx: ' + ruleInx + ' tempParamLength ' + tObj.param.length );
437 if( ruleInx
>= tObj
.param
.length
)
438 return tObj
.param
[ tObj
.param
.length
-1 ];
439 //else return the requested index:
440 return tObj
.param
[ ruleInx
];
443 //run the actual rule lookup:
444 for(var ruleInx
in rs
){
446 if( matchRuleTest( cRule
, tObj
.arg
) ){
447 //js_log("matched rule: " + ruleInx );
448 return getTempParamFromRuleInx(tObj
, rCount
);
452 //js_log('no match found for: ' + tObj.arg + ' using last/other : ' + tObj.param [ tObj.param.length -1 ] );
453 //return the last /"other" template param
454 return tObj
.param
[ tObj
.param
.length
-1 ];
458 * gMsgLoadRemote loads remote msg strings
460 * @param mixed msgSet the set of msg to load remotely
461 * @param function callback the callback to issue once string is ready
463 $.lang
.gMsgLoadRemote = function( msgSet
, callback
) {
465 if( typeof msgSet
== 'object' ) {
466 for( var i
in msgSet
) {
467 ammessages
+= msgSet
[i
] + '|';
469 } else if( typeof msgSet
== 'string' ) {
470 ammessages
+= msgSet
;
472 if( ammessages
== '' ) {
473 js_log( 'gMsgLoadRemote: no message set requested' );
478 'meta': 'allmessages',
479 'ammessages': ammessages
481 }, function( data
) {
482 if( data
.query
.allmessages
) {
483 var msgs
= data
.query
.allmessages
;
484 for( var i
in msgs
) {
486 ld
[ msgs
[i
]['name'] ] = msgs
[i
]['*'];
494 * Format a size in bytes for output, using an appropriate
495 * unit (B, KB, MB or GB) according to the magnitude in question
497 * @param size Size to format
498 * @return string Plain text (not HTML)
500 $.lang
.formatSize = function ( size
) {
501 // For small sizes no decimal places are necessary
508 // For MB and bigger two decimal places are smarter
512 msg
= 'mwe-size-gigabytes';
514 msg
= 'mwe-size-megabytes';
517 msg
= 'mwe-size-kilobytes';
520 msg
= 'mwe-size-bytes';
522 // JavaScript does not let you choose the precision when rounding
523 var p
= Math
.pow(10,round
);
524 var size
= Math
.round( size
* p
) / p
;
525 //@@todo we need a formatNum and we need to request some special packaged info to deal with that case.
526 return gM( msg
, size
);
529 $.lang
.formatNumber = function( num
){
532 Str: The number to be formatted, as a string or number.
533 outD: The decimal character for the output, such as ',' for the number 100,2
534 sep: The separator character for the output, such as ',' for the number 1,000.2
536 function addSeparatorsNF(nStr
, outD
, sep
){
538 var dpos
= nStr
.indexOf( '.' );
541 nStrEnd
= outD
+ nStr
.substring(dpos
+ 1, nStr
.length
);
542 nStr
= nStr
.substring(0, dpos
);
544 var rgx
= /(\d+)(\d{3})/;
545 while (rgx
.test(nStr
)) {
546 nStr
= nStr
.replace(rgx
, '$1' + sep
+ '$2');
548 return nStr
+ nStrEnd
;
550 //@@todo read language code and give periods or comas:
551 return addSeparatorsNF( num
, '.', ',');
557 * MediaWiki wikitext "Parser"
559 * This is not feature complete but we need a way to get at template properties
562 * @param wikiText the wikitext to be parsed
563 * @return parserObj returns a parser object that has methods for getting at
564 * things you would want
571 * lets you add a set of magic keys and associated callback functions
572 * callback: @param ( Object Template )
573 * callback: @return the transformed template output
575 * @param object magicSet key:callback
577 $.parser
.addMagic = function( magicSet
){
578 for(var i
in magicSet
)
579 pMagicSet
[ i
] = magicSet
[i
];
582 //actual parse call (returns parser object)
583 $.parser
.pNew = function( wikiText
, opt
){
584 var parseObj = function( wikiText
, opt
){
585 return this.init( wikiText
, opt
)
587 parseObj
.prototype = {
588 //the wikiText "DOM"... stores the parsed wikiText structure
589 //wtDOM : {}, (not yet supported )
591 pOut
: '', //the parser output string container
592 init :function( wikiText
){
593 this.wikiText
= wikiText
;
595 updateText : function( wikiText
){
596 this.wikiText
= wikiText
;
597 //invalidate the output (will force a re-parse )
602 * quickly recursive / parse out templates:
605 // ~ probably a better algorithm out there / should mirror php parser flow ~
606 // (we are already running white-space issues ie php parse strips whitespace diffrently)
608 // ... but I am having fun with recursion so here it is...
609 // or at least mirror: http://www.mediawiki.org/wiki/Extension:Page_Object_Model
610 function rdpp ( txt
, cn
){
613 for(var a
=0; a
< txt
.length
; a
++){
614 if( txt
[a
] == '{' && txt
[a
+1] == '{' ){
618 node
['c'] = new Array();
620 node
['c'].push( rdpp( txt
.substr( a
), true ) );
621 }else if( txt
[a
] == '}' && txt
[a
+1] == '}'){
630 //dont put closures into output:
631 if( txt
[a
] && txt
[a
]!='}' )
638 * parse template text as template name and named params
640 function parseTmplTxt( ts
){
643 tname
= ts
.split('\|').shift() ;
644 tname
= tname
.split('\{').shift() ;
645 tname
= tname
.replace( /^\s+|\s+$/g, "" ); //trim
647 //check for arguments:
648 if( tname
.split(':').length
== 1 ){
649 tObj
["name"] = tname
;
651 tObj
["name"] = tname
.split(':').shift();
652 tObj
["arg"] = tname
.split(':').pop();
655 //js_log("TNAME::" + tObj["name"] + ' from:: ' + ts);
656 var pSet
= ts
.split('\|');
659 tObj
.param
= new Array();
660 for(var pInx
in pSet
){
661 var tStr
= pSet
[ pInx
];
662 //check for empty param
664 tObj
.param
[ pInx
] = '';
667 for(var b
=0 ; b
< tStr
.length
; b
++){
668 if(tStr
[b
] == '=' && b
>0 && b
<tStr
.length
&& tStr
[b
-1]!='\\'){
670 tObj
.param
[ tStr
.split('=').shift() ] = tStr
.split('=').pop();
673 tObj
.param
[ pInx
] = tStr
;
680 function getMagicTxtFromTempNode( node
){
681 node
.tObj
= parseTmplTxt ( node
.t
);
682 //do magic swap if template key found in pMagicSet
683 if( node
.tObj
.name
in pMagicSet
){
684 var nt
= pMagicSet
[ node
.tObj
.name
]( node
.tObj
);
687 //don't swap just return text
694 * go last child first swap upward: (could probably be integrated above somehow)
697 function recurse_magic_swap( node
){
703 for(var i
in node
['c']){
704 var nt
= recurse_magic_swap( node
['c'][i
] );
705 //swap it into current
707 node
.t
= node
.t
.replace( node
['c'][i
].t
, nt
);
710 pNode
.t
= pNode
.t
.replace( node
['c'][i
].t
, nt
);
712 //do the current node:
713 var nt
= getMagicTxtFromTempNode( node
);
714 pNode
.t
= pNode
.t
.replace(node
.t
, nt
);
715 //run the swap for the outer most node
718 //node.t = getMagicFromTempObj( node.t )
719 return getMagicTxtFromTempNode( node
);
722 //parse out the template node structure:
723 this.pNode
= rdpp ( this.wikiText
);
724 //strip out the parent from the root
725 this.pNode
['p'] = null;
727 //do the recursive magic swap text:
728 this.pOut
= recurse_magic_swap( this.pNode
);
733 * parsed template api ~losely based off of ~POM~
734 * http://www.mediawiki.org/wiki/Extension:Page_Object_Model
740 * gets a requested template from the wikitext (if available)
743 templates: function( tname
){
745 var tmplSet
= new Array();
746 function getMatchingTmpl( node
){
748 for(var i
in node
['c']){
749 getMatchingTmpl( node
['c'] );
752 if( tname
&& node
.tObj
){
753 if( node
.tObj
['name'] == tname
)
754 tmplSet
.push( node
.tObj
);
755 }else if( node
.tObj
){
756 tmplSet
.push( node
.tObj
);
759 getMatchingTmpl( this.pNode
);
763 * Returns the transformed wikitext
765 * Build output from swappable index
766 * (all transforms must be expanded in parse stage and linearly rebuilt)
767 * Alternatively we could build output using a place-holder & replace system
768 * (this lets us be slightly more slopy with ordering and indexes, but probably slower)
770 * Ideal: we build a 'wiki DOM'
771 * When editing you update the data structure directly
772 * Then in output time you just go DOM->html-ish output without re-parsing anything
774 getHTML : function(){
775 //wikiText updates should invalidate pOut
776 if( this.pOut
== ''){
782 //return the parserObj
783 return new parseObj( wikiText
, opt
) ;
788 //setup legacy global shortcuts:
789 var loadGM
= $mw
.lang
.loadGM
;
790 var loadRS
= $mw
.lang
.loadRS
;
791 var gM
= $mw
.lang
.gM
;
793 // All default messages in [English] should be overwritten by the CMS language message system.
795 "mwe-loading_txt" : "Loading ...",
796 "mwe-size-gigabytes" : "$1 GB",
797 "mwe-size-megabytes" : "$1 MB",
798 "mwe-size-kilobytes" : "$1 K",
799 "mwe-size-bytes" : "$1 B",
800 "mwe-error_load_lib" : "Error: JavaScript $1 was not retrievable or does not define $2",
801 "mwe-loading-add-media-wiz" : "Loading add media wizard",
802 "mwe-apiproxy-setup" : "Setting up API proxy",
803 "mwe-load-drag-item" : "Loading dragged item",
805 "mwe-cancel" : "Cancel"
809 // Get the loading image
810 function mv_get_loading_img( style
, class_attr
){
811 var style_txt
= (style
)?style
:'';
812 var class_attr
= (class_attr
) ? 'class="' + class_attr
+ '"' : 'class="mv_loading_img"';
813 return '<div '+class_attr
+' style="' + style
+'"></div>';
816 function mv_set_loading(target
, load_id
){
817 var id_attr
= ( load_id
)?' id="' + load_id
+ '" ':'';
818 $j(target
).append('<div '+id_attr
+' style="position:absolute;top:0px;left:0px;height:100%;width:100%;'+
819 'background-color:#FFF;">' +
820 mv_get_loading_img('top:30px;left:30px') +
825 * mvJsLoader class handles initialization and js file loads
832 onReadyEvents
: new Array(),
833 doneReadyEvents
: false,
834 jQuerySetupFlag
: false,
836 // To keep consistency across threads
840 load_error
: false, // Load error flag (false by default)
842 callbacks
: new Array(),
845 doLoad: function( loadLibs
, callback
) {
847 if( loadLibs
&& loadLibs
.length
!= 0 ) {
848 //js_log("doLoad setup::" + JSON.stringify( loadLibs ) );
850 // First check if we already have this library loaded
851 var all_libs_loaded
= true;
852 for( var i
= 0; i
< loadLibs
.length
; i
++ ) {
853 // Check if the library is already loaded
854 if( ! this.checkObjPath( loadLibs
[i
] ) ) {
855 all_libs_loaded
= false;
858 if( all_libs_loaded
) {
859 js_log( 'Libraries ( ' + loadLibs
+ ') already loaded... skipping load request' );
863 // Do a check for any CSS we may need and get it
864 for( var i
= 0; i
< loadLibs
.length
; i
++ ) {
865 if( typeof mvCssPaths
[ loadLibs
[i
] ] != 'undefined' ) {
866 loadExternalCss( mvCssPaths
[ loadLibs
[i
] ] );
870 // Check if we should use the script loader to combine all the requests into one
871 if( typeof mwSlScript
!= 'undefined' ) {
875 for( var i
= 0; i
< loadLibs
.length
; i
++ ) {
876 var curLib
= loadLibs
[i
];
877 // Only add if not included yet:
878 if( ! this.checkObjPath( curLib
) ) {
879 class_set
+= coma
+ curLib
;
884 //Build the url to the scriptServer striping its request parameters:
885 var puri
= parseUri( getMvEmbedURL() );
886 if( ( getMvEmbedURL().indexOf('://') != -1 )
887 && puri
.host
!= parseUri( document
.URL
).host
)
889 var scriptPath
= puri
.protocol
+ '://' + puri
.authority
+ puri
.path
;
891 var scriptPath
= puri
.path
;
893 //js_log('scriptServer Path is: ' + scriptPath + "\n host script path:" + getMvEmbedURL() );
894 this.libs
[ last_class
] = scriptPath
+ '?class=' + class_set
+
895 '&' + getMwReqParam();
899 for( var i
= 0; i
< loadLibs
.length
; i
++ ) {
900 var curLib
= loadLibs
[i
];
902 var libLoc
= mvGetClassPath( curLib
);
903 // Do a direct load of the file (pass along unique request id from
904 // request or mv_embed Version )
905 var qmark
= (libLoc
.indexOf( '?' ) !== true) ? '?' : '&';
906 this.libs
[curLib
] = mv_embed_path
+ libLoc
+ qmark
+ getMwReqParam();
913 this.callbacks
.push( callback
);
915 if( this.checkLoading() ) {
916 //@@todo we should check the <script> Element .onLoad property to
917 //make sure its just not a very slow connection
918 //(even though the class is not loaded)
919 if( this.load_time
++ > 4000 ){ // Time out after ~80 seconds
920 js_log( gM('mwe-error_load_lib', [mvGetClassPath(this.missing_path
), this.missing_path
]) );
921 this.load_error
= true;
923 setTimeout( 'mvJsLoader.doLoad()', 20 );
926 //js_log('checkLoading passed. Running callbacks...');
927 // Only do callbacks if we are in the same instance (weird concurrency issue)
929 for( var i
= 0; i
< this.callbacks
.length
; i
++ )
931 //js_log('RESET LIBS: loading is: '+ loading + ' callback count: '+cb_count +
932 // ' p:'+ this.ptime +' c:'+ this.ctime);
936 //js_log('done loading, do call: ' + this.callbacks[0] );
937 while( this.callbacks
.length
!= 0 ) {
938 if( this.ptime
== this.ctime
- 1 ) { // Enforce thread consistency
939 this.callbacks
.pop()();
940 //func = this.callbacks.pop();
941 //js_log(' run: '+this.ctime+ ' p: ' + this.ptime + ' ' +loading+ ' :'+ func);
944 // Re-issue doLoad ( ptime will be set to ctime so we should catch up)
945 setTimeout( 'mvJsLoader.doLoad()', 25 );
950 this.ptime
= this.ctime
;
952 doLoadDepMode: function( loadChain
, callback
) {
953 // Firefox executes JS in the order in which it is included, so just directly issue the request
954 if( $j
.browser
.firefox
) {
956 for( var i
= 0; i
< loadChain
.length
; i
++ ) {
957 for( var j
= 0; j
< loadChain
[i
].length
; j
++ ) {
958 loadSet
.push( loadChain
[i
][j
] );
961 mvJsLoader
.doLoad( loadSet
, callback
);
963 // Safari and IE tend to execute out of order so load with dependency checks
964 mvJsLoader
.doLoad( loadChain
.shift(), function() {
965 if( loadChain
.length
!= 0 ) {
966 mvJsLoader
.doLoadDepMode( loadChain
, callback
);
973 checkLoading: function() {
976 for( var i
in this.libs
) { // for/in loop is OK on an object
977 if( !this.checkObjPath( i
) ) {
978 if( !this.libreq
[i
] ) {
979 loadExternalJs( this.libs
[i
] );
982 //js_log("has not yet loaded: " + i);
988 checkObjPath: function( libVar
) {
991 var objPath
= libVar
.split( '.' )
993 for( var p
= 0; p
< objPath
.length
; p
++ ) {
994 cur_path
= (cur_path
== '') ? cur_path
+ objPath
[p
] : cur_path
+ '.' + objPath
[p
];
995 eval( 'var ptest = typeof ( '+ cur_path
+ ' ); ');
996 if( ptest
== 'undefined' ) {
997 this.missing_path
= cur_path
;
1001 this.cur_path
= cur_path
;
1005 * checks for jQuery and adds the $j noConflict var
1007 jQueryCheck: function( callback
) {
1008 //js_log( 'jQueryCheck::' );
1010 // Skip stuff if $j is already loaded:
1011 if( _global
['$j'] && callback
){
1013 if( _this
.jQuerySetupFlag
)
1020 //only do the $j setup once:
1022 _global
['$j'] = jQuery
.noConflict();
1024 if( _this
.jQuerySetupFlag
== false){
1025 //js_log('setup mv_embed jQuery bindings');
1026 //setup our global settings using the (jQuery helper)
1028 // Set up the skin path
1029 _global
['mv_jquery_skin_path'] = mv_embed_path
+ 'jquery/jquery.ui/themes/' + $mw
.conf
['jui_skin'] + '/';
1030 _global
['mv_skin_img_path'] = mv_embed_path
+ 'skins/' + $mw
.conf
['skin_name'] + '/images/';
1031 _global
['mv_default_thumb_url'] = mv_skin_img_path
+ 'vid_default_thumb.jpg';
1033 // Make sure the skin/style sheets are always available:
1034 loadExternalCss( mv_jquery_skin_path
+ 'jquery-ui-1.7.1.custom.css' );
1035 loadExternalCss( mv_embed_path
+ 'skins/' + $mw
.conf
['skin_name'] + '/styles.css' );
1037 // Set up AJAX to not send dynamic URLs for loading scripts (we control that with
1038 // the scriptLoader)
1043 js_log( 'jQuery loaded into $j' );
1044 // Set up mvEmbed jQuery bindings and config based dependencies
1045 mv_jqueryBindings();
1046 _this
.jQuerySetupFlag
= true;
1054 embedVideoCheck:function( callback
) {
1056 js_log( 'embedVideoCheck:' );
1057 // Make sure we have jQuery
1058 _this
.jQueryCheck( function() {
1059 //set class videonojs to loading
1060 $j('.videonojs').html( gM('mwe-loading_txt') );
1061 //Set up the embed video player class request: (include the skin js as well)
1074 //add any requested skins (suports multiple skins per single page)
1075 if( $mw
.skin_list
){
1076 for(var i
in $mw
.skin_list
){
1077 depReq
[0].push( $mw
.skin_list
[i
] + 'Config' );
1082 // Add PNG fix if needed:
1083 if( $j
.browser
.msie
|| $j
.browser
.version
< 7 )
1084 depReq
[0].push( '$j.fn.pngFix' );
1086 //load the video libs:
1087 _this
.doLoadDepMode( depReq
, function() {
1090 $j('.videonojs').remove();
1094 addLoadEvent: function( fn
) {
1095 this.onReadyEvents
.push( fn
);
1097 // Check the jQuery flag. This way, when remote embedding, we don't load jQuery
1098 // unless js2AddOnloadHook was used or there is video on the page.
1099 runQueuedFunctions: function() {
1100 js_log("runQueuedFunctions");
1102 this.jQueryCheck( function() {
1103 _this
.runReadyEvents();
1104 _this
.doneReadyEvents
= true;
1107 runReadyEvents: function() {
1108 js_log( "runReadyEvents" );
1109 while( this.onReadyEvents
.length
) {
1110 var func
= this.onReadyEvents
.shift();
1111 //js_log('run onReady:: ' + func );
1117 // Shortcut ( @@todo consolidate shortcuts & re-factor mvJsLoader )
1118 function mwLoad( loadSet
, callback
) {
1119 mvJsLoader
.doLoad( loadSet
, callback
);
1124 // Load an external JS file. Similar to jquery .require plugin,
1125 // but checks for object availability rather than load state.
1127 /*********** INITIALIZATION CODE *************
1128 * This will get called when the DOM is ready
1129 *********************************************/
1130 /* jQuery .ready does not work when jQuery is loaded dynamically.
1131 * For an example of the problem see: 1.1.3 working: http://pastie.caboo.se/92588
1132 * and >= 1.1.4 not working: http://pastie.caboo.se/92595
1133 * $j(document).ready( function(){ */
1134 function mwdomReady( force
) {
1135 js_log( 'f:mwdomReady:' );
1136 if( !force
&& $mw
.init_done
) {
1137 js_log( "mw done, do nothing..." );
1140 $mw
.init_done
= true;
1141 // Handle the execution of queued functions with jQuery "ready"
1143 // Check if this page has a video, audio or playlist tag
1145 document
.getElementsByTagName( "video" ),
1146 document
.getElementsByTagName( "audio" ),
1147 document
.getElementsByTagName( "playlist" )
1149 if( e
[0].length
!= 0 || e
[1].length
!= 0 || e
[2].length
!= 0 ) {
1150 //look for any skin classes we have to load:
1153 if(e
[j
][k
] && typeof( e
[j
][k
]) == 'object'){
1154 var sn
= e
[j
][k
].getAttribute('class');
1155 if( sn
&& sn
!= ''){
1156 for(var n
=0;n
< $mw
.valid_skins
.length
;n
++){
1157 if( sn
.indexOf($mw
.valid_skins
[n
]) !== -1){
1158 $mw
.skin_list
.push( $mw
.valid_skins
[n
] );
1165 // Load libs and process videos
1166 mvJsLoader
.embedVideoCheck( function() {
1167 // Run any queued global events:
1168 mv_video_embed( function() {
1169 mvJsLoader
.runQueuedFunctions();
1173 mvJsLoader
.runQueuedFunctions();
1177 //js2AddOnloadHook: ensure jQuery and the DOM are ready
1178 function js2AddOnloadHook( func
) {
1179 //js_log('js2AddOnloadHook:: jquery:' + typeof window.jQuery + ' $j: ' + typeof $j);
1180 //check for jQuery then add the load event (to run after video tag rewrites (if present)
1181 mvJsLoader
.jQueryCheck( function() {
1182 if( mvJsLoader
.doneReadyEvents
) {
1183 //js_log('run queued event: ' + func);
1186 mvJsLoader
.addLoadEvent( func
);
1190 // Deprecated mwAddOnloadHook in favor of js2 naming (for clear separation of js2 code from old MW code
1191 var mwAddOnloadHook
= js2AddOnloadHook
;
1193 * This function allows for targeted rewriting
1195 function rewrite_by_id( vid_id
, ready_callback
) {
1196 js_log( 'f:rewrite_by_id: ' + vid_id
);
1197 // Force a re-check of the DOM for playlist or video elements:
1198 mvJsLoader
.embedVideoCheck( function() {
1199 mv_video_embed( ready_callback
, vid_id
);
1204 /*********** INITIALIZATION CODE *************
1205 * set DOM-ready callback to init_mv_embed
1206 *********************************************/
1207 // for Mozilla / modern browsers
1208 if ( document
.addEventListener
) {
1209 document
.addEventListener( "DOMContentLoaded", mwdomReady
, false );
1212 if( window
.onload
) {
1213 temp_f
= window
.onload
;
1215 // Use the onload method as a backup
1216 window
.onload = function () {
1223 * Store all the mwEmbed jQuery-specific bindings
1224 * (set up after jQuery is available).
1226 * These functions are genneraly are loaders that do the dynamic mapping of
1227 * dependencies for a given commponet
1231 function mv_jqueryBindings() {
1232 js_log( 'mv_jqueryBindings' );
1235 * dragDrop file loader
1237 $.fn
.dragFileUpload = function ( conf
){
1238 if( this.selector
){
1240 //load the dragger and "setup"
1241 $mw
.load( ['$j.fn.dragDropFile'], function(){
1242 $j(_this
.selector
).dragDropFile();
1247 * apiProxy Loader loader:
1249 * @param mode is either 'server' or 'client'
1251 $.apiProxy = function( mode
, pConf
, callback
){
1252 js_log('do apiProxy setup');
1253 mvJsLoader
.doLoad( [
1257 //do the proxy setup or
1258 if( mode
== 'client'){
1259 //just do the setup (no callbcak for client setup)
1260 $mw
.proxy
.client( pConf
);
1263 }else if( mode
=='server' ){
1264 //do the request with the callback
1265 $mw
.proxy
.server( pConf
, callback
);
1270 //non selector based add-media-wizard direct invocation with loader
1271 $.addMediaWiz = function( iObj
, callback
){
1272 js_log(".addMediaWiz call");
1273 //check if already loaded:
1274 if( _global
['rsdMVRS'] ){
1275 _global
['rsdMVRS'].doReDisplay();
1277 callback( _global
['rsdMVRS'] );
1281 $.addLoaderDialog( gM('mwe-loading-add-media-wiz') );
1282 //load the addMedia wizard without a target:
1283 $.fn
.addMediaWiz ( iObj
, function( amwObj
){
1285 $.closeLoaderDialog();
1286 //do the add-media-wizard display
1287 amwObj
.doInitDisplay();
1288 //call the parent callback:
1290 callback( _global
['rsdMVRS'] );
1293 $.fn
.addMediaWiz = function( iObj
, callback
) {
1294 if( this.selector
){
1295 // First set the cursor for the button to "loading"
1296 $j( this.selector
).css( 'cursor', 'wait' ).attr( 'title', gM( 'mwe-loading_txt' ) );
1298 iObj
['target_invocation'] = this.selector
;
1301 // Load the mv_embed_base skin:
1302 loadExternalCss( mv_jquery_skin_path
+ 'jquery-ui-1.7.1.custom.css' );
1303 loadExternalCss( mv_embed_path
+ 'skins/' + $mw
.conf
['skin_name'] + '/styles.css' );
1304 // Load all the required libs:
1305 mvJsLoader
.jQueryCheck( function() {
1306 // Load with staged dependencies (for IE that does not execute in order)
1307 mvJsLoader
.doLoadDepMode([
1308 [ 'remoteSearchDriver',
1310 '$j.fn.textSelection',
1320 iObj
['instance_name'] = 'rsdMVRS';
1321 if( ! _global
['rsdMVRS'] )
1322 _global
['rsdMVRS'] = new remoteSearchDriver( iObj
);
1324 callback( _global
['rsdMVRS'] );
1332 $.fn
.sequencer = function( iObj
, callback
) {
1334 iObj
['target_sequence_container'] = this.selector
;
1335 // Issue a request to get the CSS file (if not already included):
1336 loadExternalCss( mv_jquery_skin_path
+ 'jquery-ui-1.7.1.custom.css' );
1337 loadExternalCss( mv_embed_path
+ 'skins/' + $mw
.conf
['skin_name'] + '/mv_sequence.css' );
1338 // Make sure we have the required mv_embed libs (they are not loaded when no video
1339 // element is on the page)
1340 mvJsLoader
.embedVideoCheck( function() {
1341 // Load the playlist object and then the jQuery UI stuff:
1342 mvJsLoader
.doLoadDepMode([
1355 '$j.ui.progressbar',
1362 js_log( 'calling new mvSequencer' );
1363 // Initialise the sequence object (it will take over from there)
1364 // No more than one mvSeq obj for now:
1365 if( !_global
['mvSeq'] ) {
1366 _global
['mvSeq'] = new mvSequencer( iObj
);
1368 js_log( 'mvSeq already init' );
1374 * The Firefogg jQuery function:
1375 * @@note This Firefogg invocation could be made to work more like real jQuery plugins
1377 var queuedFirefoggConf
= {};
1378 $.fn
.firefogg = function( iObj
, callback
) {
1381 // Add the base theme CSS:
1382 loadExternalCss( mv_jquery_skin_path
+ 'jquery-ui-1.7.1.custom.css' );
1383 loadExternalCss( mv_embed_path
+ 'skins/' + $mw
.conf
['skin_name'] + '/styles.css' );
1385 // Check if we already have Firefogg loaded (the call just updates the element's
1387 var sElm
= $j( this.selector
).get( 0 );
1388 if( sElm
['firefogg'] ) {
1389 if( sElm
['firefogg'] == 'loading' ) {
1390 js_log( "Queued firefogg operations ( firefogg " +
1391 "not done loading ) " );
1392 $j
.extend( queuedFirefoggConf
, iObj
);
1395 // Update properties
1396 for( var i
in iObj
) {
1397 js_log( "firefogg::updated: " + i
+ ' to '+ iObj
[i
] );
1398 sElm
['firefogg'][i
] = iObj
[i
];
1400 return sElm
['firefogg'];
1403 sElm
['firefogg'] = 'loading';
1406 iObj
['selector'] = this.selector
;
1410 'mvBaseUploadInterface',
1415 '$j.ui.progressbar',
1420 if( iObj
.encoder_interface
) {
1429 // Make sure we have everything loaded that we need:
1430 mvJsLoader
.doLoadDepMode( loadSet
, function() {
1431 js_log( 'firefogg libs loaded. target select:' + iObj
.selector
);
1432 // Select interface provider based on whether we want to include the
1433 // encoder interface or not
1434 if( iObj
.encoder_interface
) {
1435 var myFogg
= new mvAdvFirefogg( iObj
);
1437 var myFogg
= new mvFirefogg( iObj
);
1440 myFogg
.doRewrite( callback
);
1441 var selectorElement
= $j( iObj
.selector
).get( 0 );
1442 selectorElement
['firefogg'] = myFogg
;
1444 js_log('pre:'+ selectorElement
['firefogg']['firefogg_form_action'])
1445 if(queuedFirefoggConf
)
1446 $j
.extend(selectorElement
['firefogg'], queuedFirefoggConf
);
1447 js_log('post:'+ selectorElement
['firefogg']['firefogg_form_action'])
1451 // Take an input player as the selector and expose basic rendering controls
1452 $.fn
.firefoggRender = function( iObj
, callback
) {
1453 // Check if we already have render loaded then just pass on updates/actions
1454 var sElm
= $j( this.selector
).get( 0 );
1455 if( sElm
['fogg_render'] ) {
1456 if( sElm
['fogg_render'] == 'loading' ) {
1457 js_log( "Error: called firefoggRender while loading" );
1460 // Call or update the property:
1462 sElm
['fogg_render'] = 'loading';
1464 iObj
['player_target'] = this.selector
;
1469 sElm
['fogg_render'] = new mvFirefoggRender( iObj
);
1470 if( callback
&& typeof callback
== 'function' )
1471 callback( sElm
['fogg_render'] );
1475 $.fn
.baseUploadInterface = function(iObj
) {
1476 mvJsLoader
.doLoadDepMode([
1478 'mvBaseUploadInterface',
1482 '$j.ui.progressbar',
1486 myUp
= new mvBaseUploadInterface( iObj
);
1491 // Shortcut to a themed button
1492 $.btnHtml = function( msg
, className
, iconId
, opt
) {
1495 var href
= (opt
.href
) ? opt
.href
: '#';
1496 var target_attr
= (opt
.target
) ? ' target="' + opt
.target
+ '" ' : '';
1497 var style_attr
= (opt
.style
) ? ' style="' + opt
.style
+ '" ' : '';
1498 return '<a href="' + href
+ '" ' + target_attr
+ style_attr
+
1499 ' class="ui-state-default ui-corner-all ui-icon_link ' +
1500 className
+ '"><span class="ui-icon ui-icon-' + iconId
+ '" />' +
1501 '<span class="btnText">'+ msg
+'<span></a>';
1503 // Shortcut to bind hover state
1504 $.fn
.btnBind = function() {
1507 $j( this ).addClass( 'ui-state-hover' );
1510 $j( this ).removeClass( 'ui-state-hover' );
1516 * resize the dialog to fit the window
1518 $.fn
.dialogFitWindow = function(opt
){
1519 var opt_default
= {'hspace':50,'vspace':50};
1522 $j
.extend(opt
, opt_default
);
1523 $j( this.selector
).dialog('option', 'width', $j(window
).width() - opt
.hspace
);
1524 $j( this.selector
).dialog('option', 'height', $j(window
).height() - opt
.vspace
);
1525 $j( this.selector
).dialog('option', 'position','center');
1526 //update the child position: (some of this should be pushed up-stream via dialog config options
1527 $j( this.selector
+'~ .ui-dialog-buttonpane').css({
1528 'position':'absolute',
1537 * small helper for putting a loading dialog box on top of everything
1538 * (helps block for request that
1540 * @param msg text text of the loader msg
1542 $.addLoaderDialog = function( msg_txt
){
1543 $.addDialog( msg_txt
, msg_txt
+ '<br>' + mv_get_loading_img() );
1546 $.addDialog = function ( title
, msg_txt
, btn
){
1547 $('#mwe_tmp_loader').remove();
1548 //append the style free loader ontop:
1549 $('body').append('<div id="mwe_tmp_loader" style="display:none" title="' + title
+ '" >' +
1552 //special btn == ok gives empty give a single "oky" -> "close"
1554 btn
[ gM('mwe-ok') ] = function(){
1555 $j('#mwe_tmp_loader').close();
1558 //turn the loader into a real dialog loader:
1559 mvJsLoader
.doLoadDepMode([
1567 $('#mwe_tmp_loader').dialog({
1577 $.closeLoaderDialog = function(){
1578 mvJsLoader
.doLoadDepMode([
1586 $j('#mwe_tmp_loader').dialog('close');
1590 $.mwProxy = function( apiConf
){
1591 mvJsLoader
.doLoad( ['$mw.apiProxy'],
1593 $mw
.apiProxy( apiConf
);
1599 * Utility functions:
1601 // Simple URL rewriter (could probably be refactored into an inline regular exp)
1602 function getURLParamReplace( url
, opt
) {
1603 var pSrc
= parseUri( url
);
1604 if( pSrc
.protocol
!= '' ) {
1605 var new_url
= pSrc
.protocol
+ '://' + pSrc
.authority
+ pSrc
.path
+ '?';
1607 var new_url
= pSrc
.path
+ '?';
1610 for( var key
in pSrc
.queryKey
) {
1611 var val
= pSrc
.queryKey
[ key
];
1612 // Do override if requested
1615 new_url
+= amp
+ key
+ '=' + val
;
1618 // Add any vars that were not already there:
1619 for( var i
in opt
) {
1620 if( !pSrc
.queryKey
[i
] ) {
1621 new_url
+= amp
+ i
+ '=' + opt
[i
];
1628 * Given a float number of seconds, returns npt format response.
1630 * @param float Seconds
1631 * @param boolean If we should show milliseconds or not.
1633 function seconds2npt( sec
, show_ms
) {
1634 if( isNaN( sec
) ) {
1635 // js_log("warning: trying to get npt time on NaN:" + sec);
1638 var hours
= Math
.floor( sec
/ 3600 );
1639 var minutes
= Math
.floor( (sec
/ 60) % 60 );
1640 var seconds
= sec
% 60;
1641 // Round the number of seconds to the required number of significant digits
1643 seconds
= Math
.round( seconds
* 1000 ) / 1000;
1645 seconds
= Math
.round( seconds
);
1648 seconds
= '0' + seconds
;
1650 minutes
= '0' + minutes
;
1652 return hours
+ ":" + minutes
+ ":" + seconds
;
1655 * Take hh:mm:ss,ms or hh:mm:ss.ms input, return the number of seconds
1657 function npt2seconds( npt_str
) {
1659 //js_log('npt2seconds:not valid ntp:'+ntp);
1662 // Strip "npt:" time definition if present
1663 npt_str
= npt_str
.replace( 'npt:', '' );
1665 times
= npt_str
.split( ':' );
1666 if( times
.length
!= 3 ){
1667 js_log( 'error: npt2seconds on ' + npt_str
);
1670 // Sometimes a comma is used instead of period for ms
1671 times
[2] = times
[2].replace( /,\s?/, '.' );
1672 // Return seconds float
1673 return parseInt( times
[0] * 3600) + parseInt( times
[1] * 60 ) + parseFloat( times
[2] );
1676 * Simple helper to grab an edit token
1678 * @param title The wiki page title you want to edit
1679 * @param api_url 'optional' The target API URL
1680 * @param callback The callback function to pass the token to
1682 function get_mw_token( title
, api_url
, callback
) {
1683 js_log( ':get_mw_token:' );
1684 if( !title
&& wgUserName
) {
1685 title
= 'User:' + wgUserName
;
1697 for( var i
in data
.query
.pages
) {
1698 if( data
.query
.pages
[i
]['edittoken'] ) {
1699 if( typeof callback
== 'function' )
1700 callback ( data
.query
.pages
[i
]['edittoken'] );
1708 // Do a remote or local API request based on request URL
1709 //@param options: url, data, cbParam, callback
1710 function do_api_req( options
, callback
) {
1711 if( typeof options
.data
!= 'object' ) {
1712 return js_error( 'Error: request paramaters must be an object' );
1714 // Generate the URL if it's missing
1715 if( typeof options
.url
== 'undefined' || !options
.url
) {
1716 if( !wgServer
|| ! wgScriptPath
) {
1717 return js_error('Error: no api url for api request');
1719 options
.url
= mwGetLocalApiUrl();
1721 if( typeof options
.data
== 'undefined' )
1724 // Force format to JSON
1725 options
.data
['format'] = 'json';
1727 // If action is not set, assume query
1728 if( ! options
.data
['action'] )
1729 options
.data
['action'] = 'query';
1731 // js_log('do api req: ' + options.url +'?' + jQuery.param(options.data) );
1732 if( options
.url
== 'proxy' && $mw
.proxy
){
1733 //assume the proxy is already "setup" since $mw.proxy is defined.
1734 // @@todo we probably integrate that setup into the api call
1735 $mw
.proxy
.doRequest( options
.data
, callback
);
1736 }else if( parseUri( document
.URL
).host
== parseUri( options
.url
).host
) {
1737 // Local request: do API request directly
1742 dataType
: 'json', // API requests _should_ always return JSON data:
1744 success: function( data
) {
1747 error: function( e
) {
1748 js_error( ' error' + e
+ ' in getting: ' + options
.url
);
1753 // Set the callback param if it's not already set
1754 if( typeof options
.jsonCB
== 'undefined' )
1755 options
.jsonCB
= 'callback';
1757 var req_url
= options
.url
;
1758 var paramAnd
= ( req_url
.indexOf( '?' ) == -1 ) ? '?' : '&';
1759 // Put all the parameters into the URL
1760 for( var i
in options
.data
) {
1761 req_url
+= paramAnd
+ encodeURIComponent( i
) + '=' + encodeURIComponent( options
.data
[i
] );
1764 var fname
= 'mycpfn_' + ( $mw
.cb_count
++ );
1765 _global
[ fname
] = callback
;
1766 req_url
+= '&' + options
.jsonCB
+ '=' + fname
;
1767 loadExternalJs( req_url
);
1770 function mwGetLocalApiUrl( url
) {
1771 if ( wgServer
&& wgScriptPath
) {
1772 return wgServer
+ wgScriptPath
+ '/api.php';
1776 // Do a "normal" request
1777 function do_request( req_url
, callback
) {
1778 js_log( 'do_request::req_url:' + req_url
+ ' != ' + parseUri( req_url
).host
);
1779 // If we are doing a request to the same domain or relative link, do a normal GET
1780 if( parseUri( document
.URL
).host
== parseUri( req_url
).host
||
1781 req_url
.indexOf('://') == -1 ) // Relative url
1783 // Do a direct request
1788 success: function( data
) {
1793 // Get data via DOM injection with callback
1794 global_req_cb
.push( callback
);
1795 // Prepend json_ to feed_format if not already requesting json format (metavid specific)
1796 if( req_url
.indexOf( "feed_format=" ) != -1 && req_url
.indexOf( "feed_format=json" ) == -1 )
1797 req_url
= req_url
.replace( /feed_format=/, 'feed_format=json_' );
1799 loadExternalJs( req_url
+ '&cb=mv_jsdata_cb&cb_inx=' + (global_req_cb
.length
- 1) );
1803 function mv_jsdata_cb( response
) {
1804 js_log( 'f:mv_jsdata_cb:'+ response
['cb_inx'] );
1805 // Run the callback from the global request callback object
1806 if( !global_req_cb
[response
['cb_inx']] ) {
1807 js_log( 'missing req cb index' );
1810 if( !response
['pay_load'] ) {
1811 js_log( "missing pay load" );
1814 switch( response
['content-type'] ) {
1818 if( typeof response
['pay_load'] == 'string' ) {
1819 //js_log('load string:'+"\n"+ response['pay_load']);
1821 if( $j
.browser
.msie
) {
1822 // Attempt to parse as XML for IE
1823 var xmldata
= new ActiveXObject("Microsoft.XMLDOM");
1824 xmldata
.async
= "false";
1825 xmldata
.loadXML( response
['pay_load'] );
1827 // For others (Firefox, Safari etc.)
1829 var xmldata
= (new DOMParser()).parseFromString( response
['pay_load'], "text/xml" );
1831 js_log( 'XML parse ERROR: ' + e
.message
);
1834 //@@todo handle XML parser errors
1835 if( xmldata
)response
['pay_load'] = xmldata
;
1839 js_log( 'bad response type' + response
['content-type'] );
1843 global_req_cb
[response
['cb_inx']]( response
['pay_load'] );
1845 // Load external JS via DOM injection
1846 function loadExternalJs( url
, callback
) {
1847 js_log( 'load js: '+ url
);
1848 //if(window['$j']) // use jquery call:
1856 var e
= document
.createElement( "script" );
1857 e
.setAttribute( 'src', url
);
1858 e
.setAttribute( 'type', "text/javascript" );
1860 e.onload = callback;
1862 //e.setAttribute('defer', true);
1863 document
.getElementsByTagName( "head" )[0].appendChild( e
);
1866 function styleSheetPresent( url
) {
1867 style_elements
= document
.getElementsByTagName( 'link' );
1868 if( style_elements
.length
> 0 ) {
1869 for( i
= 0; i
< style_elements
.length
; i
++ ) {
1870 if( style_elements
[i
].href
== url
)
1876 function loadExternalCss( url
) {
1877 // We could have the script loader group these CSS requests.
1878 // But it's debatable: it may hurt more than it helps with caching and all
1879 if( typeof url
=='object' ) {
1880 for( var i
in url
) {
1881 loadExternalCss( url
[i
] );
1886 if( url
.indexOf('?') == -1 ) {
1887 url
+= '?' + getMwReqParam();
1889 if( !styleSheetPresent( url
) ) {
1890 js_log( 'load css: ' + url
);
1891 var e
= document
.createElement( "link" );
1893 e
.type
= "text/css";
1894 e
.rel
= 'stylesheet';
1895 document
.getElementsByTagName( "head" )[0].appendChild( e
);
1898 function getMvEmbedURL() {
1899 if( _global
['mv_embed_url'] )
1900 return _global
['mv_embed_url'];
1901 var js_elements
= document
.getElementsByTagName( "script" );
1902 for( var i
= 0; i
< js_elements
.length
; i
++ ) {
1903 // Check for mv_embed.js and/or script loader
1904 var src
= js_elements
[i
].getAttribute( "src" );
1906 if( src
.indexOf( 'mv_embed.js' ) != -1 || (
1907 ( src
.indexOf( 'mwScriptLoader.php' ) != -1 || src
.indexOf('jsScriptLoader.php') != -1 )
1908 && src
.indexOf('mv_embed') != -1) ) //(check for class=mv_embed script_loader call)
1910 _global
['mv_embed_url'] = src
;
1915 js_error( 'Error: getMvEmbedURL failed to get Embed Path' );
1918 // Get a unique request ID to ensure fresh JavaScript
1919 function getMwReqParam() {
1920 if( _global
['req_param'] )
1921 return _global
['req_param'];
1922 var mv_embed_url
= getMvEmbedURL();
1926 // If we have a URI, add it to the req
1927 var urid
= parseUri( mv_embed_url
).queryKey
['urid']
1928 // If we're in debug mode, get a fresh unique request key and pass on "debug" param
1929 if( parseUri( mv_embed_url
).queryKey
['debug'] == 'true' ){
1931 req_param
+= 'urid=' + d
.getTime() + '&debug=true';
1933 // Set from request urid:
1934 req_param
+= 'urid=' + urid
;
1936 // Otherwise, just use the mv_embed version
1937 req_param
+= 'urid=' + $mw
.version
;
1939 //add the lang param:
1940 var langKey
= parseUri( mv_embed_url
).queryKey
['uselang'];
1942 req_param
+= '&uselang=' + langKey
;
1944 _global
['req_param'] = req_param
;
1946 return _global
['req_param'];
1949 * Set the global mv_embed path based on the script's location
1951 function getMvEmbedPath() {
1952 if( _global
['mv_embed_path'] )
1953 return _global
['mv_embed_path'];
1954 var mv_embed_url
= getMvEmbedURL();
1955 if( mv_embed_url
.indexOf( 'mv_embed.js' ) !== -1 ) {
1956 mv_embed_path
= mv_embed_url
.substr( 0, mv_embed_url
.indexOf( 'mv_embed.js' ) );
1957 } else if( mv_embed_url
.indexOf( 'mwScriptLoader.php' ) !== -1 ) {
1958 // Script loader is in the root of MediaWiki, so include the default mv_embed extension path
1959 mv_embed_path
= mv_embed_url
.substr( 0, mv_embed_url
.indexOf( 'mwScriptLoader.php' ) )
1960 + mediaWiki_mvEmbed_path
;
1962 mv_embed_path
= mv_embed_url
.substr( 0, mv_embed_url
.indexOf( 'jsScriptLoader.php' ) );
1964 // Make an absolute URL (if it's relative and we don't have an mv_embed path)
1965 if( mv_embed_path
.indexOf( '://' ) == -1 ) {
1966 var pURL
= parseUri( document
.URL
);
1967 if( mv_embed_path
.charAt( 0 ) == '/' ) {
1968 mv_embed_path
= pURL
.protocol
+ '://' + pURL
.authority
+ mv_embed_path
;
1971 if( mv_embed_path
== '' ) {
1972 mv_embed_path
= pURL
.protocol
+ '://' + pURL
.authority
+ pURL
.directory
+ mv_embed_path
;
1976 _global
['mv_embed_path'] = mv_embed_path
;
1977 return mv_embed_path
;
1980 if ( typeof DOMParser
== "undefined" ) {
1981 DOMParser = function () {}
1982 DOMParser
.prototype.parseFromString = function ( str
, contentType
) {
1983 if ( typeof ActiveXObject
!= "undefined" ) {
1984 var d
= new ActiveXObject( "MSXML.DomDocument" );
1987 } else if ( typeof XMLHttpRequest
!= "undefined" ) {
1988 var req
= new XMLHttpRequest
;
1989 req
.open( "GET", "data:" + (contentType
|| "application/xml") +
1990 ";charset=utf-8," + encodeURIComponent(str
), false );
1991 if ( req
.overrideMimeType
) {
1992 req
.overrideMimeType(contentType
);
1995 return req
.responseXML
;
2002 function js_log( string
) {
2003 ///add any prepend debug strings if nessesary (used for cross browser)
2004 if( $mw
.conf
['debug_pre'] )
2005 string
= $mw
.conf
['debug_pre']+ string
;
2007 if( window
.console
) {
2008 window
.console
.log( string
);
2011 * IE and non-Firebug debug:
2013 /*var log_elm = document.getElementById('mv_js_log');
2015 document.getElementsByTagName("body")[0].innerHTML = document.getElementsByTagName("body")[0].innerHTML +
2016 '<div style="position:absolute;z-index:500;top:0px;left:0px;right:0px;height:10px;">'+
2017 '<textarea id="mv_js_log" cols="120" rows="5"></textarea>'+
2020 var log_elm = document.getElementById('mv_js_log');
2023 log_elm.value+=string+"\n";
2029 function js_error( string
) {