User:B20180/common.js
Jump to navigation
Jump to search
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/**
* Internationalisation for ancestry widget
*/
var ancestry_i18n = {
en: {
link: 'Show Ancestry',
unknown: 'Unknown',
title: 'Family Ancestry',
loading: 'Loading ancestry...',
remaining: '%d remaining',
jump: 'Load ancestry of: ',
select: 'Select...',
spouses: 'Spouses',
children: 'Children',
none: 'None'
},
fr: {
link: 'Généalogie',
unknown: 'Inconnu',
title: 'Arbre généalogique',
loading: 'Chargement...',
remaining: '%d à charger',
jump: 'Chargez : ',
select: 'Sélectionnez...',
spouses: 'Conjoints',
children: 'Enfants',
none: 'Aucun'
},
id: {
link: 'Lihat Silsilah',
unknown: 'Tidak diketahui',
title: 'Silsilah Keluarga',
loading: 'Memuat silsilah...',
remaining: 'tinggal %d lagi',
jump: 'Silsilah dari: ',
select: 'Pilih...',
spouses: 'Pasangan',
children: 'Anak',
none: 'Tidak ada'
},
min: {
link: 'Lihek Silsilah',
unknown: 'Indak diketahui',
title: 'Silsilah Kaluargo',
loading: 'Mamuek silsilah...',
remaining: 'tingga %d lai',
jump: 'Silsilah dari: ',
select: 'Piliah...',
spouses: 'Pasangan',
children: 'Anak',
none: 'Indak ado'
}
};
/**
* jQuery UI widget for displaying and navigating family
* pedigree charts of person items on wikidata
*
* @author [[User:Ch1902]]
*/
$.widget('wd.ancestry', {
i18n: ancestry_i18n,
DAD: 'P22',
MUM: 'P25',
KID: 'P40',
SPOUSE: 'P26',
SEX: 'P21',
MALE: 6581097,
FEMALE: 6581072,
options: {
levels: 4,
lang: 'en',
boxsize: 165,
truncate: 22
},
firstRun: true,
dialog: null,
family: {},
deferred: null,
counter: 1,
history: {},
_create: function ()
{
var self = this, root = this._blankItem(), lang = this.options.lang;
// clamp(min, max, i)
this.options.levels = Math.max(2, Math.min(6, this.options.levels));
// set tree root
root.id = wbEntityId;
$.each(wb.entity.claims || [], function (_, claim) {
if (claim.getMainSnak().getPropertyId() === self.SEX)
root.gender = claim.getMainSnak().getValue().getNumericId();
});
// dialog
this.dialog = $('<div/>').dialog({
draggable: true,
modal: true,
title: (this.i18n[this.options.lang] || this.i18n.en).title,
autoOpen: false,
dialogClass: 'ancestry-dialog'
});
this.dialog.append($('<div/>').addClass('ancestry-content'));
this.dialog.append($('<div/>').addClass('ancestry-status').html(' '));
// events
this.dialog.on('click', 'td:has(a)', function (e) {
self._showOtherRelations(e);
});
// custom css
mw.util.addCSS(
'.ancestry-dialog td.person { min-width: ' + this.options.boxsize + 'px; max-width: ' + this.options.boxsize + 'px; }' +
'.ancestry-dialog span.unknown { font-style: italic; }' +
'.ancestry-dialog .ui-dialog-content { text-align: center; background: #FCFCFC }' +
'.ancestry-dialog table { margin: 0px auto; }' +
'.ancestry-dialog .ancestry-status { text-align: left; }' +
'.ancestry-dialog td.g0 { background: lightgrey }' +
'.ancestry-dialog td.g' + self.MALE + ' { background: #BADDFF }' +
'.ancestry-dialog td.g' + self.FEMALE + ' { background: #FFBADE }' +
'/*.ancestry-dialog select { width: 180px; }*/'
);
this.element.on('click', function (e) {
e.preventDefault();
if (self.firstRun) {
self.firstRun = false;
self._toggleDialog(true);
self._loadNewRoot(root);
} else {
self._toggleDialog(!self.dialog.is(':visible'));
}
return false;
});
},
_showLoader: function ()
{
var msg = (this.i18n[this.options.lang] || this.i18n.en).remaining;
this._setDialogContent(false); // empty
this._setDialogContent('loading');
this._setDialogContent(msg.replace(/\%d/, '<strong id="ancestry-remaining">' + Math.pow(2, this.options.levels) + '</strong>'));
this._setDialogContent('<br><br>');
this._setDialogContent($.createSpinner({size: 'large', type: 'block'}));
},
_sizeDialogToTree: function ()
{
var w = (2 * 10) + (this.options.boxsize / 2),
h = 20 * ((Math.pow(2, this.options.levels - 1) / 2) - 2);
w += (this.options.boxsize * 0.75 * this.options.levels - 1);
w += ((this.options.levels - 1) * 5);
for (var l = this.options.levels - 1; l > 0; l--)
h += (25 * Math.pow(2, l));
h += 40 + 30 + 25; // title + status + padding
this.dialog.dialog('option', 'minWidth', w);
this.dialog.dialog('option', 'minHeight', h);
this.dialog.dialog('option', 'position', {my: 'center center', at: 'center center', of: window});
},
_toggleDialog: function (show)
{
this.dialog.dialog(show ? 'open' : 'close');
},
_setDialogContent: function (content)
{
this._doDialogContent('.ancestry-content', content);
},
_setDialogStatus: function (content)
{
this._doDialogContent('.ancestry-status', content);
},
_doDialogContent: function (selector, content)
{
var self = this;
if (content === false) // empty
{
this.dialog.find(selector).empty();
}
else if ($.type(content) === 'array')
{
$.each(content, function (_, value) {
self._doDialogContent(selector, value);
});
}
else if ($.type(content) === 'object')
{
this.dialog.find(selector).append(content);
}
else
{
this.dialog.find(selector).append((this.i18n[this.options.lang] || this.i18n.en)[content] || content);
}
},
_initFamilyData: function (root)
{
this.family = {
1: root
};
},
_loadNewRoot: function (root)
{
root.spouses = [];
root.children = [];
this._initFamilyData(root);
this._showLoader();
this._setDialogStatus(false);
this.deferred = null;
this.counter = 1;
this._loadAncestry();
},
_loadAncestry: function ()
{
var self = this;
this.deferred = new $.Deferred();
this.deferred.done(function () {
self._loadAhnentafel().done(function (data) {
self._sizeDialogToTree();
self._showFamilyTree($(data.parse.text['*']));
});
});
this._loadFamilyTree();
},
_showFamilyTree: function (tpl)
{
var i = 1, max = Math.pow(2, this.options.levels);
for (i = 1; i < max; i++)
{
var span = tpl.find('span:contains("%' + i + '$s")'), cell = span.parents('td'), peep = this.family[i],
label = peep.id ? $('<a/>') : $('<em/>');
if (peep.id)
label.attr({href: mw.util.getUrl(peep.id.toUpperCase())});
if (peep.label.length > this.options.truncate && this.options.truncate > 0)
label.html(peep.label.substr(0, this.options.truncate - 1) + '…');
else
label.html(peep.label);
label.attr({title: peep.label});
cell.addClass('g' + peep.gender).data('person', peep);
cell.empty().append(label).addClass('person');
}
this._setDialogContent(false); // empty
this._setDialogContent(tpl);
this._toggleDialog(true);
},
_loadFamilyTree: function ()
{
var self = this, lang = this.options.lang, max = Math.pow(2, this.options.levels), cur = this.counter,
person = this.family[cur];
if (cur >= max || this.deferred.state() !== 'pending')
{
if (this.deferred.state() !== 'resolved')
this.deferred.resolve();
return;
}
this._doDialogContent('#ancestry-remaining', false);
this._doDialogContent('#ancestry-remaining', max - cur);
if (person.id !== false)
{
$.ajax({
url: mw.util.wikiScript('api'),
type: 'GET',
dataType: 'json',
data: {action: 'wbgetentities', format: 'json', languages: lang, ids: person.id, props: 'labels|claims'},
success: function (data)
{
var entity = data.entities[person.id], labels = entity.labels || {}, claims = entity.claims || {},
dads = claims[self.DAD] || [], mums = claims[self.MUM] || [], sex = claims[self.SEX] || [],
spouses = claims[self.SPOUSE] || [], kids = claims[self.KID] || [];
self.family[cur].label = labels[lang] && labels[lang].value || person.id.toUpperCase();
self.family[cur * 2] = self._blankItem();
self.family[cur * 2 + 1] = self._blankItem();
if (dads.length === 1)
{
var dad = wb.Snak.newFromJSON(dads[0].mainsnak);
if (dad.getValue().getType() === 'wikibase-entityid')
{
self.family[cur].father = 'Q' + dad.getValue().getNumericId();
self.family[cur * 2].id = 'Q' + dad.getValue().getNumericId();
self.family[cur * 2].gender = self.MALE;
}
}
if (mums.length === 1)
{
var mum = wb.Snak.newFromJSON(mums[0].mainsnak);
if (mum.getValue().getType() === 'wikibase-entityid')
{
self.family[cur].mother = 'Q' + mum.getValue().getNumericId();
self.family[cur * 2 + 1].id = 'Q' + mum.getValue().getNumericId();
self.family[cur * 2 + 1].gender = self.FEMALE;
}
}
// only if needed
if (self.family[cur].gender === 0 && sex.length)
{
var gen = wb.Snak.newFromJSON(sex[0].mainsnak);
if (gen.getValue().getType() === 'wikibase-entityid')
{
self.family[cur].gender = gen.getValue().getNumericId();
}
}
if (spouses.length)
{
$.each(spouses, function (_, spouse) {
var sp = wb.Snak.newFromJSON(spouse.mainsnak);
if (sp.getValue().getType() === 'wikibase-entityid')
{
self.family[cur].spouses.push('Q' + sp.getValue().getNumericId());
}
});
}
if (kids.length)
{
$.each(kids, function (_, kid) {
var child = wb.Snak.newFromJSON(kid.mainsnak);
if (child.getValue().getType() === 'wikibase-entityid')
{
self.family[cur].children.push('q' + child.getValue().getNumericId());
}
});
}
self.counter++;
self._loadFamilyTree(); // recurse
},
fail: function ()
{
self.family[cur * 2] = self._blankItem();
self.family[cur * 2 + 1] = self._blankItem();
self.counter++;
self._loadFamilyTree(); // recurse
}
});
}
else
{
this.family[cur * 2] = this._blankItem();
this.family[cur * 2 + 1] = this._blankItem();
this.counter++;
this._loadFamilyTree(); // recurse
}
},
_showOtherRelations: function (e)
{
var self = this, cell = $(e.currentTarget), data = cell.data(), person = data.person, spouses = person.spouses || [],
kids = person.children || [], holder, lang = this.options.lang, dropdown, kgroup, sgroup;
// allow click throughs
if (e.ctrlKey || e.shiftKey || e.altKey)
return true;
e.preventDefault();
// cached
if (data.status)
{
this._setDialogStatus(false);
this._setDialogStatus(data.status);
return false;
}
// holds selected person each time
dropdown = $('<select/>').append(
$('<option/>').text((this.i18n[lang] || this.i18n.en).select)
).append(
$('<option/>').attr('value', JSON.stringify(person)).text(person.label)
).on('click', 'option', function (e) {
var val = $(this).val();
if (val) {
self._loadNewRoot(JSON.parse(val));
}
});
sgroup = $('<optgroup/>').attr('label', (this.i18n[lang] || this.i18n.en).spouses).appendTo(dropdown);
kgroup = $('<optgroup/>').attr('label', (this.i18n[lang] || this.i18n.en).children).appendTo(dropdown);
if (spouses.length < 1)
sgroup.append('<option>' + ((this.i18n[lang] || this.i18n.en).none) + '</option>');
if (kids.length < 1)
kgroup.append('<option>' + ((this.i18n[lang] || this.i18n.en).none) + '</option>');
holder = $('<div/>').text((this.i18n[lang] || this.i18n.en).jump);
if (spouses.length > 0 || kids.length > 0)
{
$.when(this._loadOtherRelations(spouses.concat(kids))).done(function (data) {
$.each(spouses, function (_, spouse) {
var option, ent = data.entities[spouse], labels = ent.labels || {}, item = self._blankItem();
if (data.entities[spouse])
{
item.label = labels[lang] && labels[lang].value || spouse.toUpperCase();
item.id = spouse;
option = $('<option/>').text(item.label);
option.attr('value', JSON.stringify(item));
option.appendTo(sgroup);
}
});
$.each(kids, function (_, kid) {
var option, ent = data.entities[kid], labels = ent.labels || {}, item = self._blankItem();
if (data.entities[kid])
{
item.label = labels[lang] && labels[lang].value || kid.toUpperCase();
item.id = kid;
option = $('<option/>').text(item.label);
option.attr('value', JSON.stringify(item));
option.appendTo(kgroup);
}
});
});
}
holder.append(dropdown);
cell.data('status', holder);
this._setDialogStatus(false);
this._setDialogStatus(holder);
return false;
},
_loadOtherRelations: function (ids)
{
return $.ajax({
url: mw.util.wikiScript('api'),
type: 'GET',
dataType: 'json',
data: {
action: 'wbgetentities',
languages: this.options.lang,
format: 'json',
props: 'info|labels',
ids: ids.join('|')
}
});
},
_loadAhnentafel: function ()
{
// use CORS because text.length might be > 255 so use POST
return $.ajax({
url: '//en.wikipedia.org/w/api.php',
type: 'POST',
dataType: 'json',
data: {
origin: window.location.protocol + '//' + window.location.host,
action: 'parse',
format: 'json',
prop: 'text',
disablepp: 1,
text: this._buildAhnentafel()
}
});
},
_buildAhnentafel: function ()
{
var i, num, max = Math.pow(2, this.options.levels), tpl = '{{ahnentafel-compact' + this.options.levels + '';
tpl += '|style=font-size: 90%; line-height: 110%;|border=1|boxstyle=padding-top: 2px; padding-bottom: 2px;';
// just to replace later
for (i = 1; i < max; i++)
{
tpl += '|' + i + '= <span class="person">%' + i + '$s</span>';
}
tpl += '}}';
return tpl;
},
_blankItem: function ()
{
var i18n = this.i18n, lang = this.options.lang;
return {
id: false,
label: (i18n[lang] || i18n.en).unknown,
father: false,
mother: false,
gender: 0,
spouses: [],
children: []
};
},
_setOption: function (option, value)
{
switch (option)
{
case 'levels':
value = Math.max(2, Math.min(6, value));
break;
case 'root':
var root = this._blankItem();
root.id = value;
this._loadNewRoot(root);
break;
}
this._super(option, value);
}
});
var ancestry_deps = [
'jquery', 'jquery.ui',
'jquery.ui',
'jquery.spinner'
];
var opts = typeof ancestry_opts === 'object' ? ancestry_opts : {lang: wgUserLanguage};
if (mw.config.get('wgNamespaceNumber') === 0 && mw.config.get('wgAction') === 'view') {
mw.loader.using(ancestry_deps, function () {
$(function () {
var tool = mw.util.addPortletLink('p-tb', '#', (ancestry_i18n[opts.lang || wgUserLanguage] || ancestry_i18.en).link, 't-ancestry');
$(tool).find('a').ancestry(opts);
});
});
}