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