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