గమనిక: భద్రపరచిన తర్వాత, మార్పులను చూడాలంటే మీ విహారిణి కోశాన్ని తీసేయాల్సిరావచ్చు.

  • ఫైర్‌ఫాక్స్‌ / సఫారి: Shift మీటని నొక్కిపట్టి Reloadని నొక్కండి లేదా Ctrl-F5 గానీ Ctrl-R (మాకింటోషులో ⌘-Shift-R) గానీ నొక్కండి
  • గూగుల్ క్రోమ్: Ctrl-Shift-R (మాక్ లో ⌘-Shift-R) నొక్కండి
  • ఇంటర్నెట్ ఎక్ప్లోరర్/ఎడ్జి: Ctrl ను నొక్కిపట్టి Refresh నొక్కండి లేదా Ctrl-F5 నొక్కండి.
  • ఒపేరా:* Ctrl-F5 నొక్కండి.
/***********************************************************************
 * addViafData.js
 * 
 * A tool to make it easy to select and import authority control data
 * from VIAF (viaf.org) using a text query.
 * 
 * Adds a link to the toolbox which opens a frame at the top of the page
 * containing a search box which loads data directly from VIAF. Click
 * the icon to use the record, or the number to jump to the VIAF record
 * page.
 * 
 * Bugs and suggestions to [[User:Inductiveload]], or patch as needed
 * 
 * Version 0.1   - 2012-01-19 - Initial release  
 *               - 2012-01-25 - Allow suggested text to load even on 
 *                              non-existent pages
 *               - 2012-03-10 - Warn when VIAF AutoSuggest might not 
 *                              return all hits
 *               - 2012-04-23 - Add ability to save pages, update pages 
 * 	                            while editing, de-select VIAF entries
 * 
 **********************************************************************/

var VIAFDataGrabber = function() { //constructor

	//translating from VIAF JSON names to WS template names
	this.template_parameters_translation = {
		'viafid' 	: 'VIAF',
		'lc'		: 'LCCN',
		'isni, isni-test'	: 'ISNI',
		'dnb'		: 'GND',
		'sudoc'		: 'SUDOC',
		'bnf'		: 'BNF',
		'selibr'	: 'SELIBR',
		'jpg'		: 'ULAN',
		'nla'		: 'NLA',
		'wksid'		: 'WKS'
	}

	//list of parameters allowed in AC templates
	this.template_parameters_allowed = ['VIAF', 'LCCN', 'ISNI', 'GND', 'SUDOC', 'BNF', 'SELIBR', 'ULAN', 'NLA', 'WKS']

	this.ac_template_re = /{{ *[Aa]uthority[ _]control *\|[\s\S]*?}}/m; //regex to find AC templates
	this.footer_re = /((\n(\[\[ *Category *:[^\]]+\]\]|\[\[\w+:[^\]]+\]\])?[\n\s]*)*)$/; //regex to find the footers which go under the AC template

	this.icons = {
		'add':'//upload.wikimedia.org/wikipedia/commons/thumb/4/41/Ambox_emblem_plus.svg/20px-Ambox_emblem_plus.svg.png', 
		'remove':'//upload.wikimedia.org/wikipedia/commons/thumb/9/97/Dialog-error-round.svg/20px-Dialog-error-round.svg.png'
	}
	
	
	this.pageDataFound = false;
	this.acDataFound = false;
	this.acDataFoundIsBad = false;
	
	this.viafAutosuggestMaxResults= 10; //max results viaf returns
	
	this.add_VIAF_button();
	//this.add_VIAF_pane(); //uncomment when debugging to prevent having to click button each time
}

VIAFDataGrabber.prototype.construct_VIAF_search_URL = function(query){

	return 'http://viaf.org/viaf/search?query=local.personalNames+all+"'+ encodeURIComponent(query) +'"&maximumRecords=100&sortKeys=holdingscount&stylesheet=/viaf/xsl/results.xsl'
}

VIAFDataGrabber.prototype.close_VIAF_pane = function(){
	$('#add_viaf_data_pane').remove();
}

VIAFDataGrabber.prototype.add_VIAF_pane = function(){

	var myself = this;
	if ($('#add_viaf_data_pane').length > 0){
		return
	}

	mw.util.addCSS( '.selected .viaf-id-link { font-weight:bold; }' );
	mw.util.addCSS( '.viaf-entry-icon { float:left; width:20px; height:20px; background-image:url("' + this.icons['add'] + '"); }' );
	mw.util.addCSS( '.selected .viaf-entry-icon { background-image:url("' + this.icons['remove'] + '"); }' );
	mw.util.addCSS( '#viaf_raw_proposed_data { margin-left:1em; font-size:120%; background-color:white; }' );
	mw.util.addCSS( '.viaf-changes-list { margin-left:2em; font-size:84%; }' );
	mw.util.addCSS( '#viaf-rendered-template { margin-top:0.25em; }' );
	mw.util.addCSS( 'div#add_viaf_data_pane p { margin-top:0.25em; margin-bottom:0.25em; }' );
	
	this.viafDiv = $('<div id="add_viaf_data_pane" style="position:relative; background-color:#EEEEEE; border:1px solid blue; padding:1em; margin-bottom:0.5em;"><h3 style="padding-top:0px;">Add authority control data</h3></div>')
	
	var closeDiv = $('<div style="position:absolute; right:1em; top:0.1em;"></div>');

	var closeLink = $('<span><a href="#">[Close]</a></span>');
	
	closeLink.click( function(){
		myself.close_VIAF_pane()
	});
	
	closeDiv.append(closeLink);
	this.viafDiv.append(closeDiv);
	
	
	var jump_to_viaf_link = $("<a> Go to VIAF's search page.</a>");
	
	var viaf_query_input = $('<input type="text" name="viaf_query" id="viaf_query_input" size="48" value="' + mw.config.get('wgTitle') + '"/>');

	viaf_query_input.change( function(){
			// link to real VIAF search
			jump_to_viaf_link.attr('href', myself.construct_VIAF_search_URL( $('#viaf_query_input').val() ) );
		} );
	
	
	var submitVIAFquery = $('<input type="button" name="submit_viaf_query" id="submit_viaf_query" value="Search VIAF"/>')
	
	submitVIAFquery.click( function(){
			
			//using real search (but how to make the cross domain request?)
			//var url = 'http://viaf.org/viaf/search?query=local.personalNames+all+"'+ encodeURIComponent($('#viaf_query_input').val()) +'"&version=1.1&operation=searchRetrieve&recordSchema=http://viaf.org/VIAFCluster&maximumRecords=50&startRecord=1&resultSetTTL=300&recordPacking=xml&recordXPath=&sortKeys=&httpAccept=text/xml'
			
			//using autosuggest (clumsy, but we can use JSONP)
			$.getJSON("https://viaf.org/viaf/AutoSuggest?query="+ encodeURIComponent($('#viaf_query_input').val()) +"&callback=?", function(data){
				myself.display_query_results(data);
			});
		
		} );
		
	this.viafResultsDiv = $('<div id="viaf_results"></div>');
	this.viafCurrentDataDiv = $('<div></div>');
	this.viafProposedDataDiv = $('<div id="proposed_authority_control_data"></div>');
	this.viafSaveACDiv = $('<div id="viaf_save_authority_control" style="display:none;"><hr></div>');
	
	var saveACButton = $('<input type="button" value="Update authority control information" id="viaf_save_ac_template" name="viaf_save_ac_template"/>');
	this.viafSaveACDiv.append(saveACButton);
	
	saveACButton.click( function(){
		myself.save_ac_data();
	});
		
	this.viafDiv.append(viaf_query_input, submitVIAFquery, jump_to_viaf_link);
	
	this.viafDiv.append('<hr>', this.viafResultsDiv)
	this.viafDiv.append('<hr>', this.viafCurrentDataDiv);
	this.viafDiv.append('<hr>', this.viafProposedDataDiv);
	this.viafDiv.append(this.viafSaveACDiv);

	$('#bodyContent').prepend( this.viafDiv );
	
	viaf_query_input.change(); //trigger an update of the link
	
	if (mw.config.get('wgAction') == 'view' ){ //get data from API
		this.get_raw_page(mw.config.get('wgPageName'), this.show_current_authority_data)
	} else if ($.inArray(mw.config.get('wgAction'), ['submit', 'edit'] ) > -1 ){ //or from the current edit box
		this.show_current_authority_data ($('#wpTextbox1').val() );
	}

	submitVIAFquery.click(); //preload the page name results
}

VIAFDataGrabber.prototype.show_current_authority_data = function(data){
	
	if (data == null){
		var current_ac_data = null
	} else {
		var current_ac_data = data.match(/{{ *[Aa]uthority control *\|.*}}/g);
	}
	
	this.pageDataFound = true;
	
	if (current_ac_data == null ){
		this.viafCurrentDataDiv.html('<p>No existing authority data has been found on this page.</p>');
		this.acDataFound = false;
		this.acDataFoundIsBad = false;
	} else if (current_ac_data.length == 1){
		this.viafCurrentDataDiv.html('<p>Authority control template found on this page. New data will be merged with it.</p>');
		this.viafCurrentDataDiv.append('<tt style="margin-left:1em;" id="current_authority_control_data">' + current_ac_data[0] + '</tt>');
		this.acDataFound = this.parse_ac_template( current_ac_data[0] );
		this.acDataFoundIsBad = false;
	} else { 
		this.viafCurrentDataDiv.html('<p class="error">Multiple authority control templates exist on this page:</p><br>')
		this.acDataFound = true;
		this.acDataFoundIsBad = true;
		for (i in current_ac_data){
			this.viafCurrentDataDiv.append('<tt style="margin-left:1em" id="current_authority_control_data">' + current_ac_data[i] + '</tt><br>');
		}
	}
}

VIAFDataGrabber.prototype.parse_ac_template = function(template){
//parse a string holding a AC template into the AC array
	
	var i = 0;
	var parameter = ''
	var array = {};
	
	
	while(template[i] != '|'){ //get past the {{template name|
		i = i+1;
	}
	
	while(1){
		
		if (i == template.length){ //somehow we have overstepped the end of the template - maybe it was incomplete
			break;
		}
		
		i = i + 1;
		
		if (template[i] != '|' && template[i] != '}'){
			parameter = parameter + template[i];
		} else { //we have completed a parameter

			var myRegexp = /([^=]+)\s*=\s*(.*)/g; //look for parameter = value
			var match = myRegexp.exec(parameter);
			
			if (match != null){
				array[match[1]] = match[2]
			}
			
			if (template[i] == '}'){ //end of the template
				break;
			}
			
			parameter = '';
		}
	}
	
	return array
}

VIAFDataGrabber.prototype.add_VIAF_button = function(){
	var btn_id = 't-viaf-authority-control'
	var portletLink = mw.util.addPortletLink('p-tb', '#', 'Add authority control', btn_id, 'Add an authority control template using data from VIAF');
	
	// Bind click handler
	$( '#' + btn_id ).click( $.proxy( this.add_VIAF_pane, this ) );
}

VIAFDataGrabber.prototype.display_query_results = function(results){
	
	if (results['result'] == null){
		this.viafResultsDiv.html('No results found for "'+results['query']+'"');
	} else {
		
		var viafids = []; //list of ids to prevent dupes
		var results_list = $('<ul id="viaf-results-list" style="list-style: none;"><ul>')
		
		var count = 0;
		
		for (i in results['result']){
			
			if ($.inArray(results['result'][i]['viafid'],  viafids) == -1 ){ //fliter out duplicate VIAF ids
				viafids.push(results['result'][i]['viafid']);
				results_list.append(this.build_list_entry(results['result'][i]));
			}
			count++;
		}		
		
		this.viafResultsDiv.html('').append(results_list);

		if (count >= this.viafAutosuggestMaxResults ){
			var warning = $("<span style='font-style:italic' id='viaf-warn-further-results'>These may not be all the results available from VIAF. See the <a href='" +  this.construct_VIAF_search_URL( $('#viaf_query_input').val() ) + "'>full VIAF search page</a> for more results.</span>");
			
			this.viafResultsDiv.append(warning);
		}
	}
}

VIAFDataGrabber.prototype.build_list_entry = function(result){
	var myself = this;
	var entry = $('<li></li>');
	
	var use_id = $('<div class="viaf-entry-icon"></div>');
	
	use_id.click( function(){
		
		if (entry.hasClass('selected')){
			entry.removeClass('selected');
			myself.remove_data();
		} else {
			myself.add_viaf_data(result);
			myself.viafResultsDiv.find('li').removeClass('selected'); //remove other entries' bolding
			entry.addClass('selected'); //add it to this one
		}
	});
	
	entry.append(use_id, '&nbsp;VIAF ID: ', '<a class="viaf-id-link" href="http://viaf.org/viaf/' + result['viafid'] + '">' + result['viafid'] + '</a> ', result['term']);

	return entry;
}

VIAFDataGrabber.prototype.remove_data = function(){
//remove any result selected

	this.viafProposedDataDiv.empty();
	this.viafProposedDataDiv.empty();
	this.merge_authority_data(null);
	
}

VIAFDataGrabber.prototype.add_viaf_data = function(result){
// adds data from a viaf json result to the AC data already present

	if (!this.pageDataFound){
		this.viafCurrentDataDiv.html('<span class="error">Current page wikitext not loaded, please wait until this message changes and try again.</span>');
		return;
	}
	
	if (this.acDataFoundIsBad){
		this.viafCurrentDataDiv.append('<p class="error">The current authority control data cannot be merged with imported data. Fix the existing data first (eg. remove duplicate templates) and try again.</p>');
		return;
	}
	
	//page data is loaded, and it is OK, let's merge the data!
	this.merge_authority_data(result);
	
}

VIAFDataGrabber.prototype.construct_ac_template = function(data){
	
	var text = '{{authority control'
	
	for (field in data){
		
		if ($.inArray(field, this.template_parameters_allowed) > -1 ){
			text += '|' + field + '=' + data[field];
		}
	}
	
	text = text + '}}';
	
	return text;
}

VIAFDataGrabber.prototype.merge_authority_data = function(result){
//Merge authority data from a VIAF result with any existing data

	//construct an array of new data
	new_data = {};
	
	var id;
		
	for (field in result){
		var orig_id = result[field]
		if (field == 'lc'){//format IDs as needed
			id = '';
			var id_i = 0;
			while (orig_id[id_i].search(/[A-Za-z]/) != -1){ //alphabetical prefix
				id = id + orig_id[id_i];
				id_i = id_i + 1;
			}
			var year_len = (result[field].length - id_i < 9)?2:4;
			id = id + '/' + orig_id.slice(id_i,id_i+year_len) + '/' + orig_id.slice(id_i+year_len).replace(/^0+/, ''); //truncate leading zeroes
		} else if (field == 'dnb'){
			id = orig_id.toUpperCase()
		} else if (field == 'nla'){
			id = orig_id.replace(/^0+/, ''); //truncate leading zeroes
		} else if (field == 'bnf'){
			
			var bnf_xdigits = '0123456789bcdfghjkmnpqrstvwxz';
			var bnf_check_digit = 0;
			
			id = 'cb' + orig_id;
			for (var i=0; i<id.length; i++){
				bnf_check_digit += bnf_xdigits.indexOf(id[i]) * (i+1);
			}
			id += bnf_xdigits[bnf_check_digit % 29]; //29 is the radix

		} else {
			id = orig_id
		}
		
		if (field in this.template_parameters_translation ){ //if the parameter is allowed, get the WS template parameter and save it
			new_data[this.template_parameters_translation[field] ] = id;
		}
	}
	
	this.added_fields = {};      //fields add the existing data
	this.conflicted_fields = {}; //fields that conflict and need approval
	this.pending_fields = {};    //fields that don't conflict but won't be added
	
	for (field in new_data){
	
		if (this.acDataFound && field in this.acDataFound){
			
			if (this.acDataFound[field] == new_data[field]){ //duplicate field, ignore
				continue;
			}
			
			//console.log('Conflict in field: ' + field + '. Current data: "' +  this.acDataFound[field] + '" New data: "' + new_data[field] + '"' );
			this.conflicted_fields[field] = new_data[field];
		} else {
			this.added_fields[field] = new_data[field];
		}
	}
	
	this.provisional_data = $.extend({}, this.acDataFound); 

	for (field in this.added_fields){
		if (!(field in this.provisional_data)){ //add any new data by default
			this.provisional_data[field] = this.added_fields[field]
		}
	}

	this.show_suggestions();
}

VIAFDataGrabber.prototype.show_suggestions = function(){
	
	var myself = this;
	this.viafSaveACDiv.css('display','none');
	
	if (!('VIAF' in this.provisional_data)){ //we don't have a selected item, clear out and get out
		this.viafProposedDataDiv.empty();
		return;
	}
	
	if ($.isEmptyObject(this.added_fields) &&  $.isEmptyObject(this.pending_fields) && $.isEmptyObject(this.conflicted_fields)){
		this.viafProposedDataDiv.html('<p>No changes available for authority control template using VIAF ID ' + this.provisional_data['VIAF'] + '.</p>');
	} else {
		if ($.isEmptyObject(this.added_fields) && $.isEmptyObject(this.provisional_data) ){
			this.viafProposedDataDiv.html('<p>No authority data selected.</p>');
		} else {
			template = this.construct_ac_template(this.provisional_data)
					
			this.viafProposedDataDiv.html('<p>Proposed authority control template:</p><tt id="viaf_raw_proposed_data">' +template+ '</tt><br>');
			
			var renderedTemplateHTML = $('<div id="viaf-rendered-template"></div>');
			this.viafProposedDataDiv.append(renderedTemplateHTML);
			
			//render the template and display it
			$.ajax({ url:mw.config.get('wgServer') + wgScriptPath + "/api.php" + '?action=parse&prop=text&format=json&title=' + mw.config.get('wgPageName') + '&text=' + template ,
					success:function(data){
						renderedTemplateHTML.append( $(data['parse']['text']['*']));
					},
				});
		}
		
		var added_list = $('<ul class="viaf-changes-list"></ul>')
		var pending_list = $('<ul class="viaf-changes-list"></ul>')
		var conflict_list = $('<ul class="viaf-changes-list"></ul>')

		var list_item, link;
		
		for (field in this.added_fields){
			list_item = $( '<li>Added field: ' + field + ': ' + this.added_fields[field] + '. </li>')
			link = $('<a href="#">Remove this field.</a>');
			
			link.bind('click', {field: field}, function(e){
				myself.remove_added(e.data.field)
				return false;
			});
			
			added_list.append(list_item.append(link));
		}
		
		for (field in this.pending_fields){
			list_item = $( '<li>Pending field: ' + field + ': ' + this.pending_fields[field] + '. </li>')
			link = $('<a href="#">Add this value.</a>');
			
			link.bind('click', {field: field}, function(e){
				myself.add_removed(e.data.field)
				return false;
			});
			
			pending_list.append(list_item.append(link));
		}
		
		
		for (field in this.conflicted_fields){
			list_item = $( '<li>Conflict in field: ' + field + '. Current value: ' + this.provisional_data[field] + '; Conflicting value: ' + this.conflicted_fields[field] + '. </li>')
			link = $('<a href="#">Switch values.</a>');
			
			link.bind('click', {field: field}, function(e){
				myself.approve_conflict(e.data.field)
				return false;
			});
			
			conflict_list.append(list_item.append(link));
		}
		
		//show whichever list have items
		if (!$.isEmptyObject(this.added_fields)){ this.viafProposedDataDiv.append('Changes made:', added_list); }
		if (!$.isEmptyObject(this.pending_fields)){ this.viafProposedDataDiv.append('Available fields:', pending_list);}
		if (!$.isEmptyObject(this.conflicted_fields)){this.viafProposedDataDiv.append('Conflicts:', conflict_list);}
		
		if ($('#viaf_raw_proposed_data').length > 0 && $('#viaf_raw_proposed_data').text() != $('#current_authority_control_data').text()){ //if we changed anything
			this.viafSaveACDiv.css('display','');
		} else {
			this.viafSaveACDiv.css('display','none');
		}
	}
}

VIAFDataGrabber.prototype.approve_conflict = function(field){
	
	var temp = this.provisional_data[field];
	
	this.provisional_data[field] = this.conflicted_fields[field];
	this.conflicted_fields[field] = temp;
	
	this.show_suggestions(); //update the suggestions
}

VIAFDataGrabber.prototype.remove_added = function(field){
	
	this.pending_fields[field] = this.added_fields[field];
	delete this.added_fields[field];
	delete this.provisional_data[field];
	this.show_suggestions(); //update the suggestions
}

VIAFDataGrabber.prototype.add_removed = function(field){
	
	this.added_fields[field] = this.pending_fields[field];
	this.provisional_data[field] = this.pending_fields[field];
	delete this.pending_fields[field];
	this.show_suggestions(); //update the suggestions
}

VIAFDataGrabber.prototype.save_ac_data = function(){
	
	
	if (mw.config.get('wgAction') == 'view' ){ //update in the background
		this.get_raw_page(mw.config.get('wgPageName'), this.update_page);
	} else if ($.inArray(mw.config.get('wgAction'), ['submit', 'edit'] ) > -1 ){
		var text = $('#wpTextbox1').val();
		var text = this.add_ac_template_to_text(text);
		
		$('#wpTextbox1').val(text);
		
		$('#wpTextbox1').css({'outline':'2px solid green'});
		setTimeout(function() {
			$('#wpTextbox1').css({'outline':''});
		}, 2000);
	}
}

VIAFDataGrabber.prototype.add_ac_template_to_text = function(text){
	//splice the template into the text, overwriting any current template
	var raw_template = $('#viaf_raw_proposed_data').text();
	
	var match = this.ac_template_re.exec(text);
	
	if (match == null){ //there is no existing template
		var foot = this.footer_re.exec(text)
		
		if (foot != null){
			text = text.replace(this.footer_re, '\n' + raw_template + foot[1])
		} else {
			text = text + '\n\n' + raw_template;
		}
		
	} else {
		text = text.replace(this.ac_template_re, raw_template); //update the text in the text box
	}
	
	return text;
}

VIAFDataGrabber.prototype.update_page = function(data){
	var text = this.add_ac_template_to_text(data);
	
	this.save_page(mw.config.get('wgPageName'), text, 'VIAF data &#x21c9; local Authority Control' );

}
	
VIAFDataGrabber.prototype.get_raw_page = function(pagetitle, callback){
	var myself = this;
	
	//get the current page text
	$.ajax({
		url: mw.util.wikiScript( 'index' ),
		data: {
			action: 'raw',
			title: pagetitle,
		},
		cache:false,
		success:function(data){
			$.proxy(callback, myself)(data);
		},
		error:function(jqXHR, textStatus, errorThrown){
			alert('Page text could not be loaded: [[' + pagetitle + ']]');
		},
	});
}

VIAFDataGrabber.prototype.save_page = function(title, content, summary, go_to_diff){
	var myself = this;
	
	$.ajax({
		url: mw.util.wikiScript( 'api' ),
		data: {
			format: 'json',
			action: 'edit',
			title: title,
			summary: summary,
			text: content,
			token: mw.user.tokens.get( 'csrfToken' )
		},
		dataType: 'json',
		type: 'POST',
		success: function( data ) {
			if ( data && data.edit && data.edit.result == 'Success' ) {
				window.location = mw.config.get('wgServer') + wgScriptPath + '/index.php?' + $.param({ title: mw.config.get('wgPageName'), diff:data.edit.newrevid , oldid:data.edit.oldrevid  });
			} else if ( data && data.error ) {
				alert( 'Error: API returned error code "' + data.error.code + '": ' + data.error.info );
			} else {
				alert( 'Error: Unknown result from API.' );
			}
		},
		error: function( xhr ) {
			$.alert( 'Error: Request failed.' );
		}
	});
}

$( document ).ready( function() {
	
	if ( $.inArray( mw.config.get( 'wgNamespaceNumber' ), [ 0, 2, 14, 100, 102, 114 ] ) > -1 ){ //only trigger in sensible namespaces
		VIAFDataGrabberInstance = new VIAFDataGrabber();
	}
});