User:Texaner/common.js

/* (c) 2019 by User:Magnus Manske. GPLv3 or later.

This tool lets you quickly add statements for Structured Data on Commons (SDC) to (selected) files on galleries, category pages, and serach results.

Demo video: https://www.youtube.com/watch?v=RIjXRJNcbL0

To use this script, add it to your common.js page, like so: importScript('User:Magnus Manske/sdc_tool.js') ;

Activate by clicking on the "SDC" link in the box in the lower-right corner, or by pressing S with (Alt/Ctrl/whatever your browser uses).

$(document).ready ( function {

// Dialog function SDCdialog( config ) { SDCdialog.super.call( this, config ); }	OO.inheritClass( SDCdialog, OO.ui.Dialog );

// Specify a name for .addWindows SDCdialog.static.name = 'SDCdialog'; // Specify a title statically (or, alternatively, with data passed to the opening method). SDCdialog.static.title = 'Find a target item for SDC';

SDCdialog.prototype.update_results = function { let html = "" ; $.each ( this.results, function ( result_id , v ) {			html += " " ;			html += "  " ;			html += " " ;			html += "  ["+v.q + "] " ;			html += " " ;			html += " " ;			html += " " ;		} ) ; if ( html ===  ) html = ' No results found on Wikidata'' ' ; $('#sdc_dialog_results').html(html); $.each ( this.results, function ( result_id , v ) {			$('#sdc_dialog_results b.result_label[result="'+result_id+'"]').text(v.label);			$('#sdc_dialog_results div.result_description[result="'+result_id+'"]').text(v.description);		} ) ; let me = this ; $('.sdc_dialog_cegision_button').click(function{			let result_id = $(this).attr('result')*1 ;			let item = me.results[result_id] ;			me.on_decision(item);

});	};

// Customize the initialize function: This is where to add content to the dialog body and set up event handlers. SDCdialog.prototype.initialize = function { // Call the parent method. SDCdialog.super.prototype.initialize.call( this ); // Create and append a layout and some content. this.content = new OO.ui.PanelLayout( { 			padded: true,			expanded: false 		} );

let html = " "; html += " Enter a search query for Wikidata items " ; html += "  " ; html += " " ; html += " " ;

this.content.$element.append( html ); this.$body.append( this.content.$element );

this.results = [] ;

let me = this ; me.query_counter = 0 ; me.last_query = '' ; $('#sdc_dialog_query').keyup(function{			let query = $(this).val;			me.results = [] ;			if ( query==='' || query==me.last_query ) return ;			me.last_query = query ;			me.query_counter++ ;			let qc = me.query_counter ;			$.getJSON('https://www.wikidata.org/w/api.php?callback=?',{ action:'wbsearchentities', search:query, language:wgUserLanguage, limit:50, type:'item', format:'json' },function(d){ if ( me.query_counter != qc ) return ; // New query was started while this one was running $(d.search).each(function(k,v){					me.results.push({q:v.id,label:v.label,description:v.description||''});				}); me.update_results; });		});	};

// Override the getBodyHeight method to specify a custom height (or don't to use the automatically generated height). SDCdialog.prototype.getBodyHeight = function { return this.content.$element.outerHeight( true ); };

var sdc = {

exclude_files:['Gtk-dialog-info-14px.png','Reasonator_logo_proposal_no_background.png'], is_stopped : true, active : false, dialog: new SDCdialog( { size: 'medium' } ), windowManager: new OO.ui.WindowManager, mid_cache:[], mid2file:[], media_items:[], wikidata_items_to_load:[], wikidata_item_labels:[], target_item : { q:'', label:'', description:'' },

init : function { if ( wgNamespaceNumber == 6 ) return ; // Not for single images if ( $('a.image').length === 0 ) return ; // No possible thumbnails let me = this ; me.dialog.on_decision = me.set_target_item; $( document.body ).append( me.windowManager.$element ); me.windowManager.addWindows( [ me.dialog ] );

me.show_main_element; me.try_guess_main_item; //me.toggle_main; // Auto-open } ,

try_guess_main_item : function { let me = this ; if ( typeof wgWikibaseItemId == 'undefined' || wgWikibaseItemId === '' ) return ;

me.try_set_main_item(wgWikibaseItemId); } ,

try_set_main_item : function (q) { let me = this ; me.wikidata_items_to_load=[q] ; me.load_wikidata_items(function{				$.getJSON("https://www.wikidata.org/w/api.php?callback=?&action=wbgetentities&format=json&ids="+q,function(d){ let item = (d.entities||{})[q] ; if ( typeof item == 'undefined' ) return ; if ( me.does_item_have_property_target(item,'P31','Q4167836') ) { let statement = ((item.claims||item.statements||{}).P301||[])[0] ; if ( typeof statement == 'undefined' ) return ; // No "category's main topic" let target_id = (((statement.mainsnak||{}).datavalue||{}).value||{}).id ; if ( typeof target_id != 'undefined' && target_id != q ) me.try_set_main_item(target_id); return ; }					let label = $(me.wikidata_item_labels[q]).text; me.set_target_item({q:q,label:label,description:''}); });			});		} ,

update_main_position : function { let bottom = $('#cat_a_lot_toggle').length>0 ? 30 : 0 ;			$('#sdc_main').css({bottom:bottom+'px'}); } ,

show_action_button : function { let me = this ; var action_button = new OO.ui.ButtonWidget( {				label: 'Set '+me.get_property+' to '+me.target_item.q,				href: '#',				flags: 'progressive'			} ); $('#sdc_action_button_container').html(action_button.$element); $('#sdc_action_button_container a.oo-ui-buttonElement-button').click(function{				if ( $('input.sdc_checkbox:checked').length === 0 ) {					alert("No files selected");					return false ;				}				me.is_stopped = false ;				me.show_action_stop_button ;				me.edit_next;				return false;			}); } ,

show_action_stop_button : function { let me = this ; var action_button = new OO.ui.ButtonWidget( {				label: 'Stop editing',				href: '#',				flags: 'destructive'			} ); $('#sdc_action_button_container').html(action_button.$element); $('#sdc_action_button_container a.oo-ui-buttonElement-button').click(function{				me.is_stopped=true;				$('#sdc_action_button_container').html("Stopping edits...");				return false;			}); } ,

edit_next : function { let me = this ; if ( me.is_stopped ) return me.show_action_button; // User stopped this let cbs = $('input.sdc_checkbox:checked') ; if ( cbs.length == 0 ) return me.show_action_button; // All done let cb = $(cbs.get(0)) ; let file = decodeURIComponent(cb.attr('file')) ; if ( typeof file == 'undefined' ) return me.show_action_button; // All done

me.get_mediainfo_id_for_file ( file, function ( mediainfo_id ) {				me.add_item_statement_to_item(mediainfo_id,me.get_property,me.target_item.q,me.is_prominent,function(ok){ // TODO check ok					cb.attr('checked', false); cb.prop('checked', false); me.edit_next; //delete me.media_items[mediainfo_id] ; me.load_media_items([mediainfo_id]); });			} ) ;

} ,

is_prominent : function { return $('#sdc_prominent').is(":checked"); } ,

// `file` WITHOUT File: prefix! get_mediainfo_id_for_file : function ( file, callback ) { let me = this ; if ( typeof me.mid_cache[file] != 'undefined') { return callback ( me.mid_cache[file] ) ; }			$.get('/w/api.php',{				action:'query',				prop:'info',				titles:"File:"+file,				format:'json'			},function(d){				let mid = -1 ;				$.each ( d.query.pages, function ( page_id , page_info ) { mid = page_id } ) ;				if ( mid == -1 ) mid = '' ;				else mid = 'M'+mid ;				me.mid_cache[file] = mid ;				callback(mid);			},'json'); } ,

get_token : function ( callback ) { $.post ( '/w/api.php', {				action : 'query' ,				meta : 'tokens' ,				format : 'json' ,			} , function ( d ) {				callback(d.query.tokens.csrftoken);			} ) ; } ,

does_item_have_property_target : function ( item, property , target_item_id ) { let statement_present = false ; $.each ( ((item.statements||item.claims||{})[property]||[]), function ( dummy , statement ) {				if ( ((((statement||{}).mainsnak||{}).datavalue||{}).value||{}).id == target_item_id ) {					statement_present = true ;				}			} ) ; return statement_present ; } ,

add_item_statement_to_item : function ( item_id, property , target_item_id , prominent , callback ) { let me = this ; let value = {'entity-type':'item',id:target_item_id} ; let data = {claims:[{mainsnak:{snaktype:"value",property:property,datavalue:{value:value,type:'wikibase-entityid'}},type:"statement",rank:"normal"}]} ; let summary = 'SDC: added '+property+' => '+target_item_id+'' ; if ( prominent ) { data.claims[0].rank = 'preferred' ; summary += ', prominent' ; }

// Check if statement already present if ( typeof me.media_items[item_id] != 'undefined' ) { if ( me.does_item_have_property_target(me.media_items[item_id],property,target_item_id) ) return callback(true); }

me.get_token ( function ( token ) {				let params = {					action:'wbeditentity',					id:item_id,					data:JSON.stringify(data),					token:token,					summary:summary,					format:'json'				} ;				$.post('/w/api.php',params,function(d){ callback(true); },'json');			} ); } ,

set_target_item : function ( item ) { let me = sdc ; me.target_item = item ;

let html = "" ; html += " Target item: ["+item.q+"]  "; html = " "+html+" " ; $('#sdc_target_item_display').html(html).show; $('#sdc_target_item_label').text(item.label);

me.show_action_button;

me.update_main_position; me.windowManager.closeWindow( me.dialog ); } ,

get_property : function { return $('#sdc_property').val; } ,

open_dialog : function { this.windowManager.openWindow( this.dialog ); setTimeout(function{$('#sdc_dialog_query').focus},500); } ,

show_main_element : function { let me = this ; let html = "" ; html += "SDC" ; html += "" ;

html += "  <span id='sdc_cb_toggle'> <span id='sdc_cb_none'> " ; /*			html += " Check " ; html += "<a href='#' id='sdc_cb_all'>all</a> | " ; html += "<a href='#' id='sdc_cb_toggle'>toggle</a> | " ; html += "<a href='#' id='sdc_cb_none'>none</a>" ; html += " " ; */

html += " Property " html += "<select id='sdc_property'>" ; html += "<option value='P180' selected>depicts [P180] " ; html += "<option value='P195'>collection [P195] " ; html += " " ; html += " " ;

html += "<div id='sdc_target_item' style='margin:2px;padding:2px;border:1px solid #DDD;border-radius:3px'>"; html += "<div id='sdc_target_item_display' style='display:none;'> " ; html += "<div id='sdc_target_item_button' style='text-align:center;'> " ; html += " " ;

html += "<div id='sdc_action' style='text-align:center'>" ; html += " <input type='checkbox' id='sdc_prominent' /> Prominent " ; html += "<div id='sdc_action_button_container'>Set a target item to perform an action " ; html += " " ;

html += " " ; html += " " ; $('body').append(html); $('#sdc_main_button').click(function{ me.toggle_main; return false; });

let button_all = new OO.ui.ButtonWidget( { label: 'All',href: '#' } ) ; let button_toggle = new OO.ui.ButtonWidget( { label: 'Toggle',href: '#' } ) ; let button_none = new OO.ui.ButtonWidget( { label: 'None',href: '#' } ) ; $('#sdc_cb_all ').html(button_all.$element); $('#sdc_cb_toggle').html(button_toggle.$element); $('#sdc_cb_none').html(button_none.$element); $('#sdc_cb_all a.oo-ui-buttonElement-button').click(function{ $('input.sdc_checkbox').attr('checked', true); return false; }); $('#sdc_cb_toggle a.oo-ui-buttonElement-button').click(function{ $('input.sdc_checkbox').click; return false; }); $('#sdc_cb_none a.oo-ui-buttonElement-button').click(function{ $('input.sdc_checkbox').attr('checked', false); return false; });

let button_sti = new OO.ui.ButtonWidget( { label: 'Set target item',href: '#' } ) ; $('#sdc_target_item_button ').html(button_sti.$element); $('#sdc_target_item_button a.oo-ui-buttonElement-button').click(function{me.open_dialog;});

me.update_main_position; setTimeout(me.update_main_position,200); } ,

toggle_main : function { this.active = !this.active ; if ( this.active ) { this.show_checkboxes; $('#sdc_options').show; } else { $('div.sdc_checkbox_container').remove; $('#sdc_options').hide; }			this.update_main_position; } ,

cache_file_media_ids : function ( files ) { let me = this ; let chunks = [ [] ] ; let MAX_CHUNK_SIZE = 50 ; $.each ( files, function ( dummy , file ) {				if ( chunks[chunks.length-1].length < MAX_CHUNK_SIZE ) {					chunks[chunks.length-1].push ( file ) ;				} else {					chunks.push ( [ file ] ) ;				}			} ) ; me.wikidata_items_to_load = [] ; $.each ( chunks, function ( dummy , chunk ) {				me.cache_file_media_ids_chunk(chunk) ;			} ) ; } ,

cache_file_media_ids_chunk : function ( files ) { let me = this ; let params = { action:'query', prop:'info', titles:"File:"+files.join("|File:"), format:'json' } ;			$.post('/w/api.php',params,function(d){				let to_load = [] ;				$.each ( d.query.pages, function ( page_id , page_info ) { if ( page_id == -1 ) return ; // Paranoia let mid = 'M'+page_id ; let file = page_info.title.replace(/^File:/,'').replace(/ /g,'_'); me.mid_cache[file] = mid ; me.mid2file[mid] = file ; to_load.push ( mid ) ; } ) ;				me.load_media_items(to_load);			},'json'); } ,

load_media_items : function ( mids ) { if ( mids.length == 0 ) return ; let me = this ; let params = { action:'wbgetentities', ids:mids.join('|'), format:'json' } ;			$.post('/w/api.php',params,function(d){				$.each ( d.entities, function ( mid , mi ) { me.media_items[mid] = mi ; me.update_file_sdc(mid); } ) ;				me.load_wikidata_items;			},'json'); } ,

load_wikidata_items : function ( callback ) { let me = this ; let to_load = [] ; // Should never be more than 50 $.each ( me.wikidata_items_to_load, function ( dummy , q ) {				if ( typeof me.wikidata_item_labels[q] != 'undefined' ) {					me.update_q_labels(q);					return ;				}				if ( $.inArray(q,to_load) !== -1 ) return ;				to_load.push(q) ;			} ) ; me.wikidata_items_to_load = [] ; if ( to_load.length == 0 ) return ; $.getJSON('https://www.wikidata.org/w/api.php?callback=?',{				action:'wbformatentities',				ids:to_load.join('|'),				format:'json'			},function(d){				$.each ( d.wbformatentities, function ( q , html ) { me.wikidata_item_labels[q] = html.replace('<a ','<a target="_blank" ') ; me.update_q_labels(q); } ) ;				if ( typeof callback != 'undefined' ) callback ;			}); } ,

update_q_labels : function ( q ) { let me = this ; $('td.q_to_load[q="'+q.replace(/'/g, "&#39;")+'"]').removeClass('q_to_load').html(me.wikidata_item_labels[q]); } ,

get_wikidata_item_label_td : function ( q, prominent ) { let me = this ; let style = 'vertical-align:top;' ; if ( prominent ) style += 'font-weight:bold;' ; if ( typeof me.wikidata_item_labels[q] == 'undefined' ) { return "<td class='q_to_load' q='"+q+"' style='"+style+"'>"+q+" " ; } else { return "<td q='"+q+"' style='"+style+"'>"+me.wikidata_item_labels[q]+" " ; }		} ,

update_file_sdc : function ( mid ) { let me = this ; let file = me.mid2file[mid] ; if ( typeof file == 'undefined' ) return ; let mi = me.media_items[mid] ; if ( typeof mi == 'undefined' ) return ; let out = [] ; $.each ( (mi.statements||{}), function ( property , statements ) {				$.each ( statements , function ( dummy , statement ) { if ( ((statement.mainsnak||{}).datavalue||{}).type != 'wikibase-entityid' ) return ; me.wikidata_items_to_load.push(property); let wd_item_id = statement.mainsnak.datavalue.value.id ; me.wikidata_items_to_load.push(wd_item_id); out.push({property:property,target:wd_item_id,prominent:statement.rank=='preferred'}); } ) ;			} ) ;

let h = '' ; if ( out.length == 0 ) { let html = "<div class='sdc_statements' mid='"+mid+"' style='font-size:9pt;width:100%;text-align:left;'>" ; html += " No SDC " ; html += " " ; h = $(html); } else { let html = "<div class='sdc_statements' mid='"+mid+"' style='font-size:9pt;width:100%;text-align:left;'>" ; html += " " ; h = $(html); h.find('td').css({padding:'1px','text-align':'left'}); }

let element = $($('input.sdc_checkbox[file="'+me.sanitize_file_attribute(file)+'"]').parent) ; $('div.sdc_statements[mid="'+mid+'"').remove; element.after(h); } ,

sanitize_file_attribute : function ( file ) { return encodeURIComponent(file).replace(/'/g, "&#39;") ; } ,

show_checkboxes : function { let me = this ; let files = [] ; $('a.image').each(function(num,a){				let file = decodeURIComponent($(a).attr('href')).replace(/^.+\/File:/,'') ;				if ( $.inArray(file,me.exclude_files) > -1 ) return ; // Bad file!				if ( $(a).parents('#wdinfobox').length > 0 ) return ; // In infobox				files.push(file);				let html = "<div class='sdc_checkbox_container' style='display:flex'>" ;				html += " <input type='checkbox' class='sdc_checkbox' file='"+me.sanitize_file_attribute(file)+"' /> " ;				html += " " ;				$(a).after(html);			}); me.cache_file_media_ids(files); }

} ;

sdc.init; });