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
);
45 if ( typeof mvCssPaths
== 'undefined' )
48 function lcCssPath( cssSet
) {
49 for ( var i
in cssSet
) {
50 mvCssPaths
[i
] = cssSet
[i
];
55 * -- Load Class Paths --
57 * MUST BE VALID JSON (NOT JS)
58 * This is used by the script loader to auto-load classes (so we only define
59 * this once for PHP & JavaScript)
61 * Right now the PHP AutoLoader only reads this mv_embed.js file.
62 * In the future we could have multiple lcPath calls that PHP reads
63 * (if our autoloading class list becomes too long)
65 * we could support direct file requests to the script loader instead
66 * of shared class names read from a central location.
69 "mv_embed" : "mv_embed.js",
70 "window.jQuery" : "jquery/jquery-1.3.2.js",
71 "$j.fn.pngFix" : "jquery/plugins/jquery.pngFix.js",
72 "$j.fn.autocomplete": "jquery/plugins/jquery.autocomplete.js",
73 "$j.fn.hoverIntent" : "jquery/plugins/jquery.hoverIntent.js",
74 "$j.fn.datePicker" : "jquery/plugins/jquery.datePicker.js",
75 "$j.ui" : "jquery/jquery.ui/ui/ui.core.js",
76 "$j.fn.ColorPicker" : "libClipEdit/colorpicker/js/colorpicker.js",
77 "$j.Jcrop" : "libClipEdit/Jcrop/js/jquery.Jcrop.js",
78 "$j.fn.simpleUploadForm" : "libAddMedia/simpleUploadForm.js",
80 "mw.proxy" : "libMwApi/mw.proxy.js",
82 "mw.testLang" : "tests/testLang.js",
84 "ctrlBuilder" : "skins/ctrlBuilder.js",
85 "kskinConfig" : "skins/kskin/kskin.js",
86 "mvpcfConfig" : "skins/mvpcf/mvpcf.js",
88 "JSON" : "libMwApi/json2.js",
89 "$j.cookie" : "jquery/plugins/jquery.cookie.js",
90 "$j.contextMenu" : "jquery/plugins/jquery.contextMenu.js",
91 "$j.fn.suggestions" : "jquery/plugins/jquery.suggestions.js",
92 "$j.fn.textSelection" : "jquery/plugins/jquery.textSelection.js",
94 "$j.effects.blind" : "jquery/jquery.ui/ui/effects.blind.js",
95 "$j.effects.drop" : "jquery/jquery.ui/ui/effects.drop.js",
96 "$j.effects.pulsate" : "jquery/jquery.ui/ui/effects.pulsate.js",
97 "$j.effects.transfer" : "jquery/jquery.ui/ui/effects.transfer.js",
98 "$j.ui.droppable" : "jquery/jquery.ui/ui/ui.droppable.js",
99 "$j.ui.slider" : "jquery/jquery.ui/ui/ui.slider.js",
100 "$j.effects.bounce" : "jquery/jquery.ui/ui/effects.bounce.js",
101 "$j.effects.explode" : "jquery/jquery.ui/ui/effects.explode.js",
102 "$j.effects.scale" : "jquery/jquery.ui/ui/effects.scale.js",
103 "$j.ui.datepicker" : "jquery/jquery.ui/ui/ui.datepicker.js",
104 "$j.ui.progressbar" : "jquery/jquery.ui/ui/ui.progressbar.js",
105 "$j.ui.sortable" : "jquery/jquery.ui/ui/ui.sortable.js",
106 "$j.effects.clip" : "jquery/jquery.ui/ui/effects.clip.js",
107 "$j.effects.fold" : "jquery/jquery.ui/ui/effects.fold.js",
108 "$j.effects.shake" : "jquery/jquery.ui/ui/effects.shake.js",
109 "$j.ui.dialog" : "jquery/jquery.ui/ui/ui.dialog.js",
110 "$j.ui.resizable" : "jquery/jquery.ui/ui/ui.resizable.js",
111 "$j.ui.tabs" : "jquery/jquery.ui/ui/ui.tabs.js",
112 "$j.effects.core" : "jquery/jquery.ui/ui/effects.core.js",
113 "$j.effects.highlight" : "jquery/jquery.ui/ui/effects.highlight.js",
114 "$j.effects.slide" : "jquery/jquery.ui/ui/effects.slide.js",
115 "$j.ui.accordion" : "jquery/jquery.ui/ui/ui.accordion.js",
116 "$j.ui.draggable" : "jquery/jquery.ui/ui/ui.draggable.js",
117 "$j.ui.selectable" : "jquery/jquery.ui/ui/ui.selectable.js",
119 "$j.fn.dragDropFile" : "libAddMedia/dragDropFile.js",
120 "mvFirefogg" : "libAddMedia/mvFirefogg.js",
121 "mvAdvFirefogg" : "libAddMedia/mvAdvFirefogg.js",
122 "mvBaseUploadInterface" : "libAddMedia/mvBaseUploadInterface.js",
123 "remoteSearchDriver" : "libAddMedia/remoteSearchDriver.js",
124 "seqRemoteSearchDriver" : "libSequencer/seqRemoteSearchDriver.js",
126 "baseRemoteSearch" : "libAddMedia/searchLibs/baseRemoteSearch.js",
127 "mediaWikiSearch" : "libAddMedia/searchLibs/mediaWikiSearch.js",
128 "metavidSearch" : "libAddMedia/searchLibs/metavidSearch.js",
129 "archiveOrgSearch" : "libAddMedia/searchLibs/archiveOrgSearch.js",
130 "flickrSearch" : "libAddMedia/searchLibs/flickrSearch.js",
131 "baseRemoteSearch" : "libAddMedia/searchLibs/baseRemoteSearch.js",
133 "mvClipEdit" : "libClipEdit/mvClipEdit.js",
135 "embedVideo" : "libEmbedVideo/embedVideo.js",
136 "flowplayerEmbed" : "libEmbedVideo/flowplayerEmbed.js",
137 "kplayerEmbed" : "libEmbedVideo/kplayerEmbed.js",
138 "genericEmbed" : "libEmbedVideo/genericEmbed.js",
139 "htmlEmbed" : "libEmbedVideo/htmlEmbed.js",
140 "javaEmbed" : "libEmbedVideo/javaEmbed.js",
141 "nativeEmbed" : "libEmbedVideo/nativeEmbed.js",
142 "quicktimeEmbed" : "libEmbedVideo/quicktimeEmbed.js",
143 "vlcEmbed" : "libEmbedVideo/vlcEmbed.js",
145 "mvPlayList" : "libSequencer/mvPlayList.js",
146 "mvSequencer" : "libSequencer/mvSequencer.js",
147 "mvFirefoggRender" : "libSequencer/mvFirefoggRender.js",
148 "mvTimedEffectsEdit": "libSequencer/mvTimedEffectsEdit.js",
150 "mvTextInterface" : "libTimedText/mvTextInterface.js",
151 "mvTimeTextEdit" : "libTimedText/mvTimeTextEdit.js"
154 // Dependency mapping for CSS files for self-contained included plugins:
156 '$j.Jcrop' : 'libClipEdit/Jcrop/css/jquery.Jcrop.css',
157 '$j.fn.ColorPicker' : 'libClipEdit/colorpicker/css/colorpicker.css'
160 // For use when mv_embed with script-loader is in the root MediaWiki path
161 var mediaWiki_mvEmbed_path
= 'js2/mwEmbed/';
163 // The global scope: will be depreciated once we get everything into mw
167 * setup the empty global mw object
168 * will ensure all our functions and variables are properly namespaced
169 * reducing chance of conflicts
171 if ( !window
['mw'] ) {
176 // Inherit the default global config
177 var mwDefaultConf
= {
178 'skin_name' : 'mvpcf',
179 'jui_skin' : 'redmond',
180 'video_size' : '400x300',
181 'k_attribution' : true
186 for(var i
in mwDefaultConf
){
187 if( typeof mw
.conf
[ i
] == 'undefined' )
188 mw
.conf
[ i
] = mwDefaultConf
[ i
];
192 // @@todo move these into mw
193 var global_req_cb
= new Array(); // The global request callback array
196 * The global mw object:
198 * Any global functions/classes that are not jQuery plugins should make
199 * there way into the mw namespace
202 // list valid skins here:
203 $.valid_skins
= ['mvpcf', 'kskin'];
204 // the version of mwEmbed
205 $.version
= '1.0r21';
207 // special case of commons api url
208 // (used for default subtitles server for media with a "wikiTitleKey" atm)
209 // (@@todo eventually we should have wikiTitleKey be namespaced with interwiki ns
210 $.commons_api_url
= 'http://commons.wikimedia.org/w/api.php';
212 * some global containers flags
214 $.skin_list
= new Array();
217 $.player_list
= new Array(), // The global player list per page
218 $.req_cb
= new Array() // The global request callback array
221 * Language classes mw.lang
223 * Localized Language support attempts to mirror the functionality of Language.php in MediaWiki
224 * It contains methods for loading and transforming msg text
229 * Setup the lang object
236 * Loads a set of json messages into the lng object.
238 * @param json msgSet The set of msgs to be loaded
240 $.lang
.loadGM = function( msgSet
) {
241 for ( var i
in msgSet
) {
242 gMsg
[ i
] = msgSet
[i
];
248 * Loads a ruleset by given template key ie PLURAL : { //ruleSetObj }
250 * @param json ruleSet The ruleset object ( extends gRuleSet )
252 $.lang
.loadRS = function( ruleSet
) {
253 for ( var i
in ruleSet
) {
254 gRuleSet
[ i
] = ruleSet
[ i
];
259 * Returns a transformed msg string
261 * it take a msg key and array of replacement values of form
262 * $1, $2 and does relevant msgkey transformation returning
265 * @param string key The msg key as set by loadGm
266 * @param [mixed] args An array of replacement strings
269 $.lang
.gM = function( key
, args
) {
271 return '<' + key
+ '>';// Missing key placeholder
273 // swap in the arg values
274 var ms
= $.lang
.gMsgSwap( key
, args
);
276 // a quick check to see if we need to send the msg via the 'parser'
277 // (we can add more detailed check once we support more wiki syntax)
278 if ( ms
.indexOf( '{{' ) === -1 && ms
.indexOf( '[' ) === -1 ) {
282 // make sure we have the lagMagic setup:
283 // @@todo move to init
285 // send the msg key through the parser
286 var pObj
= $.parser
.pNew( ms
);
287 // return the transformed msg
288 return pObj
.getHTML();
293 * @param string key The msg key as set by loadGm
294 * @param [mixed] args An array or string to be replaced
297 $.lang
.gMsgSwap = function( key
, args
) {
299 return '<' + key
+ '>';// Missing key placeholder
300 // get the message string:
301 var ms
= gMsg
[ key
];
304 if ( typeof args
== 'object' || typeof args
== 'array' ) {
305 for ( var v
in args
) {
306 // Message test replace arguments start at 1 instead of zero:
307 var rep
= new RegExp( '\\$' + ( parseInt( v
) + 1 ), 'g' );
308 ms
= ms
.replace( rep
, args
[v
] );
310 } else if ( typeof args
== 'string' || typeof args
== 'number' ) {
311 ms
= ms
.replace( /\$1/g, args
);
319 * @returns string The msg key without transforming it
321 $.lang
.gMsgNoTrans = function( key
) {
325 // Missing key placeholder
326 return '<' + key
+ '>';
329 * Add Supported Magic Words to parser
331 // Set the setupflag to false:
332 $.lang
.doneSetup
= false;
333 $.lang
.magicSetup = function() {
334 if ( !$.lang
.doneSetup
) {
335 $.parser
.addMagic ( {
336 'PLURAL' : $.lang
.procPLURAL
339 $.lang
.doneSetup
= true;
344 * Process the PLURAL special language template key:
346 $.lang
.procPLURAL = function( tObj
) {
348 // (gRuleSet is loaded from script-loader to contains local ruleset)
349 var rs
= gRuleSet
['PLURAL'];
352 * Plural matchRuleTest
354 function matchRuleTest( cRule
, val
) {
355 js_log("matchRuleTest:: " + typeof cRule
+ ' ' + cRule
+ ' == ' + val
);
356 function checkValue( compare
, val
) {
357 if ( typeof compare
== 'string' ) {
358 range
= compare
.split( '-' );
359 if ( range
.length
>= 1 ) {
360 if ( val
>= range
[0] && val
<= range
[1] )
364 // else do a direct compare
365 if ( compare
== val
) {
370 // check for simple cRule type:
371 if ( typeof cRule
== 'number' ) {
372 return ( parseInt( val
) == parseInt( cRule
) );
373 } else if ( typeof cRule
== 'object' ) {
375 // if a list we need to match all for rule match
376 for ( var i
in cRule
) {
380 for ( var j
in cr
) {
387 if ( checkValue( val
% cr
['mod'], cr
['is'] ) )
389 } else if ( cr
['not'] ) {
390 if ( ! checkValue( val
% cr
['mod'], cr
['not'] ) )
396 // check all the matches (taking into consideration "or" order)
397 for ( var i
in cRule
) {
406 * Maps a given rule Index to template params:
408 * if index is out of range return last param
411 function getTempParamFromRuleInx( tObj
, ruleInx
) {
412 // js_log('getTempParamFromRuleInx: ruleInx: ' + ruleInx + ' tempParamLength ' + tObj.param.length );
413 if ( ruleInx
>= tObj
.param
.length
)
414 return tObj
.param
[ tObj
.param
.length
- 1 ];
415 // else return the requested index:
416 return tObj
.param
[ ruleInx
];
419 // run the actual rule lookup:
420 for ( var ruleInx
in rs
) {
422 if ( matchRuleTest( cRule
, tObj
.arg
) ) {
423 js_log("matched rule: " + ruleInx
);
424 return getTempParamFromRuleInx( tObj
, rCount
);
428 js_log('no match found for: ' + tObj
.arg
+ ' using last/other : ' + tObj
.param
[ tObj
.param
.length
-1 ] );
430 // return the last /"other" template param
431 return tObj
.param
[ tObj
.param
.length
- 1 ];
435 * gMsgLoadRemote loads remote msg strings
437 * @param mixed msgSet the set of msg to load remotely
438 * @param function callback the callback to issue once string is ready
440 $.lang
.gMsgLoadRemote = function( msgSet
, callback
) {
442 if ( typeof msgSet
== 'object' ) {
443 for ( var i
in msgSet
) {
444 ammessages
+= msgSet
[i
] + '|';
446 } else if ( typeof msgSet
== 'string' ) {
447 ammessages
+= msgSet
;
449 if ( ammessages
== '' ) {
450 js_log( 'gMsgLoadRemote: no message set requested' );
455 'meta': 'allmessages',
456 'ammessages': ammessages
458 }, function( data
) {
459 if ( data
.query
.allmessages
) {
460 var msgs
= data
.query
.allmessages
;
461 for ( var i
in msgs
) {
463 ld
[ msgs
[i
]['name'] ] = msgs
[i
]['*'];
471 * Format a size in bytes for output, using an appropriate
472 * unit (B, KB, MB or GB) according to the magnitude in question
474 * @param size Size to format
475 * @return string Plain text (not HTML)
477 $.lang
.formatSize = function ( size
) {
478 // For small sizes no decimal places are necessary
485 // For MB and bigger two decimal places are smarter
489 msg
= 'mwe-size-gigabytes';
491 msg
= 'mwe-size-megabytes';
494 msg
= 'mwe-size-kilobytes';
497 msg
= 'mwe-size-bytes';
499 // JavaScript does not let you choose the precision when rounding
500 var p
= Math
.pow( 10, round
);
501 var size
= Math
.round( size
* p
) / p
;
502 return gM( msg
, size
);
505 $.lang
.formatNumber = function( num
) {
508 * @param Str: The number to be formatted, as a string or number.
509 * @param outD: The decimal character for the output, such as ',' for the number 100,2
510 * @param sep: The separator character for the output, such as ',' for the number 1,000.2
512 function addSeparatorsNF( nStr
, outD
, sep
) {
514 var dpos
= nStr
.indexOf( '.' );
517 nStrEnd
= outD
+ nStr
.substring( dpos
+ 1, nStr
.length
);
518 nStr
= nStr
.substring( 0, dpos
);
520 var rgx
= /(\d+)(\d{3})/;
521 while ( rgx
.test( nStr
) ) {
522 nStr
= nStr
.replace( rgx
, '$1' + sep
+ '$2' );
524 return nStr
+ nStrEnd
;
526 // @@todo read language code and give periods or comas:
527 return addSeparatorsNF( num
, '.', ',' );
533 * MediaWiki wikitext "Parser"
535 * This is not feature complete but we need a way to get at template properties
538 * @param {String} wikiText the wikitext to be parsed
539 * @return {Object} parserObj returns a parser object that has methods for getting at
540 * things you would want
547 * Lets you add a set of magic keys and associated callback functions
549 * @param object magicSet key:callback
551 $.parser
.addMagic = function( magicSet
) {
552 for ( var i
in magicSet
)
553 pMagicSet
[ i
] = magicSet
[i
];
556 // actual parse call (returns parser object)
557 $.parser
.pNew = function( wikiText
, opt
) {
558 var parseObj = function( wikiText
, opt
) {
559 return this.init( wikiText
, opt
)
561 parseObj
.prototype = {
562 // the wikiText "DOM"... stores the parsed wikiText structure
563 // wtDOM : {}, (not yet supported )
565 pOut
: '', // the parser output string container
566 init :function( wikiText
) {
567 this.wikiText
= wikiText
;
569 updateText : function( wikiText
) {
570 this.wikiText
= wikiText
;
571 // invalidate the output (will force a re-parse )
576 * quickly recursive / parse out templates:
579 // ~ probably a better algorithm out there / should mirror php parser flow ~
580 // (we are already running white-space issues ie php parse strips whitespace differently)
581 // or at least expose something similar to: http://www.mediawiki.org/wiki/Extension:Page_Object_Model
583 // ... but I am having fun with recursion so here it is...
584 function rdpp ( txt
, cn
) {
587 for ( var a
= 0; a
< txt
.length
; a
++ ) {
588 if ( txt
[a
] == '{' && txt
[a
+ 1] == '{' ) {
592 node
['c'] = new Array();
594 node
['c'].push( rdpp( txt
.substr( a
), true ) );
595 } else if ( txt
[a
] == '}' && txt
[a
+ 1] == '}' ) {
604 // don't put closures into output:
605 if ( txt
[a
] && txt
[a
] != '}' )
612 * parse template text as template name and named params
614 function parseTmplTxt( ts
) {
616 // Get template name:
617 tname
= ts
.split( '\|' ).shift() ;
618 tname
= tname
.split( '\{' ).shift() ;
619 tname
= tname
.replace( /^\s+|\s+$/g, "" ); //trim
621 // check for arguments:
622 if ( tname
.split( ':' ).length
== 1 ) {
623 tObj
["name"] = tname
;
625 tObj
["name"] = tname
.split( ':' ).shift();
626 tObj
["arg"] = tname
.split( ':' ).pop();
629 var pSet
= ts
.split( '\|' );
632 tObj
.param
= new Array();
633 for ( var pInx
in pSet
) {
634 var tStr
= pSet
[ pInx
];
635 // check for empty param
637 tObj
.param
[ pInx
] = '';
640 for ( var b
= 0 ; b
< tStr
.length
; b
++ ) {
641 if ( tStr
[b
] == '=' && b
> 0 && b
< tStr
.length
&& tStr
[b
- 1] != '\\' ) {
643 tObj
.param
[ tStr
.split( '=' ).shift() ] = tStr
.split( '=' ).pop();
646 tObj
.param
[ pInx
] = tStr
;
653 function getMagicTxtFromTempNode( node
) {
654 node
.tObj
= parseTmplTxt ( node
.t
);
655 // do magic swap if template key found in pMagicSet
656 if ( node
.tObj
.name
in pMagicSet
) {
657 var nt
= pMagicSet
[ node
.tObj
.name
]( node
.tObj
);
660 // don't swap just return text
667 * go last child first swap upward: (could probably be integrated above somehow)
670 function recurse_magic_swap( node
) {
675 // swap all the kids:
676 for ( var i
in node
['c'] ) {
677 var nt
= recurse_magic_swap( node
['c'][i
] );
678 // swap it into current
680 node
.t
= node
.t
.replace( node
['c'][i
].t
, nt
);
683 pNode
.t
= pNode
.t
.replace( node
['c'][i
].t
, nt
);
685 // do the current node:
686 var nt
= getMagicTxtFromTempNode( node
);
687 pNode
.t
= pNode
.t
.replace( node
.t
, nt
);
688 // run the swap for the outer most node
691 // node.t = getMagicFromTempObj( node.t )
692 return getMagicTxtFromTempNode( node
);
695 // parse out the template node structure:
696 this.pNode
= rdpp ( this.wikiText
);
697 // strip out the parent from the root
698 this.pNode
['p'] = null;
700 // do the recursive magic swap text:
701 this.pOut
= recurse_magic_swap( this.pNode
);
705 * parsed template api ~loosely based off of ~POM~
706 * http://www.mediawiki.org/wiki/Extension:Page_Object_Model
712 * gets a requested template from the wikitext (if available)
715 templates: function( tname
) {
717 var tmplSet
= new Array();
718 function getMatchingTmpl( node
) {
720 for ( var i
in node
['c'] ) {
721 getMatchingTmpl( node
['c'] );
724 if ( tname
&& node
.tObj
) {
725 if ( node
.tObj
['name'] == tname
)
726 tmplSet
.push( node
.tObj
);
727 } else if ( node
.tObj
) {
728 tmplSet
.push( node
.tObj
);
731 getMatchingTmpl( this.pNode
);
735 * Returns the transformed wikitext
737 * Build output from swapable index
738 * (all transforms must be expanded in parse stage and linearly rebuilt)
739 * Alternatively we could build output using a place-holder & replace system
740 * (this lets us be slightly more sloppy with ordering and indexes, but probably slower)
742 * Ideal: we build a 'wiki DOM'
743 * When editing you update the data structure directly
744 * Then in output time you just go DOM->html-ish output without re-parsing anything
746 getHTML : function() {
747 // wikiText updates should invalidate pOut
748 if ( this.pOut
== '' ) {
754 // return the parserObj
755 return new parseObj( wikiText
, opt
) ;
759 * API and request functions
761 $.getLocalApiUrl = function() {
762 if ( typeof wgServer
!= 'undefined' && typeof wgScriptPath
!= 'undefined' ) {
763 return wgServer
+ wgScriptPath
+ '/api.php';
775 * (c) Steven Levithan <stevenlevithan.com>
778 $.parseUri = function (str
) {
779 var o
= $.parseUri
.options
,
780 m
= o
.parser
[o
.strictMode
? "strict" : "loose"].exec(str
),
784 while (i
--) uri
[o
.key
[i
]] = m
[i
] || "";
787 uri
[o
.key
[12]].replace(o
.q
.parser
, function ($0, $1, $2) {
788 if ($1) uri
[o
.q
.name
][$1] = $2;
793 $.parseUri
.options
= {
795 key
: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
798 parser
: /(?:^|&)([^&=]*)=?([^&]*)/g
801 strict
: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
802 loose
: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
807 * getAbsoluteUrl takes a src and returns the absolute location given the document.URL
808 * @param {String} src path or url
810 $.absoluteUrl = function( src
, contextUrl
){
811 var pSrc
= mw
.parseUri( src
);
812 if( pSrc
.protocol
!= '')
815 // Get parent Url location the context URL
817 var pUrl
= mw
.parseUri( contextUrl
);
819 var pUrl
= mw
.parseUri( document
.URL
);
821 // If a leading slash:
822 if( src
.indexOf( '/' ) == 1 ){
823 return pUrl
.protocol
+ '://' + pUrl
.authority
+ src
;
825 return pUrl
.protocol
+ '://' + pUrl
.authority
+ pUrl
.directory
+ src
;
829 * Takes in a string returns an xml dom object
831 $.parseXML = function ( str
){
832 if ( $j
.browser
.msie
) {
833 // Attempt to parse as XML for IE
834 var xmldata
= new ActiveXObject( "Microsoft.XMLDOM" );
835 xmldata
.async
= "false";
837 xmldata
.loadXML( str
);
840 js_log( 'XML parse ERROR: ' + e
.message
);
845 // For others (Firefox, Safari etc, older browsers
846 // Some don't have native DOMParser either fallback defined bellow.
848 var xmldata
= ( new DOMParser() ).parseFromString( str
, "text/xml" );
850 js_log( 'XML parse ERROR: ' + e
.message
);
857 // Get the mv_embed location if it has not been set
858 if ( !mv_embed_path
) {
859 var mv_embed_path
= getMvEmbedPath();
862 // load in js2 stopgap into proper location:
863 if ( typeof gMsg
!= 'undefined' ) {
864 mw
.lang
.loadGM( gMsg
)
867 // setup legacy global shortcuts:
868 var loadGM
= mw
.lang
.loadGM
;
869 var loadRS
= mw
.lang
.loadRS
;
872 // All default messages in [English] should be overwritten by the CMS language message system.
874 "mwe-loading_txt" : "Loading ...",
875 "mwe-size-gigabytes" : "$1 GB",
876 "mwe-size-megabytes" : "$1 MB",
877 "mwe-size-kilobytes" : "$1 K",
878 "mwe-size-bytes" : "$1 B",
879 "mwe-error_load_lib" : "Error: JavaScript $1 was not retrievable or does not define $2",
880 "mwe-loading-add-media-wiz" : "Loading add media wizard",
881 "mwe-apiproxy-setup" : "Setting up API proxy",
882 "mwe-load-drag-item" : "Loading dragged item",
884 "mwe-cancel" : "Cancel"
888 // Get the loading image
889 function mv_get_loading_img( style
, class_attr
) {
890 var style_txt
= ( style
) ? style
:'';
891 var class_attr
= ( class_attr
) ? 'class="' + class_attr
+ '"' : 'class="mv_loading_img"';
892 return '<div ' + class_attr
+ ' style="' + style
+ '"></div>';
895 function mv_set_loading( target
, load_id
) {
896 var id_attr
= ( load_id
) ? ' id="' + load_id
+ '" ':'';
897 $j( target
).append( '<div ' + id_attr
+ ' style="position:absolute;top:0px;left:0px;height:100%;width:100%;' +
898 'background-color:#FFF;">' +
899 mv_get_loading_img( 'top:30px;left:30px' ) +
904 * mvJsLoader class handles initialization and js file loads
911 onReadyEvents
: new Array(),
912 doneReadyEvents
: false,
913 jQuerySetupFlag
: false,
915 // To keep consistency across threads
919 load_error
: false, // Load error flag (false by default)
921 callbacks
: new Array(),
924 doLoad: function( loadLibs
, callback
) {
926 if ( loadLibs
&& loadLibs
.length
!= 0 ) {
927 // js_log("doLoad setup::" + JSON.stringify( loadLibs ) );
929 // First check if we already have this library loaded
930 var all_libs_loaded
= true;
931 for ( var i
= 0; i
< loadLibs
.length
; i
++ ) {
932 // Check if the library is already loaded
933 if ( ! this.checkObjPath( loadLibs
[i
] ) ) {
934 all_libs_loaded
= false;
937 if ( all_libs_loaded
) {
938 js_log( 'Libraries ( ' + loadLibs
+ ') already loaded... skipping load request' );
943 // Do a check for any CSS we may need and get it
944 for ( var i
= 0; i
< loadLibs
.length
; i
++ ) {
945 if ( typeof mvCssPaths
[ loadLibs
[i
] ] != 'undefined' ) {
946 loadExternalCss( mv_embed_path
+ mvCssPaths
[ loadLibs
[i
] ] );
950 // Check if we should use the script loader to combine all the requests into one
951 // ( the scriptloader defines the mwSlScript global )
952 if ( typeof mwSlScript
!= 'undefined' ) {
956 for ( var i
= 0; i
< loadLibs
.length
; i
++ ) {
957 var curLib
= loadLibs
[i
];
958 // Only add if not included yet:
959 if ( ! this.checkObjPath( curLib
) ) {
960 class_set
+= coma
+ curLib
;
965 // Build the url to the scriptServer striping its request parameters:
966 var puri
= mw
.parseUri( getMvEmbedURL() );
967 if ( ( getMvEmbedURL().indexOf( '://' ) != -1 )
968 && puri
.host
!= mw
.parseUri( document
.URL
).host
)
970 var scriptPath
= puri
.protocol
+ '://' + puri
.authority
+ puri
.path
;
972 var scriptPath
= puri
.path
;
974 // js_log('scriptServer Path is: ' + scriptPath + "\n host script path:" + getMvEmbedURL() );
975 this.libs
[ last_class
] = scriptPath
+ '?class=' + class_set
+
976 '&' + getMwReqParam();
980 for ( var i
= 0; i
< loadLibs
.length
; i
++ ) {
981 var curLib
= loadLibs
[i
];
983 var libLoc
= mvGetClassPath( curLib
);
984 // Do a direct load of the file (pass along unique request id from
985 // request or mv_embed Version )
986 var qmark
= ( libLoc
.indexOf( '?' ) !== true ) ? '?' : '&';
987 this.libs
[curLib
] = mv_embed_path
+ libLoc
+ qmark
+ getMwReqParam();
994 this.callbacks
.push( callback
);
996 if ( this.checkLoading() ) {
997 // @@todo we should check the <script> Element .onLoad property to
998 // make sure its just not a very slow connection
999 // (even though the class is not loaded)
1000 if ( this.load_time
++ > 4000 ) { // Time out after ~80 seconds
1001 js_log( gM( 'mwe-error_load_lib', [mvGetClassPath( this.missing_path
), this.missing_path
] ) );
1002 this.load_error
= true;
1004 setTimeout( 'mvJsLoader.doLoad()', 20 );
1007 // js_log('checkLoading passed. Running callbacks...');
1008 // Only do callbacks if we are in the same instance (weird concurrency issue)
1010 for ( var i
= 0; i
< this.callbacks
.length
; i
++ )
1012 // js_log('RESET LIBS: loading is: '+ loading + ' callback count: '+cb_count +
1013 // ' p:'+ this.ptime +' c:'+ this.ctime);
1017 // js_log('done loading, do call: ' + this.callbacks[0] );
1018 while ( this.callbacks
.length
!= 0 ) {
1019 if ( this.ptime
== this.ctime
- 1 ) { // Enforce thread consistency
1020 this.callbacks
.pop()();
1021 // func = this.callbacks.pop();
1022 // js_log(' run: '+this.ctime+ ' p: ' + this.ptime + ' ' +loading+ ' :'+ func);
1025 // Re-issue doLoad ( ptime will be set to ctime so we should catch up)
1026 setTimeout( 'mvJsLoader.doLoad()', 25 );
1031 this.ptime
= this.ctime
;
1033 doLoadDepMode: function( loadChain
, callback
) {
1034 // Firefox executes JS in the order in which it is included, so just directly issue the request
1035 if ( $j
.browser
.firefox
) {
1037 for ( var i
= 0; i
< loadChain
.length
; i
++ ) {
1038 for ( var j
= 0; j
< loadChain
[i
].length
; j
++ ) {
1039 loadSet
.push( loadChain
[i
][j
] );
1042 mvJsLoader
.doLoad( loadSet
, callback
);
1044 // Safari and IE tend to execute out of order so load with dependency checks
1045 mvJsLoader
.doLoad( loadChain
.shift(), function() {
1046 if ( loadChain
.length
!= 0 ) {
1047 mvJsLoader
.doLoadDepMode( loadChain
, callback
);
1054 checkLoading: function() {
1057 for ( var i
in this.libs
) { // for/in loop is OK on an object
1058 if ( !this.checkObjPath( i
) ) {
1059 if ( !this.libreq
[i
] ) {
1060 loadExternalJs( this.libs
[i
] );
1063 // js_log("has not yet loaded: " + i);
1069 checkObjPath: function( libVar
) {
1072 var objPath
= libVar
.split( '.' )
1074 for ( var p
= 0; p
< objPath
.length
; p
++ ) {
1075 cur_path
= ( cur_path
== '' ) ? cur_path
+ objPath
[p
] : cur_path
+ '.' + objPath
[p
];
1076 eval( 'var ptest = typeof ( ' + cur_path
+ ' ); ' );
1077 if ( ptest
== 'undefined' ) {
1078 this.missing_path
= cur_path
;
1082 this.cur_path
= cur_path
;
1086 * checks for jQuery and adds the $j noConflict var
1088 jQueryCheck: function( callback
) {
1089 // js_log( 'jQueryCheck::' + this.jQuerySetupFlag);
1091 if ( _global
['$j'] && _this
.jQuerySetupFlag
) {
1092 callback(); // call the callback now
1098 // only do the $j setup once:
1099 if ( !_global
['$j'] ) {
1100 _global
['$j'] = jQuery
.noConflict();
1102 if ( _this
.jQuerySetupFlag
== false ) {
1103 // js_log('setup mv_embed jQuery bindings');
1104 // Setup our global settings using the (jQuery helper)
1106 // Set up the skin path
1107 _global
['mv_jquery_skin_path'] = mv_embed_path
+ 'jquery/jquery.ui/themes/' + mw
.conf
['jui_skin'] + '/';
1108 _global
['mv_skin_img_path'] = mv_embed_path
+ 'skins/' + mw
.conf
['skin_name'] + '/images/';
1109 _global
['mv_default_thumb_url'] = mv_skin_img_path
+ 'vid_default_thumb.jpg';
1111 // Make sure the skin/style sheets are always available:
1112 loadExternalCss( mv_jquery_skin_path
+ 'jquery-ui-1.7.1.custom.css' );
1113 loadExternalCss( mv_embed_path
+ 'skins/' + mw
.conf
['skin_name'] + '/styles.css' );
1115 // Set up AJAX to not send dynamic URLs for loading scripts (we control that with
1116 // the scriptLoader)
1121 js_log( 'jQuery loaded into $j' );
1122 // Set up mvEmbed jQuery bindings and config based dependencies
1123 mv_jqueryBindings();
1124 _this
.jQuerySetupFlag
= true;
1126 // js_log('should run callback: ' + callback);
1127 // Run the callback if not already run above
1134 embedVideoCheck:function( callback
) {
1136 js_log( 'embedVideoCheck:' );
1137 // Make sure we have jQuery
1138 _this
.jQueryCheck( function() {
1139 // set class videonojs to loading
1140 $j( '.videonojs' ).html( gM( 'mwe-loading_txt' ) );
1141 // Set up the embed video player class request: (include the skin js as well)
1154 // add any requested skins (supports multiple skins per single page)
1155 if ( mw
.skin_list
) {
1156 for ( var i
in mw
.skin_list
) {
1157 depReq
[0].push( mw
.skin_list
[i
] + 'Config' );
1161 // Add PNG fix if needed:
1162 if ( $j
.browser
.msie
|| $j
.browser
.version
< 7 )
1163 depReq
[0].push( '$j.fn.pngFix' );
1165 // load the video libs:
1166 _this
.doLoadDepMode( depReq
, function() {
1169 $j( '.videonojs' ).remove();
1173 addLoadEvent: function( fn
) {
1174 // js_log('add ready event: ' + fn );
1175 this.onReadyEvents
.push( fn
);
1177 // Check the jQuery flag. This way, when remote embedding, we don't load jQuery
1178 // unless js2AddOnloadHook was used or there is video on the page.
1179 runQueuedFunctions: function() {
1180 js_log( "runQueuedFunctions" );
1182 this.jQueryCheck( function() {
1183 _this
.runReadyEvents();
1184 _this
.doneReadyEvents
= true;
1187 runReadyEvents: function() {
1188 js_log( "runReadyEvents" + this.onReadyEvents
.length
);
1189 while ( this.onReadyEvents
.length
) {
1190 var func
= this.onReadyEvents
.shift();
1191 // js_log('run onReady:: ' + func );
1197 // Shortcut ( @@todo consolidate shortcuts & re-factor mvJsLoader )
1198 function mwLoad( loadSet
, callback
) {
1199 mvJsLoader
.doLoad( loadSet
, callback
);
1204 // Load an external JS file. Similar to jquery .require plugin,
1205 // but checks for object availability rather than load state.
1207 /*********** INITIALIZATION CODE *************
1208 * This will get called when the DOM is ready
1209 *********************************************/
1210 /* jQuery .ready does not work when jQuery is loaded dynamically.
1211 * For an example of the problem see: 1.1.3 working: http://pastie.caboo.se/92588
1212 * and >= 1.1.4 not working: http://pastie.caboo.se/92595
1213 * $j(document).ready( function(){ */
1214 function mwdomReady( force
) {
1215 js_log( 'f:mwdomReady:' );
1216 if ( !force
&& mw
.init_done
) {
1217 js_log( "mw done, do nothing..." );
1220 mw
.init_done
= true;
1221 // Handle the execution of queued functions with jQuery "ready"
1223 // Check if this page has a video, audio or playlist tag
1225 document
.getElementsByTagName( "video" ),
1226 document
.getElementsByTagName( "audio" ),
1227 document
.getElementsByTagName( "playlist" )
1229 if ( e
[0].length
!= 0 || e
[1].length
!= 0 || e
[2].length
!= 0 ) {
1230 // look for any skin classes we have to load:
1231 for ( var j
in e
) {
1232 for ( var k
in e
[j
] ) {
1233 if ( e
[j
][k
] && typeof( e
[j
][k
] ) == 'object' ) {
1234 var sn
= e
[j
][k
].getAttribute( 'class' );
1235 // Try "className" for good old IE
1237 var sn
= e
[j
][k
].getAttribute( 'className' );
1239 if ( sn
&& sn
!= '' ) {
1240 for ( var n
= 0; n
< mw
.valid_skins
.length
; n
++ ) {
1241 if ( sn
.indexOf( mw
.valid_skins
[n
] ) !== -1 ) {
1242 mw
.skin_list
.push( mw
.valid_skins
[n
] );
1249 // Load libs and process videos
1250 mvJsLoader
.embedVideoCheck( function() {
1251 // Run any queued global events:
1252 mv_video_embed( function() {
1253 mvJsLoader
.runQueuedFunctions();
1257 mvJsLoader
.runQueuedFunctions();
1261 // js2AddOnloadHook: ensure jQuery and the DOM are ready
1262 function js2AddOnloadHook( func
) {
1263 // js_log('js2AddOnloadHook:: jquery:' +func);
1264 // If we are ready run directly else add load event:
1265 if ( mvJsLoader
.doneReadyEvents
) {
1266 //js_log( 'run queued event: ' + func );
1269 //js_log( 'add to load event: ' + func );
1270 mvJsLoader
.addLoadEvent( func
);
1273 // Deprecated mwAddOnloadHook in favour of js2 naming (for clear separation of js2 code from old MW code
1274 var mwAddOnloadHook
= js2AddOnloadHook
;
1276 * This function allows for targeted rewriting
1278 function rewrite_by_id( vid_id
, ready_callback
) {
1279 js_log( 'f:rewrite_by_id: ' + vid_id
);
1280 // Force a re-check of the DOM for playlist or video elements:
1281 mvJsLoader
.embedVideoCheck( function() {
1282 mv_video_embed( ready_callback
, vid_id
);
1287 /*********** INITIALIZATION CODE *************
1288 * set DOM-ready callback to init_mv_embed
1289 *********************************************/
1290 // for Mozilla / modern browsers
1291 if ( document
.addEventListener
) {
1292 document
.addEventListener( "DOMContentLoaded", mwdomReady
, false );
1295 if ( window
.onload
) {
1296 temp_f
= window
.onload
;
1298 // Use the onload method as a backup
1299 window
.onload = function () {
1306 * Store all the mwEmbed jQuery-specific bindings
1307 * (set up after jQuery is available).
1309 * These functions are generally are loaders that do the dynamic mapping of
1310 * dependencies for a given component
1314 function mv_jqueryBindings() {
1315 js_log( 'mv_jqueryBindings' );
1318 * dragDrop file loader
1320 $.fn
.dragFileUpload = function ( conf
) {
1321 if ( this.selector
) {
1323 // load the dragger and "setup"
1324 mw
.load( ['$j.fn.dragDropFile'], function() {
1325 $j( _this
.selector
).dragDropFile();
1330 * apiProxy Loader loader:
1332 * @param mode is either 'server' or 'client'
1334 $.apiProxy = function( mode
, pConf
, callback
) {
1335 js_log( 'do apiProxy setup' );
1336 mvJsLoader
.doLoad( [
1340 // do the proxy setup or
1341 if ( mode
== 'client' ) {
1342 // just do the setup (no callbcak for client setup)
1343 mw
.proxy
.client( pConf
);
1346 } else if ( mode
== 'server' ) {
1347 // do the request with the callback
1348 mw
.proxy
.server( pConf
, callback
);
1353 // non selector based add-media-wizard direct invocation with loader
1354 $.addMediaWiz = function( iObj
, callback
) {
1355 js_log( ".addMediaWiz call" );
1356 // check if already loaded:
1357 if ( _global
['rsdMVRS'] ) {
1358 _global
['rsdMVRS'].doReDisplay();
1360 callback( _global
['rsdMVRS'] );
1363 // display a loader:
1364 $.addLoaderDialog( gM( 'mwe-loading-add-media-wiz' ) );
1365 // load the addMedia wizard without a target:
1366 $.fn
.addMediaWiz ( iObj
, function( amwObj
) {
1368 $.closeLoaderDialog();
1369 // do the add-media-wizard display
1370 amwObj
.doInitDisplay();
1371 // call the parent callback:
1373 callback( _global
['rsdMVRS'] );
1376 $.fn
.addMediaWiz = function( iObj
, callback
) {
1377 if ( this.selector
) {
1378 // First set the cursor for the button to "loading"
1379 $j( this.selector
).css( 'cursor', 'wait' ).attr( 'title', gM( 'mwe-loading_txt' ) );
1381 iObj
['target_invocation'] = this.selector
;
1384 // Load the mv_embed_base skin:
1385 loadExternalCss( mv_jquery_skin_path
+ 'jquery-ui-1.7.1.custom.css' );
1386 loadExternalCss( mv_embed_path
+ 'skins/' + mw
.conf
['skin_name'] + '/styles.css' );
1387 // Load all the required libs:
1388 mvJsLoader
.jQueryCheck( function() {
1389 // Load with staged dependencies (for IE that does not execute in order)
1390 mvJsLoader
.doLoadDepMode( [
1391 [ 'remoteSearchDriver',
1393 '$j.fn.textSelection',
1403 iObj
['instance_name'] = 'rsdMVRS';
1404 if ( ! _global
['rsdMVRS'] )
1405 _global
['rsdMVRS'] = new remoteSearchDriver( iObj
);
1407 callback( _global
['rsdMVRS'] );
1415 $.fn
.sequencer = function( iObj
, callback
) {
1417 iObj
['target_sequence_container'] = this.selector
;
1418 // Issue a request to get the CSS file (if not already included):
1419 loadExternalCss( mv_jquery_skin_path
+ 'jquery-ui-1.7.1.custom.css' );
1420 loadExternalCss( mv_embed_path
+ 'skins/' + mw
.conf
['skin_name'] + '/mv_sequence.css' );
1421 // Make sure we have the required mv_embed libs (they are not loaded when no video
1422 // element is on the page)
1423 mvJsLoader
.embedVideoCheck( function() {
1424 // Load the playlist object and then the jQuery UI stuff:
1425 mvJsLoader
.doLoadDepMode( [
1438 '$j.ui.progressbar',
1445 js_log( 'calling new mvSequencer' );
1446 // Initialise the sequence object (it will take over from there)
1447 // No more than one mvSeq obj for now:
1448 if ( !_global
['mvSeq'] ) {
1449 _global
['mvSeq'] = new mvSequencer( iObj
);
1451 js_log( 'mvSeq already init' );
1457 * The Firefogg jQuery function:
1458 * @@note This Firefogg invocation could be made to work more like real jQuery plugins
1460 var queuedFirefoggConf
= { };
1461 $.fn
.firefogg = function( iObj
, callback
) {
1464 // Add the base theme CSS:
1465 loadExternalCss( mv_jquery_skin_path
+ 'jquery-ui-1.7.1.custom.css' );
1466 loadExternalCss( mv_embed_path
+ 'skins/' + mw
.conf
['skin_name'] + '/styles.css' );
1468 // Check if we already have Firefogg loaded (the call just updates the element's
1470 var sElm
= $j( this.selector
).get( 0 );
1471 if ( sElm
['firefogg'] ) {
1472 if ( sElm
['firefogg'] == 'loading' ) {
1473 js_log( "Queued firefogg operations ( firefogg " +
1474 "not done loading ) " );
1475 $j
.extend( queuedFirefoggConf
, iObj
);
1478 // Update properties
1479 for ( var i
in iObj
) {
1480 js_log( "firefogg::updated: " + i
+ ' to ' + iObj
[i
] );
1481 sElm
['firefogg'][i
] = iObj
[i
];
1483 return sElm
['firefogg'];
1485 // Avoid concurrency
1486 sElm
['firefogg'] = 'loading';
1489 iObj
['selector'] = this.selector
;
1493 'mvBaseUploadInterface',
1498 '$j.ui.progressbar',
1503 if ( iObj
.encoder_interface
) {
1512 // Make sure we have everything loaded that we need:
1513 mvJsLoader
.doLoadDepMode( loadSet
, function() {
1514 js_log( 'firefogg libs loaded. target select:' + iObj
.selector
);
1515 // Select interface provider based on whether we want to include the
1516 // encoder interface or not
1517 if ( iObj
.encoder_interface
) {
1518 var myFogg
= new mvAdvFirefogg( iObj
);
1520 var myFogg
= new mvFirefogg( iObj
);
1523 myFogg
.doRewrite( callback
);
1524 var selectorElement
= $j( iObj
.selector
).get( 0 );
1525 selectorElement
['firefogg'] = myFogg
;
1527 js_log( 'pre:' + selectorElement
['firefogg']['firefogg_form_action'] )
1528 if ( queuedFirefoggConf
)
1529 $j
.extend( selectorElement
['firefogg'], queuedFirefoggConf
);
1530 js_log( 'post:' + selectorElement
['firefogg']['firefogg_form_action'] )
1534 // Take an input player as the selector and expose basic rendering controls
1535 $.fn
.firefoggRender = function( iObj
, callback
) {
1536 // Check if we already have render loaded then just pass on updates/actions
1537 var sElm
= $j( this.selector
).get( 0 );
1538 //add a special attribute to the selector:
1539 if ( sElm
['fogg_render'] ) {
1540 if ( sElm
['fogg_render'] == 'loading' ) {
1541 js_log( "Error: called firefoggRender while loading" );
1544 // Call or update the property:
1546 sElm
['fogg_render'] = 'loading';
1548 iObj
['player_target'] = this.selector
;
1549 mvJsLoader
.doLoad( [
1550 'mvBaseUploadInterface',
1554 // Attach the firefoggRender obj to the selected elm:
1555 sElm
['fogg_render'] = new mvFirefoggRender( iObj
);
1556 if ( callback
&& typeof callback
== 'function' )
1557 callback( sElm
['fogg_render'] );
1561 $.fn
.baseUploadInterface = function( iObj
) {
1562 mvJsLoader
.doLoadDepMode( [
1564 'mvBaseUploadInterface',
1568 '$j.ui.progressbar',
1572 myUp
= new mvBaseUploadInterface( iObj
);
1577 // Shortcut to a themed button
1578 $.btnHtml = function( msg
, className
, iconId
, opt
) {
1581 var href
= ( opt
.href
) ? opt
.href
: '#';
1582 var target_attr
= ( opt
.target
) ? ' target="' + opt
.target
+ '" ' : '';
1583 var style_attr
= ( opt
.style
) ? ' style="' + opt
.style
+ '" ' : '';
1584 return '<a href="' + href
+ '" ' + target_attr
+ style_attr
+
1585 ' class="ui-state-default ui-corner-all ui-icon_link ' +
1586 className
+ '"><span class="ui-icon ui-icon-' + iconId
+ '" ></span>' +
1587 '<span class="btnText">' + msg
+ '</span></a>';
1589 // Shortcut to bind hover state
1590 $.fn
.btnBind = function() {
1593 $j( this ).addClass( 'ui-state-hover' );
1596 $j( this ).removeClass( 'ui-state-hover' );
1602 * resize the dialog to fit the window
1604 $.fn
.dialogFitWindow = function( opt
) {
1605 var opt_default
= { 'hspace':50, 'vspace':50 };
1608 $j
.extend( opt
, opt_default
);
1609 $j( this.selector
).dialog( 'option', 'width', $j( window
).width() - opt
.hspace
);
1610 $j( this.selector
).dialog( 'option', 'height', $j( window
).height() - opt
.vspace
);
1611 $j( this.selector
).dialog( 'option', 'position', 'center' );
1612 // update the child position: (some of this should be pushed up-stream via dialog config options
1613 $j( this.selector
+ '~ .ui-dialog-buttonpane' ).css( {
1614 'position':'absolute',
1623 * small helper for putting a loading dialog box on top of everything
1624 * (helps block for request that
1626 * @param msg text text of the loader msg
1628 $.addLoaderDialog = function( msg_txt
) {
1629 $.addDialog( msg_txt
, msg_txt
+ '<br>' + mv_get_loading_img() );
1632 $.addDialog = function ( title
, msg_txt
, btn
) {
1633 $( '#mwe_tmp_loader' ).remove();
1634 // append the style free loader ontop:
1635 $( 'body' ).append( '<div id="mwe_tmp_loader" style="display:none" title="' + title
+ '" >' +
1638 // special btn == ok gives empty give a single "oky" -> "close"
1639 if ( btn
== 'ok' ) {
1640 btn
[ gM( 'mwe-ok' ) ] = function() {
1641 $j( '#mwe_tmp_loader' ).close();
1644 // turn the loader into a real dialog loader:
1645 mvJsLoader
.doLoadDepMode( [
1653 $( '#mwe_tmp_loader' ).dialog( {
1663 $.closeLoaderDialog = function() {
1664 mvJsLoader
.doLoadDepMode( [
1672 $j( '#mwe_tmp_loader' ).dialog( 'destroy' ).remove();
1676 $.mwProxy = function( apiConf
) {
1677 mvJsLoader
.doLoad( ['mw.apiProxy'],
1679 mw
.apiProxy( apiConf
);
1685 * Utility functions:
1687 // Simple URL rewriter (could probably be refactored into an inline regular exp)
1688 function getURLParamReplace( url
, opt
) {
1689 var pSrc
= mw
.parseUri( url
);
1690 if ( pSrc
.protocol
!= '' ) {
1691 var new_url
= pSrc
.protocol
+ '://' + pSrc
.authority
+ pSrc
.path
+ '?';
1693 var new_url
= pSrc
.path
+ '?';
1696 for ( var key
in pSrc
.queryKey
) {
1697 var val
= pSrc
.queryKey
[ key
];
1698 // Do override if requested
1701 new_url
+= amp
+ key
+ '=' + val
;
1704 // Add any vars that were not already there:
1705 for ( var i
in opt
) {
1706 if ( !pSrc
.queryKey
[i
] ) {
1707 new_url
+= amp
+ i
+ '=' + opt
[i
];
1714 * Given a float number of seconds, returns npt format response.
1716 * @param float Seconds
1717 * @param boolean If we should show milliseconds or not.
1719 function seconds2npt( sec
, show_ms
) {
1720 if ( isNaN( sec
) ) {
1721 // js_log("warning: trying to get npt time on NaN:" + sec);
1724 var hours
= Math
.floor( sec
/ 3600 );
1725 var minutes
= Math
.floor( ( sec
/ 60 ) % 60 );
1726 var seconds
= sec
% 60;
1727 // Round the number of seconds to the required number of significant digits
1729 seconds
= Math
.round( seconds
* 1000 ) / 1000;
1731 seconds
= Math
.round( seconds
);
1734 seconds
= '0' + seconds
;
1736 minutes
= '0' + minutes
;
1738 return hours
+ ":" + minutes
+ ":" + seconds
;
1741 * Take hh:mm:ss,ms or hh:mm:ss.ms input, return the number of seconds
1743 function npt2seconds( npt_str
) {
1745 // js_log('npt2seconds:not valid ntp:'+ntp);
1748 // Strip {npt:}01:02:20 or 32{s} from time if present
1749 npt_str
= npt_str
.replace( /npt:|s/g, '' );
1755 times
= npt_str
.split( ':' );
1756 if ( times
.length
== 3 ) {
1760 } else if ( times
.length
== 2 ) {
1766 // Sometimes a comma is used instead of period for ms
1767 sec
= sec
.replace( /,\s?/, '.' );
1768 // Return seconds float
1769 return parseInt( hour
* 3600 ) + parseInt( min
* 60 ) + parseFloat( sec
);
1772 * Simple helper to grab an edit token
1774 * @param title The wiki page title you want to edit
1775 * @param api_url 'optional' The target API URL
1776 * @param callback The callback function to pass the token to
1778 function get_mw_token( title
, api_url
, callback
) {
1779 js_log( ':get_mw_token:' );
1780 if ( !title
&& wgUserName
) {
1781 title
= 'User:' + wgUserName
;
1792 }, function( data
) {
1793 for ( var i
in data
.query
.pages
) {
1794 if ( data
.query
.pages
[i
]['edittoken'] ) {
1795 if ( typeof callback
== 'function' )
1796 callback ( data
.query
.pages
[i
]['edittoken'] );
1804 // Do a remote or local API request based on request URL
1805 // @param options: url, data, cbParam, callback
1806 function do_api_req( options
, callback
) {
1807 if ( typeof options
.data
!= 'object' ) {
1808 return js_error( 'Error: request paramaters must be an object' );
1810 // Generate the URL if it's missing
1811 if ( typeof options
.url
== 'undefined' || !options
.url
) {
1812 if ( typeof wgServer
== 'undefined' ) {
1813 return js_error( 'Error: no api url for api request' );
1815 options
.url
= mw
.getLocalApiUrl();
1817 if ( typeof options
.data
== 'undefined' )
1820 // Force format to JSON
1821 options
.data
['format'] = 'json';
1823 // If action is not set, assume query
1824 if ( ! options
.data
['action'] )
1825 options
.data
['action'] = 'query';
1827 // js_log('do api req: ' + options.url +'?' + jQuery.param(options.data) );
1828 if ( options
.url
== 'proxy' && mw
.proxy
) {
1829 // assume the proxy is already "setup" since mw.proxy is defined.
1830 // @@todo should probably integrate that setup into the api call
1831 mw
.proxy
.doRequest( options
.data
, callback
);
1832 } else if ( mw
.parseUri( document
.URL
).host
== mw
.parseUri( options
.url
).host
) {
1833 // Local request: do API request directly
1838 dataType
: 'json', // API requests _should_ always return JSON data:
1840 success: function( data
) {
1843 error: function( e
) {
1844 js_error( ' error' + e
+ ' in getting: ' + options
.url
);
1849 // Set the callback param if it's not already set
1850 if ( typeof options
.jsonCB
== 'undefined' )
1851 options
.jsonCB
= 'callback';
1853 var req_url
= options
.url
;
1854 var paramAnd
= ( req_url
.indexOf( '?' ) == -1 ) ? '?' : '&';
1855 // Put all the parameters into the URL
1856 for ( var i
in options
.data
) {
1857 req_url
+= paramAnd
+ encodeURIComponent( i
) + '=' + encodeURIComponent( options
.data
[i
] );
1860 var fname
= 'mycpfn_' + ( mw
.cb_count
++ );
1861 _global
[ fname
] = callback
;
1862 req_url
+= '&' + options
.jsonCB
+ '=' + fname
;
1863 loadExternalJs( req_url
);
1867 // @@note this contains metavid specific local vs remote api remapping.
1868 // this should be depreciated and we should use "$j.get" or an explicate api call
1869 // (we should not mix the two request types)
1870 function do_request( req_url
, callback
) {
1871 js_log( 'do_request::req_url:' + mw
.parseUri( document
.URL
) + ' != ' + mw
.parseUri( req_url
).host
);
1872 // If we are doing a request to the same domain or relative link, do a normal GET
1873 if ( mw
.parseUri( document
.URL
).host
== mw
.parseUri( req_url
).host
||
1874 req_url
.indexOf( '://' ) == -1 ){ // if its a relative url go directly as well
1875 // Do a direct request
1880 success: function( data
) {
1885 // Get data via DOM injection with callback
1886 global_req_cb
.push( callback
);
1887 // Prepend json_ to feed_format if not already requesting json format (metavid specific)
1888 if ( req_url
.indexOf( "feed_format=" ) != -1 && req_url
.indexOf( "feed_format=json" ) == -1 )
1889 req_url
= req_url
.replace( /feed_format=/, 'feed_format=json_' );
1890 loadExternalJs( req_url
+ '&cb=mv_jsdata_cb&cb_inx=' + ( global_req_cb
.length
-1 ) );
1894 function mv_jsdata_cb( response
) {
1895 js_log( 'f:mv_jsdata_cb:' + response
['cb_inx'] );
1896 // Run the callback from the global request callback object
1897 if ( !global_req_cb
[response
['cb_inx']] ) {
1898 js_log( 'missing req cb index' );
1901 if ( !response
['pay_load'] ) {
1902 js_log( "missing pay load" );
1905 switch( response
['content-type'] ) {
1909 if ( typeof response
['pay_load'] == 'string' ) {
1910 response
['pay_load'] = mw
.parseXML( response
['pay_load'] );
1914 js_log( 'bad response type' + response
['content-type'] );
1918 global_req_cb
[response
['cb_inx']]( response
['pay_load'] );
1920 // Load external JS via DOM injection
1921 function loadExternalJs( url
, callback
) {
1922 js_log( 'load js: ' + url
);
1923 // if(window['$j']) // use jquery call:
1931 var e
= document
.createElement( "script" );
1932 e
.setAttribute( 'src', url
);
1933 e
.setAttribute( 'type', "text/javascript" );
1935 e.onload = callback;
1937 // e.setAttribute('defer', true);
1938 document
.getElementsByTagName( "head" )[0].appendChild( e
);
1941 function styleSheetPresent( url
) {
1942 style_elements
= document
.getElementsByTagName( 'link' );
1943 if ( style_elements
.length
> 0 ) {
1944 for ( i
= 0; i
< style_elements
.length
; i
++ ) {
1945 if ( style_elements
[i
].href
== url
)
1951 function loadExternalCss( url
) {
1952 // We could have the script loader group these CSS requests.
1953 // But it's debatable: it may hurt more than it helps with caching and all
1954 if ( typeof url
== 'object' ) {
1955 for ( var i
in url
) {
1956 loadExternalCss( url
[i
] );
1961 if ( url
.indexOf( '?' ) == -1 ) {
1962 url
+= '?' + getMwReqParam();
1964 if ( !styleSheetPresent( url
) ) {
1965 js_log( 'load css: ' + url
);
1966 var e
= document
.createElement( "link" );
1968 e
.type
= "text/css";
1969 e
.rel
= 'stylesheet';
1970 document
.getElementsByTagName( "head" )[0].appendChild( e
);
1973 function getMvEmbedURL() {
1974 if ( _global
['mv_embed_url'] )
1975 return _global
['mv_embed_url'];
1976 var js_elements
= document
.getElementsByTagName( "script" );
1977 for ( var i
= 0; i
< js_elements
.length
; i
++ ) {
1978 // Check for mv_embed.js and/or script loader
1979 var src
= js_elements
[i
].getAttribute( "src" );
1981 if ( src
.indexOf( 'mv_embed.js' ) != -1 || (
1982 ( src
.indexOf( 'mwScriptLoader.php' ) != -1 || src
.indexOf( 'jsScriptLoader.php' ) != -1 )
1983 && src
.indexOf( 'mv_embed' ) != -1 ) ) // (check for class=mv_embed script_loader call)
1985 _global
['mv_embed_url'] = src
;
1990 js_error( 'Error: getMvEmbedURL failed to get Embed Path' );
1993 // Get a unique request ID to ensure fresh JavaScript
1994 function getMwReqParam() {
1995 if ( _global
['req_param'] )
1996 return _global
['req_param'];
1997 var mv_embed_url
= getMvEmbedURL();
2001 // If we have a URI, add it to the req
2002 var urid
= mw
.parseUri( mv_embed_url
).queryKey
['urid']
2003 // If we're in debug mode, get a fresh unique request key and pass on "debug" param
2004 if ( mw
.parseUri( mv_embed_url
).queryKey
['debug'] == 'true' ) {
2006 req_param
+= 'urid=' + d
.getTime() + '&debug=true';
2007 } else if ( urid
) {
2008 // Set from request urid:
2009 req_param
+= 'urid=' + urid
;
2011 // Otherwise, just use the mv_embed version
2012 req_param
+= 'urid=' + mw
.version
;
2014 // add the lang param:
2015 var langKey
= mw
.parseUri( mv_embed_url
).queryKey
['uselang'];
2017 req_param
+= '&uselang=' + langKey
;
2019 _global
['req_param'] = req_param
;
2021 return _global
['req_param'];
2024 * Set the global mv_embed path based on the script's location
2026 function getMvEmbedPath() {
2027 if ( _global
['mv_embed_path'] )
2028 return _global
['mv_embed_path'];
2029 var mv_embed_url
= getMvEmbedURL();
2030 if ( mv_embed_url
.indexOf( 'mv_embed.js' ) !== -1 ) {
2031 mv_embed_path
= mv_embed_url
.substr( 0, mv_embed_url
.indexOf( 'mv_embed.js' ) );
2032 } else if ( mv_embed_url
.indexOf( 'mwScriptLoader.php' ) !== -1 ) {
2033 // Script loader is in the root of MediaWiki, so include the default mv_embed extension path
2034 mv_embed_path
= mv_embed_url
.substr( 0, mv_embed_url
.indexOf( 'mwScriptLoader.php' ) )
2035 + mediaWiki_mvEmbed_path
;
2037 mv_embed_path
= mv_embed_url
.substr( 0, mv_embed_url
.indexOf( 'jsScriptLoader.php' ) );
2039 // Make an absolute URL (if it's relative and we don't have an mv_embed path)
2040 if ( mv_embed_path
.indexOf( '://' ) == -1 ) {
2041 var pURL
= mw
.parseUri( document
.URL
);
2042 if ( mv_embed_path
.charAt( 0 ) == '/' ) {
2043 mv_embed_path
= pURL
.protocol
+ '://' + pURL
.authority
+ mv_embed_path
;
2046 if ( mv_embed_path
== '' ) {
2047 mv_embed_path
= pURL
.protocol
+ '://' + pURL
.authority
+ pURL
.directory
+ mv_embed_path
;
2051 _global
['mv_embed_path'] = mv_embed_path
;
2052 return mv_embed_path
;
2055 if ( typeof DOMParser
== "undefined" ) {
2056 DOMParser = function () { }
2057 DOMParser
.prototype.parseFromString = function ( str
, contentType
) {
2058 if ( typeof ActiveXObject
!= "undefined" ) {
2059 var d
= new ActiveXObject( "MSXML.DomDocument" );
2062 } else if ( typeof XMLHttpRequest
!= "undefined" ) {
2063 var req
= new XMLHttpRequest
;
2064 req
.open( "GET", "data:" + ( contentType
|| "application/xml" ) +
2065 ";charset=utf-8," + encodeURIComponent( str
), false );
2066 if ( req
.overrideMimeType
) {
2067 req
.overrideMimeType( contentType
);
2070 return req
.responseXML
;
2077 function js_log( string
) {
2078 // Add any prepend debug strings if necessary (used for cross browser)
2079 if ( mw
.conf
['debug_pre'] )
2080 string
= mw
.conf
['debug_pre'] + string
;
2082 if ( window
.console
) {
2083 window
.console
.log( string
);
2086 * IE and non-Firebug debug:
2088 /*var log_elm = document.getElementById('mv_js_log');
2090 document.getElementsByTagName("body")[0].innerHTML = document.getElementsByTagName("body")[0].innerHTML +
2091 '<div style="position:absolute;z-index:500;top:0px;left:0px;right:0px;height:10px;">'+
2092 '<textarea id="mv_js_log" cols="120" rows="5"></textarea>'+
2095 var log_elm = document.getElementById('mv_js_log');
2098 log_elm.value+=string+"\n";
2104 function js_error( string
) {