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)
17 // Fix multiple instances of mv_embed (i.e. include twice from two different servers)
19 if( MV_EMBED_VERSION
){
22 // Used to grab fresh copies of scripts. (should be changed on commit)
23 var MV_EMBED_VERSION
= '1.0r19';
26 * Configuration variables (can be set from some preceding script).
27 * Set up mwConfig global, override any of the defaultMwConfig values:
28 * @@ more config values on the way ;)
30 var defaultMwConfig
= {
32 'jui_skin': 'redmond',
33 'video_size':'400x300'
39 // Install the default config values for anything not set in mwConfig
40 checkDefaultMwConfig();
42 // Whether or not to load java from an iframe.
43 // Note: this is necessary for remote embedding because of Java's security model)
45 var mv_java_iframe
= true;
47 // For use when mv_embed with script-loader is in the root MediaWiki path
48 var mediaWiki_mvEmbed_path
= 'js2/mwEmbed/';
50 var global_player_list
= new Array(); // The global player list per page
51 var global_req_cb
= new Array(); // The global request callback array
52 var _global
= this; // Global obj
53 var mv_init_done
= false;
54 var global_cb_count
= 0;
58 // (c) Steven Levithan <stevenlevithan.com>
60 function parseUri (str
) {
61 var o
= parseUri
.options
,
62 m
= o
.parser
[o
.strictMode
? "strict" : "loose"].exec(str
),
66 while (i
--) uri
[o
.key
[i
]] = m
[i
] || "";
69 uri
[o
.key
[12]].replace(o
.q
.parser
, function ($0, $1, $2) {
70 if ($1) uri
[o
.q
.name
][$1] = $2;
77 key
: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
80 parser
: /(?:^|&)([^&=]*)=?([^&]*)/g
83 strict
: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
84 loose
: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
90 // Get the mv_embed location if it has not been set
91 if( !mv_embed_path
) {
92 var mv_embed_path
= getMvEmbedPath();
95 // Set up the skin path
96 var mv_jquery_skin_path
= mv_embed_path
+ 'jquery/jquery.ui/themes/' +mwConfig
['jui_skin'] + '/';
97 var mv_skin_img_path
= mv_embed_path
+ 'skins/' + mwConfig
['skin_name'] + '/images/';
98 var mv_default_thumb_url
= mv_skin_img_path
+ 'vid_default_thumb.jpg';
101 // Init the global message table if it has not been initialised already
106 // Language msg loader
107 function loadGM( msgSet
) {
108 for( var i
in msgSet
) {
109 gMsg
[ i
] = msgSet
[i
];
113 // All default messages in [English] should be overwritten by the CMS language message system.
115 "mwe-loading_txt" : "loading <blink>...<\/blink>",
116 "mwe-loading_title" : "Loading...",
117 "mwe-size-gigabytes" : "$1 GB",
118 "mwe-size-megabytes" : "$1 MB",
119 "mwe-size-kilobytes" : "$1 K",
120 "mwe-size-bytes" : "$1 B",
121 "mwe-error_load_lib" : "Error: JavaScript $1 was not retrievable or does not define $2"
125 * AutoLoader paths (this should mirror the file: jsAutoloadLocalClasses.php )
126 * Any file _not_ listed here won't be auto-loadable
127 * @path The path to the file (or set of files) with ending slash
128 * @gClasses The set of classes
129 * if it's an array, $j.className becomes jquery.className.js
130 * if it's an associative object then key => value pairs are used
132 if( typeof mvAutoLoadClasses
== 'undefined' )
133 mvAutoLoadClasses
= {};
135 // The script that loads the class set
136 function lcPaths( classSet
){
137 for( var i
in classSet
) {
138 mvAutoLoadClasses
[i
] = classSet
[i
];
142 function mvGetClassPath(k
){
143 if( mvAutoLoadClasses
[k
] ) {
144 //js_log('got class path:' + k + ' : '+ mvClassPaths[k]);
145 return mvAutoLoadClasses
[k
];
147 return js_error('could not find path for requested class ' + k
);
150 if( typeof mvCssPaths
== 'undefined' )
153 function lcCssPath( cssSet
) {
154 for( var i
in cssSet
) {
155 mvCssPaths
[i
] = mv_embed_path
+ cssSet
[i
];
160 * -- Load Class Paths --
162 * MUST BE VALID JSON (NOT JS)
163 * This is used by the script loader to auto-load classes (so we only define
164 * this once for PHP & JavaScript)
166 * This is more verbose than the earlier version that compressed paths
167 * but it's all good, gzipping helps compress repetetive path strings
168 * grouped by directory.
170 * Right now the PHP AutoLoader only reads this mv_embed.js file.
171 * In the future we could have multiple lcPath calls that PHP reads
172 * (if our autoloading class list becomes too long) just have to add those
173 * files to the jsAutoLoader file list.
176 "mv_embed" : "mv_embed.js",
177 "window.jQuery" : "jquery/jquery-1.3.2.js",
178 "$j.fn.pngFix" : "jquery/plugins/jquery.pngFix.js",
179 "$j.fn.autocomplete": "jquery/plugins/jquery.autocomplete.js",
180 "$j.fn.hoverIntent" : "jquery/plugins/jquery.hoverIntent.js",
181 "$j.fn.datePicker" : "jquery/plugins/jquery.datePicker.js",
182 "$j.ui" : "jquery/jquery.ui/ui/ui.core.js",
183 "$j.fn.ColorPicker" : "libClipEdit/colorpicker/js/colorpicker.js",
184 "$j.Jcrop" : "libClipEdit/Jcrop/js/jquery.Jcrop.js",
185 "$j.fn.simpleUploadForm": "libAddMedia/simpleUploadForm.js",
187 "ctrlBuilder" : "skins/ctrlBuilder.js",
188 "kskin" : "skins/kskin/kskin.js",
189 "mvpcf" : "skins/mvpcf/mvpcf.js",
191 "$j.secureEvalJSON" : "jquery/plugins/jquery.secureEvalJSON.js",
192 "$j.cookie" : "jquery/plugins/jquery.cookie.js",
193 "$j.contextMenu" : "jquery/plugins/jquery.contextMenu.js",
194 "$j.fn.suggestions" : "jquery/plugins/jquery.suggestions.js",
196 "$j.effects.blind" : "jquery/jquery.ui/ui/effects.blind.js",
197 "$j.effects.drop" : "jquery/jquery.ui/ui/effects.drop.js",
198 "$j.effects.pulsate" : "jquery/jquery.ui/ui/effects.pulsate.js",
199 "$j.effects.transfer" : "jquery/jquery.ui/ui/effects.transfer.js",
200 "$j.ui.droppable" : "jquery/jquery.ui/ui/ui.droppable.js",
201 "$j.ui.slider" : "jquery/jquery.ui/ui/ui.slider.js",
202 "$j.effects.bounce" : "jquery/jquery.ui/ui/effects.bounce.js",
203 "$j.effects.explode" : "jquery/jquery.ui/ui/effects.explode.js",
204 "$j.effects.scale" : "jquery/jquery.ui/ui/effects.scale.js",
205 "$j.ui.datepicker" : "jquery/jquery.ui/ui/ui.datepicker.js",
206 "$j.ui.progressbar" : "jquery/jquery.ui/ui/ui.progressbar.js",
207 "$j.ui.sortable" : "jquery/jquery.ui/ui/ui.sortable.js",
208 "$j.effects.clip" : "jquery/jquery.ui/ui/effects.clip.js",
209 "$j.effects.fold" : "jquery/jquery.ui/ui/effects.fold.js",
210 "$j.effects.shake" : "jquery/jquery.ui/ui/effects.shake.js",
211 "$j.ui.dialog" : "jquery/jquery.ui/ui/ui.dialog.js",
212 "$j.ui.resizable" : "jquery/jquery.ui/ui/ui.resizable.js",
213 "$j.ui.tabs" : "jquery/jquery.ui/ui/ui.tabs.js",
214 "$j.effects.core" : "jquery/jquery.ui/ui/effects.core.js",
215 "$j.effects.highlight" : "jquery/jquery.ui/ui/effects.highlight.js",
216 "$j.effects.slide" : "jquery/jquery.ui/ui/effects.slide.js",
217 "$j.ui.accordion" : "jquery/jquery.ui/ui/ui.accordion.js",
218 "$j.ui.draggable" : "jquery/jquery.ui/ui/ui.draggable.js",
219 "$j.ui.selectable" : "jquery/jquery.ui/ui/ui.selectable.js",
221 "mvFirefogg" : "libAddMedia/mvFirefogg.js",
222 "mvAdvFirefogg" : "libAddMedia/mvAdvFirefogg.js",
223 "mvBaseUploadInterface" : "libAddMedia/mvBaseUploadInterface.js",
224 "remoteSearchDriver" : "libAddMedia/remoteSearchDriver.js",
225 "seqRemoteSearchDriver" : "libAddMedia/seqRemoteSearchDriver.js",
227 "baseRemoteSearch" : "libAddMedia/searchLibs/baseRemoteSearch.js",
228 "mediaWikiSearch" : "libAddMedia/searchLibs/mediaWikiSearch.js",
229 "metavidSearch" : "libAddMedia/searchLibs/metavidSearch.js",
230 "archiveOrgSearch" : "libAddMedia/searchLibs/archiveOrgSearch.js",
231 "baseRemoteSearch" : "libAddMedia/searchLibs/baseRemoteSearch.js",
233 "mvClipEdit" : "libClipEdit/mvClipEdit.js",
235 "embedVideo" : "libEmbedVideo/embedVideo.js",
236 "flashEmbed" : "libEmbedVideo/flashEmbed.js",
237 "genericEmbed" : "libEmbedVideo/genericEmbed.js",
238 "htmlEmbed" : "libEmbedVideo/htmlEmbed.js",
239 "javaEmbed" : "libEmbedVideo/javaEmbed.js",
240 "nativeEmbed" : "libEmbedVideo/nativeEmbed.js",
241 "quicktimeEmbed" : "libEmbedVideo/quicktimeEmbed.js",
242 "vlcEmbed" : "libEmbedVideo/vlcEmbed.js",
244 "mvPlayList" : "libSequencer/mvPlayList.js",
245 "mvSequencer" : "libSequencer/mvSequencer.js",
246 "mvFirefoggRender" : "libSequencer/mvFirefoggRender.js",
247 "mvTimedEffectsEdit": "libSequencer/mvTimedEffectsEdit.js",
249 "mvTextInterface" : "libTimedText/mvTextInterface.js"
253 // Dependency mapping for CSS files for self-contained included plugins:
255 '$j.Jcrop' : 'libClipEdit/Jcrop/css/jquery.Jcrop.css',
256 '$j.fn.ColorPicker' : 'libClipEdit/colorpicker/css/colorpicker.css'
259 * Language Functions:
261 * These functions try to loosely mirror the functionality of Language.php in MediaWiki
263 function gM( key
, args
) {
267 if( typeof args
== 'object' || typeof args
== 'array' ) {
268 for( var v
in args
) {
269 // Message test replace arguments start at 1 instead of zero:
270 var rep
= '\$'+ ( parseInt(v
) + 1 );
271 ms
= ms
.replace( rep
, args
[v
] );
273 } else if( typeof args
=='string' || typeof args
=='number' ) {
274 ms
= ms
.replace(/\$1/, args
);
278 // Missing key placeholder
279 return '<' + key
+ '>';
283 * gMsgLoadRemote loads remote msg strings
285 * @param mixed msgSet the set of msg to load remotely
286 * @param function callback the callback to pass loaded msg to
288 function gMsgLoadRemote( msgSet
, callback
) {
290 if( typeof msgSet
== 'object' ) {
291 for( var i
in msgSet
) {
292 ammessages
+= msgSet
[i
] + '|';
294 } else if( typeof msgSet
== 'string' ) {
295 ammessages
+= msgSet
;
297 if( ammessages
== '' ) {
298 js_log( 'gMsgLoadRemote: no message set requested' );
303 'meta': 'allmessages',
304 'ammessages': ammessages
306 }, function( data
) {
307 if( data
.query
.allmessages
) {
308 var msgs
= data
.query
.allmessages
;
309 for( var i
in msgs
) {
311 ld
[ msgs
[i
]['name'] ] = msgs
[i
]['*'];
315 // Load the result into local msg var
321 * Format a size in bytes for output, using an appropriate
322 * unit (B, KB, MB or GB) according to the magnitude in question
324 * @param size Size to format
325 * @return string Plain text (not HTML)
327 function formatSize( size
) {
328 // For small sizes no decimal places are necessary
335 // For MB and bigger two decimal places are smarter
339 msg
= 'mwe-size-gigabytes';
341 msg
= 'mwe-size-megabytes';
344 msg
= 'mwe-size-kilobytes';
347 msg
= 'mwe-size-bytes';
349 // JavaScript does not let you choose the precision when rounding
350 var p
= Math
.pow(10,round
);
351 var size
= Math
.round( size
* p
) / p
;
352 //@@todo we need a formatNum and we need to request some special packaged info to deal with that case.
353 return gM( msg
, size
);
356 // Get the loading image
357 function mv_get_loading_img( style
, class_attr
){
358 var style_txt
= (style
)?style
:'';
359 var class_attr
= (class_attr
)?'class="'+class_attr
+'"':'class="mv_loading_img"';
360 return '<div '+class_attr
+' style="' + style
+'"></div>';
363 function mv_set_loading(target
, load_id
){
364 var id_attr
= ( load_id
)?' id="' + load_id
+ '" ':'';
365 $j(target
).append('<div '+id_attr
+' style="position:absolute;top:0px;left:0px;height:100%;width:100%;'+
366 'background-color:#FFF;">' +
367 mv_get_loading_img('top:30px;left:30px') +
372 * mvJsLoader class handles initialization and js file loads
376 function mwLoad( loadSet
, callback
) {
377 mvJsLoader
.doLoad( loadSet
, callback
);
384 onReadyEvents
: new Array(),
385 doneReadyEvents
: false,
386 jQueryCheckFlag
: false,
388 // To keep consistency across threads
392 load_error
: false, // Load error flag (false by default)
394 callbacks
: new Array(),
397 doLoad: function( loadLibs
, callback
) {
400 if( loadLibs
&& loadLibs
.length
!= 0 ) {
402 // First check if we already have this library loaded
403 var all_libs_loaded
= true;
404 for( var i
= 0; i
< loadLibs
.length
; i
++ ) {
405 // Check if the library is already loaded
406 if( ! this.checkObjPath( loadLibs
[i
] ) ) {
407 all_libs_loaded
= false;
410 if( all_libs_loaded
) {
411 js_log( 'All libraries already loaded, skipping load request' );
415 // Do a check for any CSS we may need and get it
416 for( var i
= 0; i
< loadLibs
.length
; i
++ ) {
417 if( typeof mvCssPaths
[ loadLibs
[i
] ] != 'undefined' ) {
418 loadExternalCss( mvCssPaths
[ loadLibs
[i
] ] );
422 // Check if we should use the script loader to combine all the requests into one
423 if( typeof mwSlScript
!= 'undefined' ) {
427 for( var i
= 0; i
< loadLibs
.length
; i
++ ) {
428 var curLib
= loadLibs
[i
];
429 // Only add if not included yet:
430 if( ! this.checkObjPath( curLib
) ) {
431 class_set
+= coma
+ curLib
;
436 var puri
= parseUri( getMvEmbedURL() );
437 if( ( getMvEmbedURL().indexOf('://') != -1 )
438 && puri
.host
!= parseUri( document
.URL
).host
)
440 mwSlScript
= puri
.protocol
+ '://' + puri
.authority
+ mwSlScript
;
443 var dbug_attr
= ( puri
.queryKey
['debug'] ) ? '&debug=true' : '';
444 this.libs
[ last_class
] = mwSlScript
+ '?class=' + class_set
+
445 '&urid=' + getMvUniqueReqId() + dbug_attr
;
449 for( var i
= 0; i
< loadLibs
.length
; i
++ ) {
450 var curLib
= loadLibs
[i
];
452 var libLoc
= mvGetClassPath( curLib
);
453 // Do a direct load of the file (pass along unique request id from
454 // request or mv_embed Version )
455 var qmark
= (libLoc
.indexOf( '?' ) !== true) ? '?' : '&';
456 this.libs
[curLib
] = mv_embed_path
+ libLoc
+ qmark
+ 'urid=' + getMvUniqueReqId();
462 this.callbacks
.push( callback
);
464 if( this.checkLoading() ) {
465 //@@todo we should check the <script> Element .onLoad property to
466 //make sure its just not a very slow connection
468 if( this.load_time
++ > 4000 ){ // Time out after ~80 seconds
469 js_error( gM('mwe-error_load_lib', mvGetClassPath(this.missing_path
), this.missing_path
) );
470 this.load_error
= true;
472 setTimeout( 'mvJsLoader.doLoad()', 20 );
475 //js_log('checkLoading passed. Running callbacks...');
476 // Only do callbacks if we are in the same instance (weird concurency issue)
478 for( var i
= 0; i
< this.callbacks
.length
; i
++ )
480 //js_log('RESET LIBS: loading is: '+ loading + ' callback count: '+cb_count +
481 // ' p:'+ this.ptime +' c:'+ this.ctime);
485 //js_log('done loading, do call: ' + this.callbacks[0] );
486 while( this.callbacks
.length
!= 0 ) {
487 if( this.ptime
== this.ctime
- 1 ) { // Enforce thread consistency
488 this.callbacks
.pop()();
489 //func = this.callbacks.pop();
490 //js_log(' run: '+this.ctime+ ' p: ' + this.ptime + ' ' +loading+ ' :'+ func);
493 // Re-issue doLoad ( ptime will be set to ctime so we should catch up)
494 setTimeout( 'mvJsLoader.doLoad()', 25 );
499 this.ptime
= this.ctime
;
501 doLoadDepMode: function( loadChain
, callback
) {
502 // Firefox executes JS in the order in which it is included, so just directly issue the request
503 if( $j
.browser
.firefox
) {
505 for( var i
= 0; i
< loadChain
.length
; i
++ ) {
506 for( var j
= 0; j
< loadChain
[i
].length
; j
++ ) {
507 loadSet
.push( loadChain
[i
][j
] );
510 mvJsLoader
.doLoad( loadSet
, callback
);
512 // Safari and IE tend to execute out of order so load with dependency checks
513 mvJsLoader
.doLoad( loadChain
.shift(), function() {
514 if( loadChain
.length
!= 0 ) {
515 mvJsLoader
.doLoadDepMode( loadChain
, callback
);
522 checkLoading: function() {
525 for( var i
in this.libs
) { // for/in loop is OK on an object
526 if( !this.checkObjPath( i
) ) {
527 if( !this.libreq
[i
] ) {
528 loadExternalJs( this.libs
[i
] );
531 //js_log("has not yet loaded: " + i);
537 checkObjPath: function( libVar
) {
540 var objPath
= libVar
.split( '.' )
542 for( var p
= 0; p
< objPath
.length
; p
++ ) {
543 cur_path
= (cur_path
== '') ? cur_path
+ objPath
[p
] : cur_path
+ '.' + objPath
[p
];
544 eval( 'var ptest = typeof ( '+ cur_path
+ ' ); ');
545 if( ptest
== 'undefined' ) {
546 this.missing_path
= cur_path
;
550 this.cur_path
= cur_path
;
554 * checks for jQuery and adds the $j noConflict var
556 jQueryCheck: function( callback
) {
557 // Skip stuff if $j is already loaded
558 if( _global
['$j'] && callback
)
565 _global
['$j'] = jQuery
.noConflict();
566 // Set up AJAX to not send dynamic URLs for loading scripts (we control that with
571 js_log( 'jquery loaded' );
572 // Set up mvEmbed jQuery bindings:
580 embedVideoCheck:function( callback
) {
582 js_log( 'embedVideoCheck:' );
583 // Set videonojs to loading
584 // Issue a style sheet request to get both mv_embed and jQuery styles:
585 loadExternalCss( mv_jquery_skin_path
+ 'jquery-ui-1.7.1.custom.css' );
586 loadExternalCss( mv_embed_path
+ 'skins/'+mwConfig
['skin_name'] + '/styles.css' );
588 // Make sure we have jQuery
589 _this
.jQueryCheck( function() {
590 $j('.videonojs').html( gM('mwe-loading_txt') );
602 // Add PNG fix if needed:
603 if( $j
.browser
.msie
|| $j
.browser
.version
< 7 )
604 depReq
[0].push( '$j.fn.pngFix' );
606 _this
.doLoadDepMode( depReq
, function() {
609 $j('.videonojs').remove();
613 addLoadEvent: function( fn
) {
614 this.onReadyEvents
.push( fn
);
616 // Check the jQuery flag. This way, when remote embedding, we don't load jQuery
617 // unless js2AddOnloadHook was used or there is video on the page.
618 runQueuedFunctions: function() {
620 this.doneReadyEvents
= true;
621 if( this.jQueryCheckFlag
) {
622 this.jQueryCheck( function() {
623 _this
.runReadyEvents();
626 this.runReadyEvents();
629 runReadyEvents: function() {
630 js_log( "runReadyEvents" );
631 while( this.onReadyEvents
.length
) {
632 this.onReadyEvents
.shift()();
637 // Load an external JS file. Similar to jquery .require plugin,
638 // but checks for object availability rather than load state.
640 /*********** INITIALIZATION CODE *************
641 * This will get called when the DOM is ready
642 *********************************************/
643 /* jQuery .ready does not work when jQuery is loaded dynamically.
644 * For an example of the problem see: 1.1.3 working: http://pastie.caboo.se/92588
645 * and >= 1.1.4 not working: http://pastie.caboo.se/92595
646 * $j(document).ready( function(){ */
647 function mwdomReady( force
) {
648 js_log( 'f:mwdomReady:' );
649 if( !force
&& mv_init_done
) {
650 js_log( "mv_init_done already done, do nothing..." );
654 // Handle the execution of queued functions with jQuery "ready"
656 // Check if this page has a video or playlist
658 document
.getElementsByTagName( "video" ),
659 document
.getElementsByTagName( "audio" ),
660 document
.getElementsByTagName( "playlist" )
662 if( e
[0].length
!= 0 || e
[1].length
!= 0 || e
[2].length
!= 0 ) {
663 js_log( 'we have items to rewrite' );
664 setSwappableToLoading( e
);
665 // Load libs and process them
666 mvJsLoader
.embedVideoCheck( function() {
667 // Run any queued global events:
668 mv_video_embed( function() {
669 mvJsLoader
.runQueuedFunctions();
673 // If we already have jQuery, make sure it's loaded into its proper context $j
674 // Run any queued global events
675 mvJsLoader
.runQueuedFunctions();
678 // A quick function that sets the initial text of swappable elements to "loading".
679 // jQuery might not be ready. Does not destroy inner elements.
680 function setSwappableToLoading( e
) {
682 //for(var j = 0; i < j.length; j++){
685 //js2AddOnloadHook: ensure jQuery and the DOM are ready
686 function js2AddOnloadHook( func
) {
687 // Make sure the skin/style sheets are always available:
688 loadExternalCss( mv_jquery_skin_path
+ 'jquery-ui-1.7.1.custom.css' );
689 loadExternalCss( mv_embed_path
+ 'skins/' + mwConfig
['skin_name'] + '/styles.css' );
691 // If we have already run the DOM-ready function, just run the function directly:
692 if( mvJsLoader
.doneReadyEvents
) {
693 // Make sure jQuery is there:
694 mvJsLoader
.jQueryCheck( function() {
698 // If we are using js2AddOnloadHook we need to get jQuery into place (if it's not already included)
699 mvJsLoader
.jQueryCheckFlag
= true;
700 mvJsLoader
.addLoadEvent( func
);
703 // Deprecated mwAddOnloadHook in favor of js2 naming (for clear separation of js2 code from old MW code
704 var mwAddOnloadHook
= js2AddOnloadHook
;
706 * This function allows for targeted rewriting
708 function rewrite_by_id( vid_id
, ready_callback
) {
709 js_log( 'f:rewrite_by_id: ' + vid_id
);
710 // Force a recheck of the DOM for playlist or video elements:
711 mvJsLoader
.embedVideoCheck( function() {
712 mv_video_embed( ready_callback
, vid_id
);
715 // Deprecated in favor of updates to OggHandler
716 function rewrite_for_OggHandler( vidIdList
){
717 for( var i
= 0; i
< vidIdList
.length
; i
++ ) {
718 var vidId
= vidIdList
[i
];
719 js_log( 'looking at vid: ' + i
+' ' + vidId
);
720 // Grab the thumbnail and src of the video
721 var pimg
= $j( '#' + vidId
+ ' img' );
722 var poster_attr
= 'poster = "' + pimg
.attr( 'src' ) + '" ';
723 var pwidth
= pimg
.attr( 'width' );
724 var pheight
= pimg
.attr( 'height' );
728 if( pwidth
== '22' && pheight
== '22' ) {
731 type_attr
= 'type="audio/ogg"';
739 var re
= new RegExp( /videoUrl(":?\s*)*([^&]*)/ );
740 src
= re
.exec( $j( '#'+vidId
).html() )[2];
742 var re
= new RegExp( /length(":?\s*)*([^&]*)/ );
743 duration
= re
.exec( $j( '#'+vidId
).html() )[2];
745 var re
= new RegExp( /offset(":?\s*)*([^&]*)/ );
746 offset
= re
.exec( $j( '#'+vidId
).html() )[2];
747 var offset_attr
= offset
? 'startOffset="' + offset
+ '"' : '';
750 // Replace the top div with the mv_embed based player:
751 var vid_html
= '<video id="vid_' + i
+'" '+
752 'src="' + src
+ '" ' +
756 'duration="' + duration
+ '" ' +
757 'style="width:' + pwidth
+ 'px;height:' +
758 pheight
+ 'px;"></video>';
759 //js_log("Video HTML: " + vid_html);
760 $j( '#'+vidId
).html( vid_html
);
763 // Rewrite that video ID:
764 rewrite_by_id( 'vid_' + i
);
769 /*********** INITIALIZATION CODE *************
770 * set DOM-ready callback to init_mv_embed
771 *********************************************/
772 // for Mozilla browsers
773 if ( document
.addEventListener
) {
774 document
.addEventListener( "DOMContentLoaded", function(){ mwdomReady() }, false );
776 // Use the onload method instead when DOMContentLoaded does not exist
777 window
.onload = function() { mwdomReady() };
780 * Should deprecate and use jquery.ui.dialog instead
782 function mv_write_modal( content
, speed
) {
783 $j( '#modalbox,#mv_overlay' ).remove();
785 '<div id="modalbox" style="background:#DDD;border:3px solid #666666;font-size:115%;' +
786 'top:30px;left:20px;right:20px;bottom:30px;position:fixed;z-index:100;">' +
789 '<div id="mv_overlay" style="background:#000;cursor:wait;height:100%;left:0;position:fixed;' +
790 'top:0;width:100%;z-index:5;filter:alpha(opacity=60);-moz-opacity: 0.6;' +
792 $j( '#modalbox,#mv_overlay' ).show( speed
);
794 function mv_remove_modal( speed
) {
795 $j( '#modalbox,#mv_overlay' ).remove( speed
);
799 * Store all the mwEmbed jQuery-specific bindings
800 * (set up after jQuery is available).
801 * This lets you call rewrites in a jQuery way
803 * @@ eventually we should refactor mwCode over to jQuery style plugins
804 * and mv_embed.js will just handle dependency mapping and loading.
807 function mv_jqueryBindings() {
808 js_log( 'mv_jqueryBindings' );
810 $.fn
.addMediaWiz = function( iObj
, callback
) {
811 // First set the cursor for the button to "loading"
812 $j( this.selector
).css( 'cursor', 'wait' ).attr( 'title', gM( 'mwe-loading_title' ) );
814 iObj
['target_invocation'] = this.selector
;
816 // Load the mv_embed_base skin:
817 loadExternalCss( mv_jquery_skin_path
+ 'jquery-ui-1.7.1.custom.css' );
818 loadExternalCss( mv_embed_path
+ 'skins/' + mwConfig
['skin_name']+'/styles.css' );
819 // Load all the required libs:
820 mvJsLoader
.jQueryCheck( function() {
821 // Load with staged dependencies (for IE and Safari that don't execute in order)
822 mvJsLoader
.doLoadDepMode([
823 [ 'remoteSearchDriver',
834 iObj
['instance_name'] = 'rsdMVRS';
835 _global
['rsdMVRS'] = new remoteSearchDriver( iObj
);
837 callback( _global
['rsdMVRS'] );
842 $.fn
.sequencer = function( iObj
, callback
) {
844 iObj
['target_sequence_container'] = this.selector
;
845 // Issue a request to get the CSS file (if not already included):
846 loadExternalCss( mv_jquery_skin_path
+ 'jquery-ui-1.7.1.custom.css' );
847 loadExternalCss( mv_embed_path
+ 'skins/' + mwConfig
['skin_name'] + '/mv_sequence.css' );
848 // Make sure we have the required mv_embed libs (they are not loaded when no video
849 // element is on the page)
850 mvJsLoader
.embedVideoCheck( function() {
851 // Load the playlist object and then the jQuery UI stuff:
852 mvJsLoader
.doLoadDepMode([
872 js_log( 'calling new mvSequencer' );
873 // Initialise the sequence object (it will take over from there)
874 // No more than one mvSeq obj for now:
875 if( !_global
['mvSeq'] ) {
876 _global
['mvSeq'] = new mvSequencer( iObj
);
878 js_log( 'mvSeq already init' );
884 * The Firefogg jQuery function:
885 * @@note This Firefogg invocation could be made to work more like real jQuery plugins
887 $.fn
.firefogg = function( iObj
, callback
) {
890 // Add the base theme CSS:
891 loadExternalCss( mv_jquery_skin_path
+ 'jquery-ui-1.7.1.custom.css' );
892 loadExternalCss( mv_embed_path
+ 'skins/'+mwConfig
['skin_name'] + '/styles.css' );
894 // Check if we already have Firefogg loaded (the call just updates the element's
896 var sElm
= $j( this.selector
).get( 0 );
897 if( sElm
['firefogg'] ) {
898 if( sElm
['firefogg'] == 'loading' ) {
899 js_log( "Error: called firefogg operations on Firefogg selector that is " +
900 "not done loading" );
904 for( var i
in iObj
) {
905 js_log( "firefogg::updated: " + i
+ ' to '+ iObj
[i
] );
906 sElm
['firefogg'][i
] = iObj
[i
];
908 return sElm
['firefogg'];
911 sElm
['firefogg'] = 'loading';
914 iObj
['selector'] = this.selector
;
918 'mvBaseUploadInterface',
927 if( iObj
.encoder_interface
) {
936 // Make sure we have everything loaded that we need:
937 mvJsLoader
.doLoadDepMode( loadSet
, function() {
938 js_log( 'firefogg libs loaded. target select:' + iObj
.selector
);
939 // Select interface provider based on whether we want to include the
940 // encoder interface or not
941 if( iObj
.encoder_interface
) {
942 var myFogg
= new mvAdvFirefogg( iObj
);
944 var myFogg
= new mvFirefogg( iObj
);
947 myFogg
.doRewrite( callback
);
948 var selectorElement
= $j( iObj
.selector
).get( 0 );
949 selectorElement
['firefogg'] = myFogg
;
953 // Take an input player as the selector and expose basic rendering controls
954 $.fn
.firefoggRender = function( iObj
, callback
) {
955 // Check if we already have render loaded then just pass on updates/actions
956 var sElm
= $j( this.selector
).get( 0 );
957 if( sElm
['fogg_render'] ) {
958 if( sElm
['fogg_render'] == 'loading' ) {
959 js_log( "Error: called firefoggRender while loading" );
962 // Call or update the property:
964 sElm
['fogg_render'] = 'loading';
966 iObj
['player_target'] = this.selector
;
971 sElm
['fogg_render'] = new mvFirefoggRender( iObj
);
972 if( callback
&& typeof callback
== 'function' )
973 callback( sElm
['fogg_render'] );
977 $.fn
.baseUploadInterface = function(iObj
) {
978 mvJsLoader
.doLoadDepMode([
980 'mvBaseUploadInterface',
988 myUp
= new mvBaseUploadInterface( iObj
);
993 // Shortcut to a themed button
994 $.btnHtml = function( msg
, className
, iconId
, opt
) {
997 var href
= (opt
.href
) ? opt
.href
: '#';
998 var target_attr
= (opt
.target
) ? ' target="' + opt
.target
+ '" ' : '';
999 var style_attr
= (opt
.style
) ? ' style="' + opt
.style
+ '" ' : '';
1000 return '<a href="' + href
+ '" ' + target_attr
+ style_attr
+
1001 ' class="ui-state-default ui-corner-all ui-icon_link ' +
1002 className
+ '"><span class="ui-icon ui-icon-' + iconId
+ '" />' +
1005 // Shortcut to bind hover state
1006 $.fn
.btnBind = function() {
1009 $j( this ).addClass( 'ui-state-hover' );
1012 $j( this ).removeClass( 'ui-state-hover' );
1021 * Utility functions:
1023 // Simple URL rewriter (could probably be refactored into an inline regular expresion)
1024 function getURLParamReplace( url
, opt
) {
1025 var pSrc
= parseUri( url
);
1026 if( pSrc
.protocol
!= '' ) {
1027 var new_url
= pSrc
.protocol
+ '://' + pSrc
.authority
+ pSrc
.path
+ '?';
1029 var new_url
= pSrc
.path
+ '?';
1032 for( var key
in pSrc
.queryKey
) {
1033 var val
= pSrc
.queryKey
[ key
];
1034 // Do override if requested
1037 new_url
+= amp
+ key
+ '=' + val
;
1040 // Add any vars that were not already there:
1041 for( var i
in opt
) {
1042 if( !pSrc
.queryKey
[i
] ) {
1043 new_url
+= amp
+ i
+ '=' + opt
[i
];
1050 * Given a float number of seconds, returns npt format response.
1052 * @param float Seconds
1053 * @param boolean If we should show milliseconds or not.
1055 function seconds2npt( sec
, show_ms
) {
1056 if( isNaN( sec
) ) {
1057 // js_log("warning: trying to get npt time on NaN:" + sec);
1060 var hours
= Math
.floor( sec
/ 3600 );
1061 var minutes
= Math
.floor( (sec
/ 60) % 60 );
1062 var seconds
= sec
% 60;
1063 // Round the number of seconds to the required number of significant digits
1065 seconds
= Math
.round( seconds
* 1000 ) / 1000;
1067 seconds
= Math
.round( seconds
);
1070 seconds
= '0' + seconds
;
1072 minutes
= '0' + minutes
;
1074 return hours
+ ":" + minutes
+ ":" + seconds
;
1077 * Take hh:mm:ss,ms or hh:mm:ss.ms input, return the number of seconds
1079 function npt2seconds( npt_str
) {
1081 //js_log('npt2seconds:not valid ntp:'+ntp);
1084 // Strip "npt:" time definition if present
1085 npt_str
= npt_str
.replace( 'npt:', '' );
1087 times
= npt_str
.split( ':' );
1088 if( times
.length
!= 3 ){
1089 js_log( 'error: npt2seconds on ' + npt_str
);
1092 // Sometimes a comma is used instead of period for ms
1093 times
[2] = times
[2].replace( /,\s?/, '.' );
1094 // Return seconds float
1095 return parseInt( times
[0] * 3600) + parseInt( times
[1] * 60 ) + parseFloat( times
[2] );
1098 * Simple helper to grab an edit token
1100 * @param title The wiki page title you want to edit
1101 * @param api_url 'optional' The target API URL
1102 * @param callback The callback function to pass the token to
1104 function get_mw_token( title
, api_url
, callback
) {
1105 js_log( ':get_mw_token:' );
1106 if( !title
&& wgUserName
) {
1107 title
= 'User:' + wgUserName
;
1119 for( var i
in data
.query
.pages
) {
1120 if( data
.query
.pages
[i
]['edittoken'] ) {
1121 if( typeof callback
== 'function' )
1122 callback ( data
.query
.pages
[i
]['edittoken'] );
1130 // Do a remote or local API request based on request URL
1131 //@param options: url, data, cbParam, callback
1132 function do_api_req( options
, callback
) {
1133 if( typeof options
.data
!= 'object' ) {
1134 return js_error( 'Error: request paramaters must be an object' );
1136 // Generate the URL if it's missing
1137 if( typeof options
.url
== 'undefined' || options
.url
=== false ) {
1138 if( !wgServer
|| ! wgScriptPath
) {
1139 return js_error('Error: no api url for api request');
1141 options
.url
= mwGetLocalApiUrl();
1143 if( typeof options
.data
== 'undefined' )
1146 // Force format to JSON
1147 options
.data
['format'] = 'json';
1149 // If action is not set, assume query
1150 if( !options
.data
['action'] )
1151 options
.data
['action'] = 'query';
1153 // js_log('do api req: ' + options.url +'?' + jQuery.param(options.data) );
1154 // Build request string
1155 if( parseUri( document
.URL
).host
== parseUri( options
.url
).host
) {
1156 // Local request: do API request directly
1161 dataType
: 'json', // API requests _should_ always return JSON data:
1163 success: function( data
) {
1166 error: function( e
) {
1167 js_error( ' error' + e
+ ' in getting: ' + options
.url
);
1172 // Set the callback param if it's not already set
1173 if( typeof options
.jsonCB
== 'undefined' )
1174 options
.jsonCB
= 'callback';
1176 var req_url
= options
.url
;
1177 var paramAnd
= ( req_url
.indexOf( '?' ) == -1 ) ? '?' : '&';
1178 // Put all the parameters into the URL
1179 for( var i
in options
.data
) {
1180 req_url
+= paramAnd
+ encodeURIComponent( i
) + '=' + encodeURIComponent( options
.data
[i
] );
1183 var fname
= 'mycpfn_' + ( global_cb_count
++ );
1184 _global
[ fname
] = callback
;
1185 req_url
+= '&' + options
.jsonCB
+ '=' + fname
;
1186 loadExternalJs( req_url
);
1189 function mwGetLocalApiUrl( url
) {
1190 if ( wgServer
&& wgScriptPath
) {
1191 return wgServer
+ wgScriptPath
+ '/api.php';
1195 // Grab wiki form error for wiki html page proccessing (should be deprecated)
1196 function grabWikiFormError( result_page
) {
1198 sp
= result_page
.indexOf( '<span class="error">' );
1200 se
= result_page
.indexOf( '</span>', sp
);
1201 res
.error_txt
= result_page
.substr( sp
, sp
- se
) + '</span>';
1204 sp
= result_page
.indexOf( '<ul class="warning">' )
1206 se
= result_page
.indexOf( '</ul>', sp
);
1207 res
.error_txt
= result_page
.substr( sp
, se
- sp
) + '</ul>';
1208 // Try to add the ignore form item
1209 sfp
= result_page
.indexOf( '<form method="post"' );
1211 sfe
= result_page
.indexOf( '</form>', sfp
);
1212 res
.form_txt
= result_page
.substr( sfp
, sfe
- sfp
) + '</form>';
1215 // One more error type check
1216 sp
= result_page
.indexOf( 'class="mw-warning-with-logexcerpt">' )
1218 se
= result_page
.indexOf( '</div>', sp
);
1219 res
.error_txt
= result_page
.substr( sp
, se
- sp
) + '</div>';
1225 // Do a "normal" request
1226 function do_request( req_url
, callback
) {
1227 js_log( 'do_request::req_url:' + req_url
+ ' != ' + parseUri( req_url
).host
);
1228 // If we are doing a request to the same domain or relative link, do a normal GET
1229 if( parseUri( document
.URL
).host
== parseUri( req_url
).host
||
1230 req_url
.indexOf('://') == -1 ) // Relative url
1232 // Do a direct request
1237 success: function( data
) {
1242 // Get data via DOM injection with callback
1243 global_req_cb
.push( callback
);
1244 // Prepend json_ to feed_format if not already requesting json format
1245 if( req_url
.indexOf( "feed_format=" ) != -1 && req_url
.indexOf( "feed_format=json" ) == -1 )
1246 req_url
= req_url
.replace( /feed_format=/, 'feed_format=json_' );
1247 loadExternalJs( req_url
+ '&cb=mv_jsdata_cb&cb_inx=' + (global_req_cb
.length
- 1) );
1251 function mv_jsdata_cb( response
) {
1252 js_log( 'f:mv_jsdata_cb:'+ response
['cb_inx'] );
1253 // Run the callback from the global request callback object
1254 if( !global_req_cb
[response
['cb_inx']] ) {
1255 js_log( 'missing req cb index' );
1258 if( !response
['pay_load'] ) {
1259 js_log( "missing pay load" );
1262 switch( response
['content-type'] ) {
1266 if( typeof response
['pay_load'] == 'string' ) {
1267 //js_log('load string:'+"\n"+ response['pay_load']);
1269 if( $j
.browser
.msie
) {
1270 // Attempt to parse as XML for IE
1271 var xmldata
= new ActiveXObject("Microsoft.XMLDOM");
1272 xmldata
.async
= "false";
1273 xmldata
.loadXML( response
['pay_load'] );
1275 // For others (Firefox, Safari etc.)
1277 var xmldata
= (new DOMParser()).parseFromString( response
['pay_load'], "text/xml" );
1279 js_log( 'XML parse ERROR: ' + e
.message
);
1282 //@@todo handle XML parser errors
1283 if( xmldata
)response
['pay_load'] = xmldata
;
1287 js_log( 'bad response type' + response
['content-type'] );
1291 global_req_cb
[response
['cb_inx']]( response
['pay_load'] );
1293 // Load external JS via DOM injection
1294 function loadExternalJs( url
, callback
) {
1295 js_log( 'load js: '+ url
);
1296 //if(window['$j']) // use jquery call:
1304 var e
= document
.createElement( "script" );
1305 e
.setAttribute( 'src', url
);
1306 e
.setAttribute( 'type', "text/javascript" );
1308 e.onload = callback;
1310 //e.setAttribute('defer', true);
1311 document
.getElementsByTagName( "head" )[0].appendChild( e
);
1314 function styleSheetPresent( url
) {
1315 style_elements
= document
.getElementsByTagName( 'link' );
1316 if( style_elements
.length
> 0 ) {
1317 for( i
= 0; i
< style_elements
.length
; i
++ ) {
1318 if( style_elements
[i
].href
== url
)
1324 function loadExternalCss( url
) {
1325 // We could have the script loader group these CSS requests.
1326 // But it's debatable: it may hurt more than it helps with caching and all
1327 if( typeof url
=='object' ) {
1328 for( var i
in url
) {
1329 loadExternalCss( url
[i
] );
1334 if( url
.indexOf('?') == -1 ) {
1335 url
+= '?' + getMvUniqueReqId();
1337 if( !styleSheetPresent( url
) ) {
1338 js_log( 'load css: ' + url
);
1339 var e
= document
.createElement( "link" );
1341 e
.type
= "text/css";
1342 e
.rel
= 'stylesheet';
1343 document
.getElementsByTagName( "head" )[0].appendChild( e
);
1346 function getMvEmbedURL() {
1347 if( _global
['mv_embed_url'] )
1348 return _global
['mv_embed_url'];
1349 var js_elements
= document
.getElementsByTagName( "script" );
1350 for( var i
= 0; i
< js_elements
.length
; i
++ ) {
1351 // Check for mv_embed.js and/or script loader
1352 var src
= js_elements
[i
].getAttribute( "src" );
1354 if( src
.indexOf( 'mv_embed.js' ) != -1 || (
1355 ( src
.indexOf( 'mwScriptLoader.php' ) != -1 || src
.indexOf('jsScriptLoader.php') != -1 )
1356 && src
.indexOf('mv_embed') != -1) ) //(check for class=mv_embed script_loader call)
1358 _global
['mv_embed_url'] = src
;
1363 js_error( 'Error: getMvEmbedURL failed to get Embed Path' );
1366 // Get a unique request ID to ensure fresh JavaScript
1367 function getMvUniqueReqId() {
1368 if( _global
['urid'] )
1369 return _global
['urid'];
1370 var mv_embed_url
= getMvEmbedURL();
1371 // If we have a URI, return it
1372 var urid
= parseUri( mv_embed_url
).queryKey
['urid']
1374 _global
['urid'] = urid
;
1377 // If we're in debug mode, get a fresh unique request key
1378 if( parseUri( mv_embed_url
).queryKey
['debug'] == 'true' ) {
1380 var urid
= d
.getTime();
1381 _global
['urid'] = urid
;
1384 // Otherwise, just return the mv_embed version
1385 return MV_EMBED_VERSION
;
1388 * Set the global mv_embed path based on the script's location
1390 function getMvEmbedPath() {
1391 if( _global
['mv_embed_path'] )
1392 return _global
['mv_embed_path'];
1393 var mv_embed_url
= getMvEmbedURL();
1394 if( mv_embed_url
.indexOf( 'mv_embed.js' ) !== -1 ) {
1395 mv_embed_path
= mv_embed_url
.substr( 0, mv_embed_url
.indexOf( 'mv_embed.js' ) );
1396 } else if( mv_embed_url
.indexOf( 'mwScriptLoader.php' ) !== -1 ) {
1397 // Script loader is in the root of MediaWiki, so include the default mv_embed extension path
1398 mv_embed_path
= mv_embed_url
.substr( 0, mv_embed_url
.indexOf( 'mwScriptLoader.php' ) )
1399 + mediaWiki_mvEmbed_path
;
1401 mv_embed_path
= mv_embed_url
.substr( 0, mv_embed_url
.indexOf( 'jsScriptLoader.php' ) );
1403 // Make an absolute URL (if it's relative and we don't have an mv_embed path)
1404 if( mv_embed_path
.indexOf( '://' ) == -1 ) {
1405 var pURL
= parseUri( document
.URL
);
1406 if( mv_embed_path
.charAt( 0 ) == '/' ) {
1407 mv_embed_path
= pURL
.protocol
+ '://' + pURL
.authority
+ mv_embed_path
;
1410 if( mv_embed_path
== '' ) {
1411 mv_embed_path
= pURL
.protocol
+ '://' + pURL
.authority
+ pURL
.directory
+ mv_embed_path
;
1415 _global
['mv_embed_path'] = mv_embed_path
;
1416 return mv_embed_path
;
1419 if ( typeof DOMParser
== "undefined" ) {
1420 DOMParser = function () {}
1421 DOMParser
.prototype.parseFromString = function ( str
, contentType
) {
1422 if ( typeof ActiveXObject
!= "undefined" ) {
1423 var d
= new ActiveXObject( "MSXML.DomDocument" );
1426 } else if ( typeof XMLHttpRequest
!= "undefined" ) {
1427 var req
= new XMLHttpRequest
;
1428 req
.open( "GET", "data:" + (contentType
|| "application/xml") +
1429 ";charset=utf-8," + encodeURIComponent(str
), false );
1430 if ( req
.overrideMimeType
) {
1431 req
.overrideMimeType(contentType
);
1434 return req
.responseXML
;
1441 function js_log( string
) {
1442 if( window
.console
) {
1443 window
.console
.log( string
);
1446 * IE and non-Firebug debug:
1448 /*var log_elm = document.getElementById('mv_js_log');
1450 document.getElementsByTagName("body")[0].innerHTML = document.getElementsByTagName("body")[0].innerHTML +
1451 '<div style="position:absolute;z-index:500;top:0px;left:0px;right:0px;height:10px;">'+
1452 '<textarea id="mv_js_log" cols="120" rows="5"></textarea>'+
1455 var log_elm = document.getElementById('mv_js_log');
1458 log_elm.value+=string+"\n";
1464 function checkDefaultMwConfig() {
1465 for( var i
in defaultMwConfig
) {
1466 if( typeof( mwConfig
[i
] ) == 'undefined' ) {
1467 mwConfig
[i
] = defaultMwConfig
[i
];
1472 function js_error( string
) {