Update messages and formatting
[lhc/web/wiklou.git] / js2 / mwEmbed / libAddMedia / remoteSearchDriver.js
1 /*
2 * a library for doing remote media searches
3 *
4 * initial targeted archives are:
5 the local wiki
6 wikimedia commons
7 metavid
8 and archive.org
9 */
10
11 loadGM({
12 "mwe-add_media_wizard" : "Add media wizard",
13 "mwe-media_search" : "Media search",
14 "rsd_box_layout" : "Box layout",
15 "rsd_list_layout" : "List layout",
16 "rsd_results_desc" : "Results",
17 "rsd_results_next" : "next",
18 "rsd_results_prev" : "previous",
19 "rsd_no_results" : "No search results for <b>$1<\/b>",
20 "mwe-upload_tab" : "Upload",
21 "rsd_layout" : "Layout:",
22 "rsd_resource_edit" : "Edit resource: $1",
23 "mwe-resource_description_page" : "Resource description page",
24 "rsd_local_resource_title" : "Local resource title",
25 "rsd_do_insert" : "Do insert",
26 "mwe-cc_title" : "Creative Commons",
27 "mwe-cc_by_title" : "Attribution",
28 "mwe-cc_nc_title" : "Noncommercial",
29 "mwe-cc_nd_title" : "No Derivative Works",
30 "mwe-cc_sa_title" : "Share Alike",
31 "mwe-cc_pd_title" : "Public Domain",
32 "mwe-unknown_license" : "Unknown license",
33 "mwe-no_import_by_url" : "This user or wiki <b>can not<\/b> import assets from remote URLs.<\/p><p>Do you need to login?<\/p><p>If permissions are set, you may have to enable $wgAllowCopyUploads (<a href=\"http : \/\/www.mediawiki.org\/wiki\/Manual:$wgAllowCopyUploads\">more information<\/a>).<\/p>",
34 "mwe-results_from" : "Results from <a href=\"$1\" target=\"_new\" >$2<\/a>",
35 "mwe-missing_desc_see_source" : "This asset is missing a description. Please see the [$1 orginal source] and help describe it.",
36 "rsd_config_error" : "Add media wizard configuration error: $1",
37
38 "mwe-your_recent_uploads" : "Your recent uploads",
39 "mwe-upload_a_file" : "Upload a new file",
40
41 "mwe-resource_page_desc" : "Resource page description:",
42 "mwe-edit_resource_desc" : "Edit wiki text resource description:",
43 "mwe-local_resource_title" : "Local resource title:",
44 "mwe-watch_this_page" : "Watch this page",
45 "mwe-do_import_resource" : "Import resource",
46 "mwe-update_preview" : "Update preview",
47 "mwe-cancel_import" : "Cancel import",
48 "mwe-importing_asset" : "Importing asset",
49 "mwe-preview_insert_resource" : "Preview insert of resource: $1"
50 });
51
52 var default_remote_search_options = {
53 'profile':'mediawiki_edit',
54 'target_container':null, //the div that will hold the search interface
55 //if using a modeal dialog (instead of target_container) how close to the edge of the window should we go:
56 'modal_edge_padding':'20px',
57
58 'target_invocation': null, //the button or link that will invoke the search interface
59
60 'default_provider_id':'all', //all or one of the content_providers ids
61
62 'caret_pos':null,
63 'local_wiki_api_url':null,
64
65 //can be 'api', 'form', 'autodetect', 'remote_link'
66 'import_url_mode': 'autodetect',
67
68 'target_title':null,
69
70 'target_textbox':null,
71 'target_render_area': null, //where output render should go:
72 'instance_name': null, //a globally accessible callback instance name
73 'default_query':null, //default search query
74 //specific to sequence profile
75 'p_seq':null,
76 'cFileNS':'File', //what is the cannonical namespace for images
77 //@@todo (should get that from the api or inpage vars)
78
79 'enable_upload_tab':true, // if we want to enable an uploads tab:
80 'upload_api_target' : 'http://localhost/wiki_trunk/api.php' // can be local or the url of the upload api.
81 }
82
83 if(typeof wgServer == 'undefined')
84 wgServer = '';
85 if(typeof wgScriptPath == 'undefined')
86 wgScriptPath = '';
87 if(typeof stylepath == 'undefined')
88 stylepath = '';
89
90 /*
91 * base remoteSearch Driver interface
92 */
93 var remoteSearchDriver = function(iObj){
94 return this.init( iObj );
95 }
96 remoteSearchDriver.prototype = {
97 results_cleared:false,
98 //here we define the set of possible media content providers:
99 main_search_options:{
100 'selprovider':{
101 'title': 'Select Providers'
102 },
103 'advanced_search':{
104 'title': 'Advanced Options'
105 }
106 },
107 /*
108 * sets the default display item:
109 * can be any content_providers key or 'all'
110 */
111 disp_item : 'wiki_commons',
112 /** the default content providers list.
113 *
114 * (should be note that special tabs like "upload" and "combined" don't go into the content proviers list:
115 * @note do not use double underscore in content providers names (used for id lookup)
116 *
117 * @@todo we will want to load more per user-preference and per category lookup
118 */
119 content_providers:{
120 /*content_providers documentation:
121 * @@todo we should move the bulk of the configuration to each file
122 *
123
124 @enabled: whether the search provider can be selected
125 @checked: whether the search provider will show up as seleatable tab (todo: user prefrence)
126 @d: default: if the current cp should be displayed (only one should be the default)
127 @title: the title of the search provider
128 @desc: can use html... todo: need to localize
129 @api_url: the url to query against given the library type:
130 @lib: the search library to use corresponding to the
131 search object ie: 'mediaWiki' = new mediaWikiSearchSearch()
132 @tab_img: the tab image (if set to false use title text)
133 if === "ture" use standard location skin/images/{cp_id}_tab.png
134 if === string use as url for image
135
136 @linkback_icon default is: /wiki/skins/common/images/magnify-clip.png
137
138 //domain insert: two modes: simple config or domain list:
139 @local : if the content provider assets need to be imported or not.
140 @local_domains : sets of domains for which the content is local
141 //@@todo should query wgForeignFileRepos setting maybe interwikimap from the api
142 */
143 'this_wiki':{
144 'enabled': 1,
145 'checked': 1,
146 'title' : 'This Wiki',
147 'desc' : '(should be updated with the proper text) maybe import from some config value',
148 'api_url': ( wgServer && wgScriptPath )? wgServer + wgScriptPath+ '/api.php': null,
149 'lib' : 'mediaWiki',
150 'local' : true,
151 'tab_img': false
152 },
153 'wiki_commons':{
154 'enabled': 1,
155 'checked': 1,
156 'title' :'Wikimedia Commons',
157 'desc' : 'Wikimedia Commons is a media file repository making available public domain '+
158 'and freely-licensed educational media content (images, sound and video clips) to all.',
159 'homepage': 'http://commons.wikimedia.org/wiki/Main_Page',
160 'api_url':'http://commons.wikimedia.org/w/api.php',
161 'lib' :'mediaWiki',
162 'resource_prefix': 'WC_', //prefix on imported resources (not applicable if the repository is local)
163
164 //list all the domains where commons is local?
165 // probably should set this some other way by doing an api query
166 // or by seeding this config when calling the remote search?
167 'local_domains': ['wikimedia','wikipedia','wikibooks'],
168 //specific to wiki commons config:
169 'search_title':false, //disable title search
170 //set up default range limit
171 'offset' : 0,
172 'limit' : 30,
173 'tab_img':true
174 },
175 'archive_org':{
176 'enabled':1,
177 'checked':1,
178 'title' : 'Archive.org',
179 'desc' : 'The Internet Archive, a digital library of cultural artifacts',
180 'homepage':'http://www.archive.org/about/about.php',
181
182 'api_url':'http://homeserver7.us.archive.org:8983/solr/select',
183 'lib' : 'archiveOrg',
184 'local' : false,
185 'resource_prefix': 'AO_',
186 'tab_img':true
187 },
188 'metavid':{
189 'enabled':1,
190 'checked':1,
191 'title' :'Metavid.org',
192 'homepage':'http://metavid.org',
193 'desc' : 'Metavid hosts thousands of hours of US house and senate floor proceedings',
194 'api_url':'http://metavid.org/w/index.php?title=Special:MvExportSearch',
195 'lib' : 'metavid',
196 'local' :false, //if local set to true we can use local
197 'resource_prefix': 'MV_', //what prefix to use on imported resources
198
199 'local_domains': ['metavid'], // if the domain name contains metavid
200 // no need to import metavid content to metavid sites
201
202 'stream_import_key': 'mv_ogg_low_quality', // which stream to import, could be mv_ogg_high_quality
203 //or flash stream, see ROE xml for keys
204
205 'remote_embed_ext': false, //if running the remoteEmbed extension no need to copy local
206 //syntax will be [remoteEmbed:roe_url link title]
207 'tab_img':true
208 }
209 },
210 //define the licenses
211 // ... this will get complicated quick...
212 // (just look at complexity for creative commons without exessive "duplicate data")
213 // ie cc_by could be "by/3.0/us/" or "by/2.1/jp/" to infinitum...
214 // some complexity should be negated by license equivalances.
215
216 // but we will have to abstract into another class let content providers provide license urls
217 // and we have to clone the license object and allow local overrides
218
219 licenses:{
220 //for now only support creative commons type licenses
221 //used page: http://creativecommons.org/licenses/
222 'cc':{
223 'base_img_url':'http://upload.wikimedia.org/wikipedia/commons/thumb/',
224 'base_license_url': 'http://creativecommons.org/licenses/',
225 'licenses':{
226 'by': 'by/3.0/',
227 'by-sa': 'by-sa/3.0/',
228 'by-nc-nd': 'by-nc-nd/3.0/',
229 'by-nc': 'by-nc/3.0/',
230 'by-nd': 'by-nd/3.0/',
231 'by-nc-sa': 'by-nc-sa/3.0/',
232 'by-sa': 'by-nc/3.0',
233 'pd': 'publicdomain/'
234 },
235 'license_img':{
236 'by':{
237 'im':'1/11/Cc-by_new_white.svg/20px-Cc-by_new_white.svg.png'
238 },
239 'nc':{
240 'im':'2/2f/Cc-nc_white.svg/20px-Cc-nc_white.svg.png'
241 },
242 'nd':{
243 'im':'b/b3/Cc-nd_white.svg/20px-Cc-nd_white.svg.png'
244 },
245 'sa':{
246 'im':'d/df/Cc-sa_white.svg/20px-Cc-sa_white.svg.png'
247 },
248 'pd':{
249 'im':'5/51/Cc-pd-new_white.svg/20px-Cc-pd-new_white.svg.png'
250 }
251 }
252 }
253 },
254 /*
255 * getlicenseImgSet
256 * @param license_key the license key (ie "by-sa" or "by-nc-sa" etc)
257 */
258 getlicenseImgSet: function( licenseObj ){
259 //js_log('output images: '+ imgs);
260 return '<div class="rsd_license" title="'+ licenseObj.title + '" >' +
261 '<a target="_new" href="'+ licenseObj.lurl +'" ' +
262 'title="' + licenseObj.title + '">'+
263 licenseObj.img_html +
264 '</a>'+
265 '</div>';
266 },
267 /*
268 * getLicenceKeyFromKey
269 * @param license_key the key of the license (must be defined in: this.licenses.cc.licenses)
270 */
271 getLicenceFromKey:function( license_key , force_url){
272 if( typeof( this.licenses.cc.licenses[ license_key ]) == 'undefined')
273 return js_error('could not find:' + license_key);
274 //set the current license pointer:
275 var cl = this.licenses.cc;
276 var title = gM('mwe-cc_title');
277 var imgs = '';
278 var license_set = license_key.split('-');
279 for(var i=0;i < license_set.length; i++){
280 lkey = license_set[i];
281 title += ' ' + gM( 'mwe-cc_' + lkey + '_title');
282 imgs +='<img class="license_desc" width="20" src="' + cl.base_img_url +
283 cl.license_img[ lkey ].im + '">';
284 }
285 var url = (force_url) ? force_url : cl.base_license_url + cl.licenses[ license_key ];
286 return {
287 'title' : title,
288 'img_html' : imgs,
289 'key' : license_key,
290 'lurl' : url
291 };
292 },
293 /*
294 * getLicenceKeyFromUrl
295 * @param licence_url the url of the license
296 */
297 getLicenceFromUrl: function( license_url ){
298 //js_log("getLicenceFromUrl::" + license_url);
299 //first do a direct lookup check:
300 for(var i in this.licenses.cc.licenses){
301 var lkey = this.licenses.cc.licenses[i].split('/')[0];
302 //guess by url trim
303 if( parseUri(license_url).path.indexOf('/'+ lkey +'/') != -1){
304 return this.getLicenceFromKey( i , license_url);
305 }
306 }
307 //could not find it return mwe-unknown_license
308 return {
309 'title' : gM('mwe-unknown_license'),
310 'img_html' : '<span>' + gM('mwe-unknown_license') + '</span>',
311 'lurl' : license_url
312 };
313 },
314 //some default layout values:
315 thumb_width : 80,
316 image_edit_width : 400,
317 video_edit_width : 400,
318 insert_text_pos : 0, //insert at the start (will be overwritten by the user cursor pos)
319 result_display_mode : 'box', //box or list
320
321 cUpLoader : null,
322 cEdit : null,
323 dmodalCss : {},
324
325 init: function( iObj ){
326 var _this = this;
327 js_log('remoteSearchDriver:init');
328 for( var i in default_remote_search_options ) {
329 if( iObj[i]){
330 this[ i ] = iObj[i];
331 }else{
332 this[ i ] = default_remote_search_options[i];
333 }
334 }
335 //update the base text:
336 if(_this.target_textbox)
337 _this.getTexboxSelection();
338
339 //set up the content provider config:
340 if( this.cpconfig ){
341 for(var cpc in cpconfig){
342 for(var cinx in this.cpconfig[cpc]){
343 if( this.content_providers[cpc] )
344 this.content_providers[ cpc ][ cinx ] = this.cpconfig[cpc][ cinx];
345 }
346 }
347 }
348
349 //make sure the selected cp has an api to query against (if its a content_provider
350 if( this.content_providers[ this.disp_item ] &&
351 !this.content_providers[ this.disp_item ].api_url ){
352 for(var inx in this.content_providers){
353 if( this.content_providers[ inx ].api_url ){
354 this.disp_item = inx;
355 break;
356 }
357 }
358 }
359
360
361 //set up the default model config:
362 this.dmodalCss = {
363 'width':'auto',
364 'height':'auto',
365 'top' : this.modal_edge_padding,
366 'left' : this.modal_edge_padding,
367 'right' : this.modal_edge_padding,
368 'bottom': this.modal_edge_padding
369 }
370
371
372 //set up the target invocation:
373 if( $j(this.target_invocation).length==0 ){
374 js_log("RemoteSearchDriver:: no target invocation provided (will have to run your own doInitDisplay() )");
375 }else{
376 if(this.target_invocation){
377 $j(this.target_invocation).css('cursor','pointer').attr('title', gM('mwe-add_media_wizard')).click(function(){
378 _this.doInitDisplay();
379 });
380 }
381 }
382 },
383 doInitDisplay:function(){
384 var _this = this;
385 //setup the parent container:
386 this.init_modal();
387 //fill in the html:
388 this.init_interface_html();
389 //bind actions:
390 this.add_interface_bindings();
391
392 //update the target bining to just unhide the dialog:
393 $j(this.target_invocation).unbind().click(function(){
394 js_log("re-open");
395 //update the base text:
396 if( _this.target_textbox )
397 _this.getTexboxSelection();
398 //$j(_this.target_container).dialog("open");
399 $j(_this.target_container).parents('.ui-dialog').fadeIn('slow');
400 });
401 },
402 //gets the in and out points for insert position or grabs the selected text for search
403 getTexboxSelection:function(){
404 //update the caretPos
405 this.getCaretPos();
406
407 //if we have highlighted text use that as the query: (would be fun to add context menu once we have rich editor in-place)
408 if( this.caret_pos.selection )
409 this.default_query = this.caret_pos.selection
410
411 },
412 getCaretPos:function(){
413 this.caret_pos={};
414 var txtarea = $j(this.target_textbox).get(0);
415 var getTextCusorStartPos = function (o){
416 if (o.createTextRange) {
417 var r = document.selection.createRange().duplicate()
418 r.moveEnd('character', o.value.length)
419 if (r.text == '') return o.value.length
420 return o.value.lastIndexOf(r.text)
421 } else return o.selectionStart
422 }
423 var getTextCusorEndPos = function (o){
424 if (o.createTextRange) {
425 var r = document.selection.createRange().duplicate();
426 r.moveStart('character', -o.value.length);
427 return r.text.length;
428 } else{
429 return o.selectionEnd
430 }
431 }
432 this.caret_pos.s = getTextCusorStartPos( txtarea );
433 this.caret_pos.e = getTextCusorEndPos( txtarea );
434 this.caret_pos.text = txtarea.value;
435 if(this.caret_pos.s && this.caret_pos.e &&
436 (this.caret_pos.s != this.caret_pos.e))
437 this.caret_pos.selection = this.caret_pos.text.substring(this.caret_pos.s, this.caret_pos.e).replace(/ /g, '\xa0') || '\xa0';
438
439 js_log('got caret_pos:' + this.caret_pos.s);
440 //restore text value: (creating textRanges sometimes screws with the text content)
441 $j(this.target_textbox).val(this.caret_pos.text);
442 },
443 init_modal:function(){
444 js_log("init_modal");
445 var _this = this;
446 //add the parent target_container if not provided or missing
447 if(!_this.target_container || $j(_this.target_container).length==0){
448 $j('body').append('<div id="rsd_modal_target" style="position:absolute;top:30px;left:0px;bottom:45px;right:0px;" title="' + gM('mwe-add_media_wizard') + '" ></div>');
449 _this.target_container = '#rsd_modal_target';
450 //js_log('appended: #rsd_modal_target' + $j(_this.target_container).attr('id'));
451 //js_log('added target id:' + $j(_this.target_container).attr('id'));
452 //get layout
453 //layout = _this.getMaxModalLayout();
454 $j(_this.target_container).dialog({
455 bgiframe: true,
456 autoOpen: true,
457 modal: true,
458 buttons: {
459 '_': function() {
460 //just a place-holder
461 }
462 },
463 close: function() {
464 js_log('closed modal');
465 $j(this).parents('.ui-dialog').fadeOut('slow');
466 }
467 }).parent('.ui-dialog').css( _this.dmodalCss )
468 //@@bind on resize to disable css dialog to update dmodelCss
469 .bind('resizestart', function(event, ui) {
470 _this.dmodalCss = {};
471 $j(this).css({});
472 })
473 //bind on drag to remove preset style as well
474 .bind('dragstart', function(event, ui) {
475 _this.dmodalCss = {};
476 $j(this).css({});
477 });
478 //update the child position: (some of this should be pushed up-stream via dialog config options
479 $j(_this.target_container +'~ .ui-dialog-buttonpane').css({
480 'position':'absolute',
481 'left':'0px',
482 'right':'0px',
483 'bottom':'0px'
484 });
485 //re add cancel button
486 _this.cancelClipEditCB();
487 js_log('done setup of target_container: ' +
488 $j(_this.target_container +'~ .ui-dialog-buttonpane').length);
489
490
491 /*var resizeTimer = false;
492 $j(window).bind('resize', function() {
493 var adjustModal = function(){
494 var layout = _this.getMaxModalLayout();
495 //js_log("should adjust: h " + layout.h + ' width:' + layout.w);
496 $j(_this.target_container).dialog('option', 'width', layout.w);
497 $j(_this.target_container).dialog('option', 'height', layout.h);
498 }
499 if (resizeTimer) clearTimeout(resizeTimer);
500 var resizeTimer = setTimeout(adjustModal, 100);
501 });*/
502 }
503 },
504 getMaxModalLayout:function(border){
505 if(!border)
506 border = 50;
507 //js_log('setting h:' + (parseInt( $j(document).height() ) - parseInt(border*2)) + ' from:' + $j(document).height() );
508 return {
509 'h': parseInt( $j(document).height() ) - parseInt(border*4),
510 'w': parseInt( $j(document).width() ) - parseInt(border*2),
511 'r': border,
512 't': border
513 }
514 },
515 //sets up the initial html interface
516 init_interface_html:function(){
517 js_log('init_interface_html');
518 var _this = this;
519 var dq = (this.default_query)? this.default_query : '';
520 js_log('f::init_interface_html');
521
522 var o = '<div class="rsd_control_container" style="width:100%">' +
523 '<form id="rsd_form" action="javascript:return false;" method="GET">'+
524 '<input class="ui-widget-content ui-corner-all" type="text" tabindex="1" value="' + dq + '" maxlength="512" id="rsd_q" name="rsd_q" '+
525 'size="20" autocomplete="off"/> '+
526 $j.btnHtml( gM('mwe-media_search'), 'rms_search_button', 'search') +
527 '</form>';
528 //close up the control container:
529 o+='</div>';
530
531 //search provider tabs based on "checked" and "enabled" and "combined tab"
532 o+='<div id="rsd_results_container" style="top:0px;bottom:0px;left:0px;right:0px;"></div>';
533 $j(this.target_container).html( o );
534
535 //add simple styles:
536 $j(this.target_container + ' .rms_search_button').btnBind().click(function(){
537 _this.runSearch();
538 });
539
540 //draw the tabs:
541 this.drawTabs();
542 //run the default search:
543 if( this.default_query )
544 this.runSearch();
545 },
546 add_interface_bindings:function(){
547 var _this = this;
548 js_log("f:add_interface_bindings:");
549
550
551 $j('#mso_selprovider,#mso_selprovider_close').unbind().click(function(){
552 if($j('#rsd_options_bar:hidden').length !=0 ){
553 $j('#rsd_options_bar').animate({
554 'height':'110px',
555 'opacity':1
556 }, "normal");
557 }else{
558 $j('#rsd_options_bar').animate({
559 'height':'0px',
560 'opacity':0
561 }, "normal", function(){
562 $j(this).hide();
563 });
564 }
565 });
566 //set form bindings
567 $j('#rsd_form').unbind().submit(function(){
568 _this.runSearch();
569 //don't submit the form
570 return false;
571 });
572 },
573 doUploadInteface:function(){
574 js_log("doUploadInteface::");
575 var _this = this;
576 //set it to loading:
577 mv_set_loading('#tab-upload');
578 //do things async to keep interface snapy
579 setTimeout(function(){
580 //do config variable reality checks:
581 if( _this.upload_api_target == 'local' ){
582 if( ! _this.local_wiki_api_url ){
583 $j('#tab-upload').html( gM( 'rsd_config_error', 'missing_local_api_url' ) );
584 return false;
585 }else{
586 _this.upload_api_target = _this.local_wiki_api_url;
587 }
588 }
589 //make sure we have a url for the upload target:
590 if( parseUri( _this.upload_api_target ).host == _this.upload_api_target ){
591 $j('#tab-upload').html( gM('rsd_config_error', 'bad_api_url') );
592 return false;
593 }
594 //output the form
595 //set the form action based on domain:
596 if( parseUri( document.URL ).host == parseUri( _this.upload_api_target ).host ){
597 mvJsLoader.doLoad(['$j.fn.simpleUploadForm'],function(){
598
599 //get extened info about the file
600 var cp = _this.content_providers['this_wiki'];
601 //check for "this_wiki" enabled
602 if(!cp.enabled){
603 $j('#tab-upload').html('error this_wiki not enabled (can\'t get uploaded file info)');
604 return false;
605 }
606
607 //load this_wiki search system to grab the rObj
608 _this.loadSearchLib(cp, function(){
609 //do basic layout form on left upload "bin" on right
610 $j('#tab-upload').html('<table cellspacing="10">' +
611 '<tr>' +
612 '<td valign="top" style="width:350px;">' +
613 '<h4>' + gM('mwe-upload_a_file') + '</h4>' +
614 '<div id="upload_form">' +
615 mv_get_loading_img() +
616 '</div>' +
617 '</td>' +
618 '<td valign="top" id="upload_bin_cnt">' +
619 '<h4>' + gM('mwe-your_recent_uploads') + '</h4>' +
620 '<div id="upload_bin">' +
621 mv_get_loading_img() +
622 '</div>'+
623 '</td>' +
624 '</tr>' +
625 '</table>');
626
627
628 //fill in the user page:
629 if(typeof wgUserName != 'undefined' && wgUserName){
630 //load the upload bin with anything the current user has uploaded
631 cp.sObj.getUserRecentUploads( wgUserName, function(){
632 _this.drawOutputResults();
633 });
634 }else{
635 $j('#upload_bin_cnt').empty();
636 }
637
638 //deal with the api form upload form directly:
639 $j('#upload_form').simpleUploadForm({
640 "api_target" : _this.upload_api_target ,
641 "ondone_cb" : function( resultData ){
642 var wTitle = resultData['wpDestFile'];
643 //add a loading div
644 $j( _this.target_container ).append('<div id="temp_edit_loader" '+
645 'style="position:absolute;top:0px;left:0px;bottom:5px;right:4px;background-color:#FFF;">' +
646 mv_get_loading_img('position:absolute;top:30px;left:30px') +
647 '</div>');
648 cp.sObj.addByTitle( wTitle, function( rObj ){
649 $j( _this.target_container ).find('#temp_edit_loader').remove();
650 //redraw (with added result if new)
651 _this.drawOutputResults();
652 //pull up recource editor:
653 _this.resourceEdit( rObj, $j('#res_upload_' + rObj.id).get(0) );
654 });
655 //return false to close progress window:
656 return false;
657 }
658 })
659 });
660 });
661 }else{
662 //setup the proxy
663 js_log('do proxy:: ' + parseUri( _this.upload_api_target ).host);
664 $j('#tab-upload').html('proxy upload not yet ready');
665 }
666 },1);
667 },
668 runSearch: function(){
669 js_log("f:runSearch::" + this.disp_item);
670 //draw_direct_flag
671 var draw_direct_flag = true;
672 if( !this.content_providers[this.disp_item] ){
673 //check if its the special upload tab case:
674 if( this.disp_item == 'upload'){
675 this.doUploadInteface();
676 }else{
677 js_log("can't run search for:" + this.disp_item);
678 }
679 return false;
680 }
681 cp = this.content_providers[this.disp_item];
682
683 //check if we need to update:
684 if( typeof cp.sObj != 'undefined' ){
685 if(cp.sObj.last_query == $j('#rsd_q').val() && cp.sObj.last_offset == cp.offset){
686 js_log('last query is: ' + cp.sObj.last_query + ' matches: ' + $j('#rsd_q').val() );
687 }else{
688 js_log('last query is: ' + cp.sObj.last_query + ' not match: ' + $j('#rsd_q').val() );
689 draw_direct_flag = false;
690 }
691 }else{
692 draw_direct_flag = false;
693 }
694 if( !draw_direct_flag ){
695 //set the content to loading while we do the search:
696 $j('#tab-' + this.disp_item).html( mv_get_loading_img() );
697
698 //make sure the search library is loaded and issue the search request
699 this.getLibSearchResults( cp );
700 }
701 },
702 //issue a api request & cache the result
703 //this check can be avoided by setting the this.import_url_mode = 'api' | 'form' | insted of 'autodetect' or 'none'
704 checkForCopyURLSupport:function ( callback ){
705 var _this = this;
706 js_log('checkForCopyURLSupport:: ');
707 //see if we already have the import mode:
708 if( this.import_url_mode != 'autodetect'){
709 js_log('import mode: ' + _this.import_url_mode);
710 callback();
711 }
712 //if we don't have the local wiki api defined we can't auto-detect use "link"
713 if(!_this.local_wiki_api_url){
714 js_log('import mode: remote link (no import_wiki_api_url)');
715 _this.import_url_mode = 'remote_link';
716 callback();
717 }
718 if( this.import_url_mode == 'autodetect' ){
719 do_api_req( {
720 'data': { 'action':'paraminfo', 'modules':'upload' },
721 'url': _this.local_wiki_api_url
722 }, function(data){
723 if( typeof data.paraminfo.modules[0].classname == 'undefined'){
724 //@@todo would be nice if API permission on: action=query&meta=userinfo&uiprop=rights
725 // upload_by_url property reflected if $wgAllowCopyUploads config value .. oh well.
726 $j.ajax({
727 type: "GET",
728 dataType: 'html',
729 url: wgArticlePath.replace( '$1', 'Special:Upload' ), //@@todo may have problems in localized special pages
730 //(could hit meta=siteinfo & specialpagealiases )
731 // but might be overkill for now cuz we want to switch to new-upload branch soon.
732 success: function( form_html ){
733 if( form_html.indexOf( 'wpUploadFileURL' ) != -1){
734 _this.import_url_mode = 'form';
735 }else{
736 _this.import_url_mode = 'none';
737 }
738 js_log('import mode: ' + _this.import_url_mode);
739 callback();
740 },
741 error: function(){
742 js_log('error in getting Special:Upload page');
743 _this.import_url_mode = 'none';
744
745 js_log('import mode: ' + _this.import_url_mode);
746 callback();
747 }
748 });
749 }else{
750 for( var i in data.paraminfo.modules[0].parameters ){
751 var pname = data.paraminfo.modules[0].parameters[i].name;
752 if( pname == 'url' ){
753 js_log( 'Autodetect Upload Mode: api: copy by url:: ' );
754 //check permission too:
755 _this.checkForCopyURLPermission(function( canCopyUrl ){
756 if(canCopyUrl){
757 _this.import_url_mode = 'api';
758 js_log('import mode: ' + _this.import_url_mode);
759 callback();
760 }else{
761 _this.import_url_mode = 'none';
762 js_log('import mode: ' + _this.import_url_mode);
763 callback();
764 }
765 });
766 break;
767 }
768 }
769 }
770 });
771 }
772 },
773 /*
774 * checkForCopyURLPermission:
775 * not really nessesary the api request to upload will return apopprirate error if the user lacks permission. or $wgAllowCopyUploads is set to false
776 * (use this function if we want to issue a warning up front)
777 */
778 checkForCopyURLPermission:function( callback ){
779 var _this = this;
780 //do api check:
781 do_api_req( {
782 'data':{ 'action' : 'query', 'meta' : 'userinfo', 'uiprop' : 'rights' },
783 'url': _this.local_wiki_api_url,
784 'userinfo' : true
785 }, function(data){
786 for( var i in data.query.userinfo.rights){
787 var right = data.query.userinfo.rights[i];
788 //js_log('checking: ' + right ) ;
789 if(right == 'upload_by_url'){
790 callback( true );
791 return true; //break out of the function
792 }
793 }
794 callback( false );
795 });
796 },
797 getLibSearchResults:function( cp ){
798 var _this = this;
799
800 //first check if we should even run the search at all (can we import / insert into the page? )
801 if( !this.checkRepoLocal( cp ) && this.import_url_mode == 'autodetect' ){
802 //cp is not local check if we can support the import mode:
803 this.checkForCopyURLSupport( function(){
804 _this.getLibSearchResults( cp );
805 });
806 return false;
807 }else if( !this.checkRepoLocal( cp ) && this.import_url_mode == 'none'){
808 if( this.disp_item == 'combined' ){
809 //combined results are harder to error handle just ignore that repo
810 cp.sObj.loading = false;
811 }else{
812 $j('#tab-' + this.disp_item).html( '<div style="padding:10px">'+ gM('mwe-no_import_by_url') +'</div>');
813 }
814 return false;
815 }
816 _this.loadSearchLib(cp, function(){
817 //do search
818 cp.sObj.getSearchResults();
819 _this.checkResultsDone();
820 });
821 },
822 loadSearchLib:function(cp, callback){
823 var _this = this;
824 //set up the library req:
825 mvJsLoader.doLoad( [
826 'baseRemoteSearch',
827 cp.lib +'Search'
828 ], function(){
829 js_log("loaded lib:: " + cp.lib );
830 //else we need to run the search:
831 var iObj = {'cp':cp, 'rsd':_this};
832 eval('cp.sObj = new '+cp.lib+'Search( iObj );');
833 if(!cp.sObj){
834 js_log('Error: could not find search lib for ' + cp_id);
835 return false;
836 }
837
838 //inherit defaults if not set:
839 cp.limit = (cp.limit) ? cp.limit : cp.sObj.limit;
840 cp.offset = (cp.offset) ? cp.offset : cp.sObj.offset;
841 callback();
842 });
843 },
844 /* check for all the results to finish */
845 checkResultsDone: function(){
846 //js_log('rsd:checkResultsDone');
847 var _this = this;
848 var loading_done = true;
849
850 for(var cp_id in this.content_providers){
851 var cp = this.content_providers[ cp_id ];
852 if(typeof cp['sObj'] != 'undefined'){
853 if( cp.sObj.loading )
854 loading_done=false;
855 }
856 }
857 if( loading_done ){
858 this.drawOutputResults();
859 }else{
860 //make sure the instance name is up-to-date refrence to _this;
861 eval( _this.instance_name + ' = _this');
862 setTimeout( _this.instance_name + '.checkResultsDone()', 50);
863 }
864 },
865 drawTabs: function(){
866 var _this = this;
867 //add the tabs to the rsd_results container:
868 var o='<div id="rsd_tabs_container" style="width:100%;">';
869 var selected_tab = 0;
870 var inx =0;
871 o+= '<ul>';
872 var tabc = '';
873 for(var cp_id in this.content_providers){
874 var cp = this.content_providers[cp_id];
875 if( cp.enabled && cp.checked && cp.api_url){
876 //add selected default if set
877 if( this.disp_item == cp_id)
878 selected_tab=inx;
879
880 o+='<li class="rsd_cp_tab">';
881 o+='<a id="rsd_tab_' + cp_id + '" href="#tab-' + cp_id + '">';
882 if(cp.tab_img === true){
883 o+='<img alt="' + cp.title +'" src="' + mv_skin_img_path + 'remote_cp/' + cp_id + '_tab.png">';
884 }else{
885 o+= cp.title;
886 }
887 o+='</a>';
888 o+='</li>';
889 inx++;
890 }
891 tabc+='<div id="tab-'+ cp_id +'" class="rsd_results"/>';
892
893 }
894 //do an upload tab if enabled:
895 if( this.enable_upload_tab ){
896 o+='<li class="rsd_cp_tab" ><a id="rsd_tab_upload" href="#tab-upload">' + gM('mwe-upload_tab') + '</a></li>';
897 tabc+='<div id="tab-upload" />';
898 if(this.disp_item == 'upload')
899 selected_tab = inx++;
900 }
901 o+='</ul>';
902 //output the tab content containers:
903 o+=tabc;
904 o+='</div>'; //close tab container
905
906 //output the respective results holders
907 $j('#rsd_results_container').html(o);
908 //setup bindings for tabs make them sortable: (@@todo remember order)
909 js_log('selected tab is: ' + selected_tab);
910 $j("#rsd_tabs_container").tabs({
911 selected:selected_tab,
912 select: function(event, ui) {
913 _this.selectTab( $j(ui.tab).attr('id').replace('rsd_tab_', '') );
914 }
915 //add sorting
916 }).find(".ui-tabs-nav").sortable({axis:'x'});
917
918 /*$j('.rsd_cp_tab').click(function(){
919 _this.selectTab( $j(this).attr('id').replace(/rsd_tab_/, '') );
920 });*/
921
922 //setup key binding (no longer nessesary tabs provide this functionality)
923 /*$j().keyup(function(e){
924 js_log('keyup on : ' +e.which );
925 //if escape pressed clear the interface:
926 if(e.which == 27)
927 _this.closeAll();
928 });*/
929
930 },
931 //resource title
932 getResourceFromTitle:function( rTitle , callback){
933 var _this = this;
934 reqObj={
935 'action':'query',
936 'titles': _this.cFileNS + ':' + rTitle
937 };
938 do_api_req( {
939 'data':reqObj,
940 'url':this.local_wiki_api_url
941 }, function(data){
942 //@@todo propogate the rObj
943 var rObj = {};
944 }
945 );
946 },
947 //@@todo we could load the id with the content provider id to find the object faster...
948 getResourceFromId:function( rid ){
949 js_log('getResourceFromId:' + rid );
950 //strip out /res/ if preset:
951 rid = rid.replace(/res_/, '');
952 //js_log("looking at: " + rid);
953 p = rid.split('__');
954 var cp_id = p[0];
955 var rid = p[1];
956
957 if(cp_id == 'upload')
958 cp_id = 'this_wiki';
959
960 var cp = this.content_providers[cp_id];
961 if(cp && cp['sObj'] && cp.sObj.resultsObj[rid]){
962 return cp.sObj.resultsObj[rid];
963 }
964 js_log("ERROR: could not find " + rid);
965 return false;
966 },
967 drawOutputResults: function(){
968 js_log('f:drawOutputResults::' + this.disp_item);
969 var _this = this;
970 var o='';
971
972 var cp_id = this.disp_item;
973 var tab_target = '';
974 if(this.disp_item == 'upload'){
975 tab_target = '#upload_bin';
976 var cp = this.content_providers['this_wiki'];
977 }else{
978 var cp = this.content_providers[this.disp_item];
979 tab_target = '#tab-' + cp_id;
980 }
981 //empty the existing results:
982 $j(tab_target).empty();
983
984 //output the results bar / controls
985 _this.setResultBarControl();
986
987 var drawResultCount =0;
988
989 //output all the results for the current disp_item
990 if( typeof cp['sObj'] != 'undefined' ){
991 $j.each(cp.sObj.resultsObj, function(rInx, rItem){
992 if( _this.result_display_mode == 'box' ){
993 o+='<div id="mv_result_' + rInx + '" class="mv_clip_box_result" style="width:' +
994 _this.thumb_width + 'px;height:'+ (_this.thumb_width-20) +'px;position:relative;">';
995 //check for missing poster types for audio
996 if( rItem.mime=='audio/ogg' && !rItem.poster ){
997 rItem.poster = mv_skin_img_path + 'sound_music_icon-80.png';
998 }
999 //get a thumb with proper resolution transform if possible:
1000 o+='<img title="'+rItem.title+'" class="rsd_res_item" id="res_' + cp_id + '__' + rInx +
1001 '" style="width:' + _this.thumb_width + 'px;" src="' +
1002 cp.sObj.getImageTransform( rItem, {'width':_this.thumb_width } )
1003 + '">';
1004 //add a linkback to resource page in upper right:
1005 if( rItem.link )
1006 o+='<a target="_new" style="position:absolute;top:0px;right:0px" title="' +
1007 gM('mwe-resource_description_page') +
1008 '" href="' + rItem.link + '"><img src="http://upload.wikimedia.org/wikipedia/commons/6/6b/Magnify-clip.png"></a>';
1009 //add license icons if present
1010 if( rItem.license )
1011 o+= _this.getlicenseImgSet( rItem.license );
1012 o+='</div>';
1013 }else if(_this.result_display_mode == 'list'){
1014 o+='<div id="mv_result_' + rInx + '" class="mv_clip_list_result" style="width:90%">';
1015 o+='<img title="'+rItem.title+'" class="rsd_res_item" id="res_' + cp_id + '_' + rInx +'" style="float:left;width:' +
1016 _this.thumb_width + 'px;" src="' +
1017 cp.sObj.getImageTransform( rItem, {'width':_this.thumb_width } )
1018 + '">';
1019 //add license icons if present
1020 if( rItem.license )
1021 o+= _this.getlicenseImgSet( rItem.license );
1022
1023 o+= rItem.desc ;
1024 o+='<div style="clear:both" />';
1025 o+='</div>';
1026 }
1027 drawResultCount++;
1028 });
1029 js_log('append to: ' + '#tab-' + cp_id);
1030 //put in the tab output (plus clear the output)
1031 $j(tab_target).append( o + '<div style="clear:both"/>');
1032 }
1033
1034 js_log( ' drawResultCount :: ' + drawResultCount + ' append: ' + $j('#rsd_q').val() );
1035
1036 //remove any old search res
1037 $j('#rsd_no_search_res').remove();
1038 if( drawResultCount == 0 )
1039 $j('#tab-' + cp_id).append( '<span style="padding:10px">' + gM( 'rsd_no_results', $j('#rsd_q').val() ) + '</span>');
1040
1041 this.addResultBindings();
1042 },
1043 addResultBindings:function(){
1044 var _this = this;
1045 $j('.mv_clip_'+_this.result_display_mode+'_result').hover(function(){
1046 $j(this).addClass('mv_clip_'+_this.result_display_mode+'_result_over');
1047 //also set the animated image if avaliable
1048 var res_id = $j(this).children('.rsd_res_item').attr('id');
1049 var rObj = _this.getResourceFromId( res_id );
1050 if( rObj.poster_ani )
1051 $j('#' + res_id ).attr('src', rObj.poster_ani);
1052 },function(){
1053 $j(this).removeClass('mv_clip_'+_this.result_display_mode+'_result_over');
1054 var res_id = $j(this).children('.rsd_res_item').attr('id');
1055 var rObj = _this.getResourceFromId( res_id );
1056 //restore the original (non animated)
1057 if( rObj.poster_ani )
1058 $j('#' + res_id ).attr('src', rObj.poster);
1059 });
1060 //resource click action: (bring up the resource editor)
1061 $j('.rsd_res_item').unbind().click(function(){
1062 var rObj = _this.getResourceFromId( $j(this).attr("id") );
1063 _this.resourceEdit( rObj, this );
1064 });
1065 },
1066 addResourceEditLoader:function(maxWidth, overflow_style){
1067 var _this = this;
1068 if(!maxWidth)maxWidth=400;
1069 if(!overflow_style)overflow_style='overflow:auto;';
1070 //remove any old instance:
1071 $j( _this.target_container ).find('#rsd_resource_edit').remove();
1072 //add the edit layout window with loading place holders
1073 $j( _this.target_container ).append('<div id="rsd_resource_edit" '+
1074 'style="position:absolute;top:0px;left:0px;bottom:5px;right:4px;background-color:#FFF;">' +
1075 '<div id="clip_edit_disp" style="position:absolute;' + overflow_style + 'width:100%;height:100%;padding:5px;'+
1076 'width:' + (maxWidth) + 'px;" >' +
1077 mv_get_loading_img('position:absolute;top:30px;left:30px') +
1078 '</div>'+
1079 '<div id="clip_edit_ctrl" class="ui-widget ui-widget-content ui-corner-all" style="position:absolute;'+
1080 'left:' + ( maxWidth + 10 ) +'px;top:5px;bottom:10px;right:0px;overflow:auto;padding:5px;">'+
1081 mv_get_loading_img() +
1082 '</div>'+
1083 '</div>');
1084 },
1085 resourceEdit:function( rObj, rsdElement){
1086 js_log('f:resourceEdit:' + rObj.title);
1087 var _this = this;
1088 //remove any existing resource edit interface:
1089 $j('#rsd_resource_edit').remove();
1090 //set the media type:
1091 if(rObj.mime.indexOf('image')!=-1){
1092 //set width to default image_edit_width
1093 var maxWidth = _this.image_edit_width;
1094 var mediaType = 'image';
1095 }else if(rObj.mime.indexOf('audio')!=-1){
1096 var maxWidth = _this.video_edit_width;
1097 var mediaType = 'audio';
1098 }else{
1099 //set to default video size:
1100 var maxWidth = _this.video_edit_width;
1101 var mediaType = 'video';
1102 }
1103 //so that transcripts show ontop
1104 var overflow_style = ( mediaType =='video' )?'':'overflow:auto;';
1105 //append to the top level of model window:
1106 _this.addResourceEditLoader(maxWidth, overflow_style);
1107 //update add media wizard title:
1108 $j( _this.target_container ).dialog( 'option', 'title', gM('mwe-add_media_wizard')+': '+ gM('rsd_resource_edit', rObj.title ) );
1109 js_log('did append to: '+ _this.target_container );
1110
1111 $j('#rsd_resource_edit').css('opacity',0);
1112
1113 $j('#rsd_edit_img').remove();//remove any existing rsd_edit_img
1114
1115 //left side holds the image right size the controls /
1116 $j(rsdElement).clone().attr('id', 'rsd_edit_img').appendTo('#clip_edit_disp').css({
1117 'position':'absolute',
1118 'top':'40%',
1119 'left':'20%',
1120 'cursor':'default',
1121 'opacity':0
1122 });
1123
1124
1125 //try and keep aspect ratio for the thumbnail that we clicked:
1126 var tRatio = $j('#rsd_edit_img').width() / $j('#rsd_edit_img').height() ;
1127 if( ! tRatio )
1128 var tRatio = 1; //set ratio to 1 if the width of the thumbnail can't be found for some reason
1129
1130 js_log('set from ' + $j('#rsd_edit_img').width()+'x'+ $j('#rsd_edit_img').height() + ' to init thumbimage to ' + maxWidth + ' x ' + parseInt( tRatio * maxWidth) );
1131 //scale up image and to swap with high res version
1132 $j('#rsd_edit_img').animate({
1133 'opacity':1,
1134 'top':'5px',
1135 'left':'5px',
1136 'width': maxWidth + 'px',
1137 'height': parseInt( tRatio * maxWidth) + 'px'
1138 }, "slow"); // do it slow to give it a chance to finish loading the HQ version
1139
1140 if( mediaType == 'image' ){
1141 _this.loadHQImg(rObj, {'width':maxWidth}, 'rsd_edit_img', function(){
1142 $j('.mv_loading_img').remove();
1143 });
1144 }
1145 //also fade in the container:
1146 $j('#rsd_resource_edit').animate({
1147 'opacity':1,
1148 'background-color':'#FFF',
1149 'z-index':99
1150 });
1151 js_log('do load the media editor:');
1152 //do load the media Editor
1153 _this.doMediaEdit( rObj , mediaType );
1154 },
1155 loadHQImg:function(rObj, size, target_img_id, callback){
1156 //get the HQ image url:
1157 rObj.pSobj.getImageObj( rObj, size, function( imObj ){
1158 rObj['edit_url'] = imObj.url;
1159
1160 js_log("edit url: " + rObj.edit_url);
1161 //update the rObj
1162 rObj['width'] = imObj.width;
1163 rObj['height'] = imObj.height;
1164
1165 //see if we need to animate some transition
1166 if( size.width != imObj.width ){
1167 js_log('loadHQImg:size mismatch: ' + size.width + ' != ' + imObj.width );
1168 //set the target id to the new size:
1169 $j('#'+target_img_id).animate( {
1170 'width':imObj.width + 'px',
1171 'height':imObj.height + 'px'
1172 });
1173 }else{
1174 js_log('using req size: ' + imObj.width + 'x' + imObj.height);
1175 $j('#'+target_img_id).animate( {'width':imObj.width+'px', 'height' : imObj.height + 'px'});
1176 }
1177 //don't swap it in until its loaded:
1178 var img = new Image();
1179 // load the image image:
1180 $j(img).load(function () {
1181 $j('#'+target_img_id).attr('src', rObj.edit_url );
1182 //let the caller know we are done and what size we ended up with:
1183 callback();
1184 }).error(function () {
1185 js_log("Error with: " + rObj.edit_url);
1186 }).attr('src', rObj.edit_url);
1187 });
1188 },
1189 cancelClipEditCB:function(){
1190 var _this = this;
1191 var b_target = _this.target_container + '~ .ui-dialog-buttonpane';
1192 $j('#rsd_resource_edit').remove();
1193 //restore the resource container:
1194 $j('#rsd_results_container').show();
1195 //restore the title:
1196 $j( _this.target_container ).dialog( 'option', 'title', gM('mwe-add_media_wizard'));
1197 js_log("should update: " + b_target + ' with: cancel');
1198 //restore the buttons:
1199 $j(b_target).html( $j.btnHtml( 'Cancel' , 'mv_cancel_rsd', 'close'))
1200 .children('.mv_cancel_rsd')
1201 .btnBind()
1202 .click(function(){
1203 $j( _this.target_container).dialog('close');
1204 })
1205
1206 },
1207 /*set-up the control actions for clipEdit with relevent callbacks */
1208 getClipEditControlActions:function( cp ){
1209 var _this = this;
1210 var cConf= {};
1211
1212 cConf['insert'] = function(rObj){
1213 _this.insertResource(rObj);
1214 }
1215 //if not directly inserting the resource is support a preview option:
1216 if( _this.import_url_mode != 'remote_link'){
1217 cConf['preview'] = function(rObj){
1218 _this.previewResource( rObj )
1219 };
1220 }
1221 cConf['cancel'] = function(){
1222 _this.cancelClipEditCB()
1223 }
1224 return cConf;
1225 },
1226 //loads the media editor:
1227 doMediaEdit:function( rObj , mediaType){
1228 var _this = this;
1229 var cp = rObj.pSobj.cp;
1230 var mvClipInit = {
1231 'rObj':rObj, //the resource object
1232 'parent_ct' : 'rsd_modal_target',
1233 'clip_disp_ct' : 'clip_edit_disp',
1234 'control_ct' : 'clip_edit_ctrl',
1235 'media_type' : mediaType,
1236 'p_rsdObj' : _this,
1237 'controlActionsCb' : _this.getClipEditControlActions( cp )
1238 };
1239
1240 var clibs = ['mvClipEdit'];
1241 if( mediaType == 'image'){
1242 //display the mvClipEdit obj once we are done loading:
1243 mvJsLoader.doLoad( clibs, function(){
1244 //run the image clip tools
1245 _this.cEdit = new mvClipEdit( mvClipInit );
1246 });
1247 }
1248 if( mediaType == 'video' || mediaType == 'audio'){
1249 //get any additonal embedding helper meta data prior to doing the acutal embed
1250 // normally this meta should be provided in the search result (but archive.org has a seperate query for more meida meta)
1251 rObj.pSobj.getEmbedTimeMeta( rObj, function(){
1252 //make sure we have the embedVideo libs:
1253 mvJsLoader.embedVideoCheck( function(){
1254 js_log('append html: ' + rObj.pSobj.getEmbedHTML( rObj, {id:'embed_vid'}) );
1255 $j('#clip_edit_disp').html(
1256 rObj.pSobj.getEmbedHTML( rObj, {id:'embed_vid'})
1257 );
1258 //rewrite by id
1259 rewrite_by_id('embed_vid',function(){
1260 //grab any information that we got from the ROE xml or parsed from the media file
1261 rObj.pSobj.getEmbedObjParsedInfo( rObj, 'embed_vid' );
1262 //add the re-sizable to the doLoad request:
1263 clibs.push( '$j.ui.resizable');
1264 clibs.push( '$j.fn.hoverIntent');
1265 mvJsLoader.doLoad(clibs, function(){
1266 //make sure the rsd_edit_img is hidden:
1267 $j('#rsd_edit_img').remove();
1268 //run the image clip tools
1269 _this.cEdit = new mvClipEdit( mvClipInit );
1270 });
1271 });
1272 });
1273 });
1274 }
1275 },
1276 checkRepoLocal:function( cp ){
1277 if( cp.local ){
1278 return true;
1279 }else{
1280 //check if we can embed the content locally per a domain name check:
1281 var local_host = parseUri( this.local_wiki_api_url ).host;
1282 if( cp.local_domains ) {
1283 for(var i=0;i < cp.local_domains.length; i++){
1284 var ld = cp.local_domains[i];
1285 if( local_host.indexOf( ld ) != -1)
1286 return true;
1287 }
1288 }
1289 return false;
1290 }
1291 },
1292 checkImportResource:function( rObj, cir_callback){
1293 //@@todo get the localized File/Image namespace name or do a general {NS}:Title
1294 var cp = rObj.pSobj.cp;
1295 var _this = this;
1296
1297 //update base target_resource_title:
1298 rObj.target_resource_title = rObj.titleKey.replace(/File:|Image:/,'')
1299
1300 //check if local repository
1301 //or if import mode if just "linking" (we should alaredy have the 'url'
1302
1303 if( this.checkRepoLocal( cp ) || this.import_url_mode == 'remote_link'){
1304 //local repo jump directly to check Import Resource callback:
1305 cir_callback( rObj );
1306 }else{
1307 //update target_resource_title with resource repository prefix:
1308 rObj.target_resource_title = cp.resource_prefix + rObj.target_resource_title;
1309
1310 //check if the resource is not already on this wiki
1311 reqObj={
1312 'action':'query',
1313 'titles': _this.cFileNS + ':' + rObj.target_resource_title,
1314 'prop' : 'imageinfo',
1315 'iiprop' : 'url',
1316 'iiurlwidth': '400'
1317 };
1318
1319 do_api_req( {
1320 'data':reqObj,
1321 'url':this.local_wiki_api_url
1322 }, function(data){
1323 var found_title = false;
1324 for(var i in data.query.pages){
1325 if( i != '-1' && i != '-2' ){
1326 js_log('found title: ' + i + ':' + data.query.pages[i]['title']);
1327 found_title=data.query.pages[i]['title'];
1328 //update to local src
1329 rObj.local_src = data.query.pages[i]['imageinfo'][0].url;
1330 //@@todo maybe update poster too?
1331 rObj.local_poster = data.query.pages[i]['imageinfo'][0].thumburl;
1332 }
1333 }
1334 if( found_title ){
1335 js_log("checkImportResource:found title:" + found_title);
1336 //resource is already present (or resource with same name is already present)
1337 rObj.target_resource_title = found_title.replace(/File:|Image:/,'');
1338 cir_callback( rObj );
1339 }else{
1340 js_log("resource not present: update:"+ _this.cFileNS + ':' + rObj.target_resource_title);
1341
1342 //update the rObj with import info
1343 rObj.pSobj.updateDataForImport( rObj );
1344
1345 //setup the resource description from resource description:
1346 var wt = '{{Information '+"\n";
1347
1348 if( rObj.desc ){
1349 wt += '|Description= ' + rObj.desc + "\n";
1350 }else{
1351 wt += '|Description= ' + gM('mwe-missing_desc_see_source', rObj.link ) + "\n";
1352 }
1353
1354 //output search specific info
1355 wt+='|Source=' + rObj.pSobj.getImportResourceDescWiki( rObj ) + "\n";
1356
1357 if( rObj.author )
1358 wt+='|Author=' + rObj.author +"\n";
1359
1360 if( rObj.date )
1361 wt+='|Date=' + rObj.date +"\n";
1362
1363 //add the Permision info:
1364 wt+='|Permission=' + rObj.pSobj.getPermissionWikiTag( rObj ) +"\n";
1365
1366 if( rObj.other_versions )
1367 wt+='|other_versions=' + rObj.other_versions + "\n";
1368
1369 wt+='}}';
1370
1371 //get any extra categories or helpful links
1372 wt+= rObj.pSobj.getExtraResourceDescWiki( rObj );
1373
1374
1375 $j('#rsd_resource_import').remove();//remove any old resource imports
1376
1377 //@@ show user dialog to import the resource
1378 $j( _this.target_container ).append('<div id="rsd_resource_import" '+
1379 'class="ui-state-highlight ui-widget-content ui-state-error" ' +
1380 'style="position:absolute;top:50px;left:50px;right:50px;bottom:50px;z-index:5">' +
1381 '<h3 style="color:red">Resource: <span style="color:black">' + rObj.title + '</span> needs to be imported</h3>'+
1382 '<div id="rsd_preview_import_container" style="position:absolute;width:50%;bottom:0px;left:0px;overflow:auto;top:30px;">' +
1383 rObj.pSobj.getEmbedHTML( rObj, {'id': _this.target_container + '_rsd_pv_vid', 'max_height':'200','only_poster':true} )+ //get embedHTML with small thumb:
1384 '<br style="clear both">'+
1385 '<strong>'+gM('mwe-resource_page_desc') +'</strong>'+
1386 '<div id="rsd_import_desc" syle="display:inline;">'+
1387 mv_get_loading_img('position:absolute;top:5px;left:5px') +
1388 '</div>'+
1389 '</div>'+
1390 '<div id="rds_edit_import_container" style="position:absolute;left:50%;' +
1391 'bottom:0px;top:30px;right:0px;overflow:auto;">'+
1392 '<strong>' + gM('mwe-local_resource_title') + '</strong><br>'+
1393 '<input type="text" size="30" value="' + rObj.target_resource_title + '" readonly="true"><br>'+
1394 '<strong>' + gM('mwe-edit_resource_desc') + '</strong>' +
1395 '<textarea id="rsd_import_ta" id="mv_img_desc" style="width:90%;" rows="8" cols="50">' +
1396 wt +
1397 '</textarea><br>' +
1398 '<input type="checkbox" value="true" id="wpWatchthis" name="wpWatchthis" tabindex="7"/>' +
1399 '<label for="wpWatchthis">'+gM('mwe-watch_this_page')+'</label><br><br><br>' +
1400
1401 $j.btnHtml(gM('mwe-do_import_resource'), 'rsd_import_doimport', 'check' ) + ' ' +
1402
1403 $j.btnHtml(gM('mwe-update_preview'), 'rsd_import_apreview', 'refresh' ) + '<div style="clear:both;height:20px;"/>' +
1404
1405 $j.btnHtml(gM('mwe-cancel_import'), 'rsd_import_acancel', 'close' ) + ' ' +
1406
1407 '</div>'+
1408 //output the rendered and non-renderd version of description for easy swiching:
1409 '</div>');
1410 //add hover:
1411 //update video tag
1412 rewrite_by_id(_this.target_container + '_rsd_pv_vid');
1413 //load the preview text:
1414 _this.getParsedWikiText( wt, _this.cFileNS +':'+ rObj.target_resource_title, function( o ){
1415 $j('#rsd_import_desc').html(o);
1416 });
1417 //add bidings:
1418 $j( _this.target_container + ' .rsd_import_apreview').btnBind().click(function(){
1419 /*$j('#rsd_import_desc').show().html(
1420 mv_get_loading_img()
1421 );*/
1422 //load the preview text:
1423 _this.getParsedWikiText( $j('#rsd_import_ta').val(), _this.cFileNS +':'+ rObj.target_resource_title, function( o ){
1424 js_log('got updated preivew: ');
1425 $j('#rsd_import_desc').html(o);
1426 });
1427 });
1428 $j(_this.target_container + ' .rsd_import_doimport').btnBind().click(function(){
1429 //check import mode:
1430 if(_this.import_url_mode=='form'){
1431 _this.doImportSpecialPage( rObj, cir_callback );
1432 }else if( _this.import_url_mode=='api'){
1433 _this.doImportAPI( rObj , cir_callback);
1434 }else{
1435 js_log("Error: import mode is not form or API (can not copy asset)");
1436 }
1437 });
1438 $j( _this.target_container + ' .rsd_import_acancel').btnBind().click(function(){
1439 $j('#rsd_resource_import').fadeOut("fast",function(){
1440 $j(this).remove();
1441 });
1442 });
1443 }
1444 }
1445 );
1446 }
1447 },
1448 doImportAPI:function(rObj, cir_callback){
1449 var _this = this;
1450 //baseUploadInterface
1451 mvJsLoader.doLoad([
1452 'mvBaseUploadInterface',
1453 '$j.ui.progressbar'
1454 ],function(){
1455 //initicate a download similar to url copy:
1456 myUp = new mvBaseUploadInterface({
1457 'api_url' : _this.local_wiki_api_url,
1458 'done_upload_cb':function(){
1459 //we have finished the upload:
1460
1461 //close up the rsd_resource_import
1462 $j('#rsd_resource_import').remove();
1463 //run the parent callback:
1464 cir_callback();
1465 //return false to avoid BaseUploadInterface done actions
1466 return false;
1467 }
1468 });
1469 //set the edit token if we have it handy
1470 _this.getEditToken(function( token ){
1471 myUp.etoken = token;
1472 myUp.doHttpUpload({
1473 'url' : rObj.src,
1474 'filename' : rObj.target_resource_title,
1475 'comment' : $j('#rsd_import_ta').val()
1476 });
1477 })
1478
1479
1480 });
1481 },
1482 getEditToken:function(callback){
1483 //first try the page form:
1484 var etoken = $j("input[name='wpEditToken']").val();
1485 if(etoken){
1486 callback( etoken );
1487 return ;
1488 }
1489 //@@todo try to load over ajax if( _this.local_wiki_api_url ) is set
1490 // (your on the api domain but are inserting from a normal page view)
1491 if( _this.local_wiki_api_url){
1492 get_mw_token(null, _this.local_wiki_api_url, function(token){
1493 callback( token );
1494 })
1495 }
1496 callback(false);
1497 return false;
1498 },
1499 /**
1500 * doImportSpecialPage
1501 * can be depricated once we support upload api support is widespred.
1502 */
1503 doImportSpecialPage:function(rObj, cir_callback){
1504 var _this = this;
1505 //get an edittoken:
1506 do_api_req( {
1507 'data': { 'action':'query',
1508 'prop':'info',
1509 'intoken':'edit',
1510 'titles': rObj.titleKey
1511 },
1512 'url':_this.local_wiki_api_url
1513 }, function(data){
1514 //could recheck if it has been created in the mean time
1515 if( data.query.pages[-1] ){
1516 var editToken = data.query.pages[-1]['edittoken'];
1517 if(!editToken){
1518 //@@todo give an ajax login or be more friendly in some way:
1519 js_error("You don't have permission to upload (are you logged in?)");
1520 //remove top level:
1521 $j('#modalbox').fadeOut("normal",function(){
1522 $j(this).remove();
1523 $j('#mv_overlay').remove();
1524 });
1525 }else{
1526 //not sure if we can do remote url uploads (so just do a local post)
1527 js_log('got token for new page:' +editToken);
1528 var postVars = {
1529 'wpSourceType' :'web',
1530 'wpUploadFileURL' : rObj.src,
1531 'wpDestFile' : rObj.target_resource_title,
1532 'wpUploadDescription' : $j('#rsd_import_ta').val(),
1533 'wpWatchthis' : $j('#wpWatchthis').val(),
1534 'wpUpload' : 'Upload file'
1535 }
1536 //set to uploading:
1537 $j('#rsd_resource_import').append('<div id="rsd_import_progress"'+
1538 'style="position:absolute;top:0px;'+
1539 'left:0px;width:100%;height:100%;'+
1540 'z-index:5;background:#FFF;overflow:auto;">'+
1541 '<div style="position:absolute;left:30%;right:30%"><h3>'+gM('mwe-importing_asset')+'</h3><br>' +
1542 mv_get_loading_img('','mv_loading_bar_img') +
1543 '</div>'+
1544 '</div>'
1545 );
1546 $j.post(wgArticlePath.replace(/\$1/,'Special:Upload'),
1547 postVars,
1548 function(data){
1549 //@@todo this will be replaced once we add upload image support to the api.
1550
1551 //very basic test to see if we got passed to the image page:
1552 //@@todo more normalization stuff
1553 var sstring ='var wgPageName = "' + _this.cFileNS + ':' + rObj.target_resource_title.replace(/ /g,'_') +'"';
1554 if(data.indexOf( sstring ) !=-1){
1555 js_log('found: ' + sstring);
1556 $j('#rsd_resource_import').remove();
1557 cir_callback( rObj );
1558 }else{
1559 js_log("Error or warning: (did not find: \"" + sstring + ' in output' );
1560 pos_etitle = '<h1 class="firstHeading">';
1561 var error_txt = form_txt = '';
1562 var res = grabWikiFormError( data );
1563
1564 if( res.error_txt )
1565 error_txt = res.error_txt;
1566
1567 if( res.form_txt )
1568 form_txt = res.form_txt;
1569
1570 js_log( 'error text is: ' + error_txt );
1571 $j( '#rsd_resource_import' ).html( '<h3>Error</h3>' + error_txt + '<br>' + form_txt +
1572 '<br>'+
1573 '<a href="#" id="rsd_import_error" >' + gM('mwe-cancel_import') + '</a>'
1574 );
1575 //set up cancel action:
1576 $j('#rsd_import_error').click(function(){
1577 $j('#rsd_resource_import').remove();
1578 });
1579 }
1580
1581 }
1582 );
1583 }
1584 }
1585 }
1586 );
1587 },
1588 previewResource:function( rObj ){
1589 var _this = this;
1590 this.checkImportResource( rObj, function(){
1591 //put another window ontop:
1592 $j( _this.target_container ).append('<div id="rsd_preview_display"' +
1593 'style="position:absolute;overflow:hidden;z-index:4;top:0px;bottom:75px;right:0px;left:0px;background-color:#FFF;">' +
1594 mv_get_loading_img('top:30px;left:30px') +
1595 '</div>');
1596
1597 var bPlaneTarget = _this.target_container +'~ .ui-dialog-buttonpane';
1598 var pTitle = $j( _this.target_container ).dialog('option', 'title');
1599
1600 //update title:
1601 $j( _this.target_container ).dialog('option', 'title', gM('mwe-preview_insert_resource', rObj.title) );
1602
1603 //update buttons preview:
1604 $j(bPlaneTarget).html( $j.btnHtml( gM('rsd_do_insert'), 'preview_do_insert', 'check') + ' ' )
1605 .children('.preview_do_insert')
1606 .click(function(){
1607 _this.insertResource( rObj );
1608 });
1609 //update cancel button
1610 $j(bPlaneTarget).append('<a href="#" class="preview_close">Do More Modification</a>')
1611 .children('.preview_close')
1612 .click(function(){
1613 $j('#rsd_preview_display').remove();
1614 //restore title:
1615 $j( _this.target_container ).dialog('option', 'title', pTitle);
1616 //restore buttons (from the clipEdit object::)
1617 _this.cEdit.updateInsertControlActions();
1618 });
1619
1620 //update the preview_wtext
1621 _this.updatePreviewText( rObj );
1622 _this.getParsedWikiText(_this.preview_wtext, _this.target_title,
1623 function(phtml){
1624 $j('#rsd_preview_display').html( phtml );
1625 //update the display of video tag items (if any)
1626 mwdomReady(true);
1627 }
1628 );
1629 });
1630 },
1631 updatePreviewText:function( rObj ){
1632 var _this = this;
1633
1634 if( _this.import_url_mode == 'remote_link' ){
1635 _this.cur_embed_code = rObj.pSobj.getEmbedHTML(rObj);
1636 }else{
1637 _this.cur_embed_code = rObj.pSobj.getEmbedWikiCode( rObj );
1638 }
1639
1640 //insert at start if textInput cursor has not been set (ie == length)
1641 if( _this.caret_pos && _this.caret_pos.text){
1642 if( _this.caret_pos.text.length == _this.caret_pos.s)
1643 _this.caret_pos.s=0;
1644 _this.preview_wtext = _this.caret_pos.text.substring(0, _this.caret_pos.s) +
1645 _this.cur_embed_code +
1646 _this.caret_pos.text.substring( _this.caret_pos.s );
1647 }else{
1648 _this.preview_wtext = $j(_this.target_textbox).val() + _this.cur_embed_code;
1649 }
1650 //check for missing </refrences>
1651 if( _this.preview_wtext.indexOf('<references/>') ==-1 && _this.preview_wtext.indexOf('<ref>') != -1 )
1652 _this.preview_wtext = _this.preview_wtext + '<references/>';
1653 },
1654 getParsedWikiText:function( wikitext, title, callback ){
1655 do_api_req( {
1656 'data':{'action':'parse',
1657 'text':wikitext
1658 },
1659 'url':this.local_wiki_api_url
1660 },function(data){
1661 callback( data.parse.text['*'] );
1662 }
1663 );
1664 },
1665 insertResource:function( rObj){
1666 js_log('insertResource: ' + rObj.title);
1667 var _this = this
1668 //dobule check that the resource is present:
1669 this.checkImportResource( rObj, function(){
1670 _this.updatePreviewText( rObj );
1671 $j(_this.target_textbox).val( _this.preview_wtext );
1672
1673 //update the render area (if present)
1674 if(_this.target_render_area && _this.cur_embed_code){
1675 //output with some padding:
1676 $j(_this.target_render_area).append( _this.cur_embed_code + '<div style="clear:both;height:10px">')
1677 //update if its video or audio:
1678 if( rObj.mime.indexOf('audio')!=-1 ||
1679 rObj.mime.indexOf('video')!=-1 ||
1680 rObj.mime.indexOf('/ogg') !=-1){
1681 mvJsLoader.embedVideoCheck(function(){
1682 mv_video_embed();
1683 });
1684 }
1685 }
1686 _this.closeAll();
1687 });
1688 },
1689 closeAll:function(){
1690 var _this = this;
1691 js_log("close all:: " + _this.target_container);
1692 _this.cancelClipEditCB();
1693 $j(_this.target_container).dialog('close');
1694 },
1695 setResultBarControl:function( ){
1696 var _this = this;
1697 var box_dark_url = mv_skin_img_path + 'box_layout_icon_dark.png';
1698 var box_light_url = mv_skin_img_path + 'box_layout_icon.png';
1699 var list_dark_url = mv_skin_img_path + 'list_layout_icon_dark.png';
1700 var list_light_url = mv_skin_img_path + 'list_layout_icon.png';
1701
1702 var about_desc ='';
1703 if( this.content_providers[this.disp_item] ){
1704 var cp = this.content_providers[this.disp_item];
1705 about_desc ='<span style="position:relative;top:0px;font-style:italic;">' +
1706 '<i>' + gM('mwe-results_from', [cp.homepage, cp.title]) + '</i></span>';
1707 $j('#tab-'+this.disp_item).append( '<div id="rds_results_bar">'+
1708 '<span style="float:left;top:0px;font-style:italic;">'+
1709 gM('rsd_layout')+' '+
1710 '<img id="msc_box_layout" ' +
1711 'title = "' + gM('rsd_box_layout') + '" '+
1712 'src = "' + ( (_this.result_display_mode=='box')?box_dark_url:box_light_url ) + '" ' +
1713 'style="width:20px;height:20px;cursor:pointer;"> ' +
1714 '<img id="msc_list_layout" '+
1715 'title = "' + gM('rsd_list_layout') + '" '+
1716 'src = "' + ( (_this.result_display_mode=='list')?list_dark_url:list_light_url ) + '" '+
1717 'style="width:20px;height:20px;cursor:pointer;">'+
1718 about_desc +
1719 '</span>'+
1720 '<span id="rsd_paging_ctrl" style="float:right;"></span>'+
1721 '</div>'
1722 );
1723 //get paging with bindings:
1724 this.getPaging('#rsd_paging_ctrl');
1725
1726 $j('#msc_box_layout').hover(function(){
1727 $j(this).attr("src", box_dark_url );
1728 }, function(){
1729 $j(this).attr("src", ( (_this.result_display_mode=='box')?box_dark_url:box_light_url ) );
1730 }).click(function(){
1731 $j(this).attr("src", box_dark_url);
1732 $j('#msc_list_layout').attr("src", list_light_url);
1733 _this.setDispMode('box');
1734 });
1735
1736 $j('#msc_list_layout').hover(function(){
1737 $j(this).attr("src", list_dark_url);
1738 }, function(){
1739 $j(this).attr("src", ( (_this.result_display_mode=='list')?list_dark_url:list_light_url ) );
1740 }).click(function(){
1741 $j(this).attr("src", list_dark_url);
1742 $j('#msc_box_layout').attr("src", box_light_url);
1743 _this.setDispMode('list');
1744 });
1745 }
1746 },
1747 getPaging:function(target){
1748 var _this = this;
1749 var cp_id = this.disp_item;
1750 var cp = this.content_providers[ this.disp_item ];
1751 //js_log('getPaging:'+ cp_id + ' len: ' + cp.sObj.num_results);
1752 var to_num = ( cp.limit > cp.sObj.num_results )?
1753 (cp.offset + cp.sObj.num_results):
1754 (cp.offset + cp.limit);
1755 var out = gM('rsd_results_desc') + ' ' + (cp.offset+1) + ' to ' + to_num;
1756 //check if we have more results (next prev link)
1757 if( cp.offset >= cp.limit )
1758 out+=' <a href="#" id="rsd_pprev">' + gM('rsd_results_prev') + ' ' + cp.limit + '</a>';
1759
1760 if( cp.sObj.more_results )
1761 out+=' <a href="#" id="rsd_pnext">' + gM('rsd_results_next') + ' ' + cp.limit + '</a>';
1762
1763 $j(target).html(out);
1764 //set bindings
1765 $j('#rsd_pnext').click(function(){
1766 cp.offset += cp.limit;
1767 _this.runSearch();
1768 });
1769 $j('#rsd_pprev').click(function(){
1770 cp.offset -= cp.limit;
1771 if(cp.offset<0)
1772 cp.offset=0;
1773 _this.runSearch();
1774 });
1775
1776 return;
1777
1778 },
1779 selectTab:function( selected_cp_id ){
1780 js_log('select tab: ' + selected_cp_id);
1781 this.disp_item = selected_cp_id;
1782 if( this.disp_item == 'upload' ){
1783 this.doUploadInteface();
1784 }else{
1785 //update the search results:
1786 this.runSearch();
1787 }
1788 },
1789 setDispMode:function(mode){
1790 js_log('setDispMode:' + mode);
1791 this.result_display_mode=mode;
1792 //run /update search display:
1793 this.drawOutputResults();
1794 }
1795 };