మీడియావికీ:Gadget-addViafData.js
గమనిక: భద్రపరచిన తర్వాత, మార్పులను చూడాలంటే మీ విహారిణి కోశాన్ని తీసేయాల్సిరావచ్చు.
- ఫైర్ఫాక్స్ / సఫారి: 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, ' 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 ⇉ 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();
}
});