MediaWiki:Gadget-templating.js: Difference between revisions
Appearance
No edit summary |
No edit summary |
||
| Line 1: | Line 1: | ||
/* eslint-env browser, es6 */ | |||
/* global mw, $ */ | |||
mw.loader.using(['mediawiki.api']).then(function () { | |||
(async function () { | |||
const api = new mw.Api(); | |||
const userLang = mw.config.get('wgUserLanguage') || 'en'; | |||
// ---- 1) CONFIG: how we find your template items ---- | |||
// Use exactly what works in the search box: | |||
const SEARCH_QUERY = 'haswbstatement:P1=Q196450'; | |||
// If needed, you can hardcode Q-IDs instead and skip searchTemplates(). | |||
function statementExists(property, value) { | // ---- 2) HELPERS used by the builder ---- | ||
const chunk = (arr, n) => { const out=[]; for (let i=0;i<arr.length;i+=n) out.push(arr.slice(i,i+n)); return out; }; | |||
const getBestLabel = (labels) => | |||
(labels?.[userLang]?.value) || (labels && Object.values(labels)[0]?.value) || undefined; | |||
// decide if "_" means "placeholder" for string-like types | |||
const isPlaceholderSnak = (snak) => { | |||
if (!snak || snak.snaktype !== 'value' || !snak.datavalue) return false; | |||
const dt = snak.datatype; | |||
const v = snak.datavalue.value; | |||
if (dt === 'string' || dt === 'external-id' || dt === 'url') return v === '_'; | |||
if (dt === 'monolingualtext') return v?.text === '_'; | |||
return false; | |||
}; | |||
const cleanTemplateLabel = (raw) => (raw || '') | |||
.replace(/^RDA\s*/i, '') | |||
.replace(/\s*Template$/i, '') | |||
.trim(); | |||
const slugify = (s) => (s || '') | |||
.normalize('NFKD') | |||
.replace(/[\u0300-\u036f]/g, '') | |||
.toLowerCase() | |||
.replace(/[^a-z0-9]+/g, '-') | |||
.replace(/^-+|-+$/g, ''); | |||
// ---- 3) BUILDER: search → fetch entities → fetch property meta → transform → WbTemplates ---- | |||
async function searchTemplates(query) { | |||
const res = await api.get({ | |||
action: 'query', | |||
list: 'search', | |||
srsearch: query, | |||
// make API behave like Special:Search | |||
srbackend: 'CirrusSearch', | |||
srqiprofile: 'wikibase', | |||
srlimit: 50, | |||
formatversion: 2 | |||
}); | |||
return (res.query?.search || []) | |||
.map(h => (h.title.match(/Q\d+$/) || [])[0]) | |||
.filter(Boolean); | |||
} | |||
async function fetchEntities(ids) { | |||
const map = {}; | |||
for (const group of chunk(ids, 50)) { | |||
const res = await api.get({ | |||
action: 'wbgetentities', | |||
ids: group.join('|'), | |||
props: 'labels|claims', | |||
languages: userLang | |||
}); | |||
Object.assign(map, res.entities || {}); | |||
} | |||
return map; | |||
} | |||
async function fetchPropertyMeta(entityMap) { | |||
const pids = new Set(); | |||
Object.values(entityMap).forEach(e => Object.keys(e.claims || {}).forEach(pid => pids.add(pid))); | |||
const map = {}; | |||
for (const group of chunk([...pids], 50)) { | |||
const res = await api.get({ | |||
action: 'wbgetentities', | |||
ids: group.join('|'), | |||
props: 'labels|datatype', | |||
languages: userLang | |||
}); | |||
Object.assign(map, res.entities || {}); | |||
} | |||
// normalize | |||
const meta = {}; | |||
for (const [pid, ent] of Object.entries(map)) { | |||
meta[pid] = { | |||
id: pid, | |||
label: getBestLabel(ent.labels) || pid, | |||
datatype: ent.datatype // e.g., 'wikibase-item', 'string', 'globe-coordinate' | |||
}; | |||
} | |||
return meta; | |||
} | |||
function transformToTemplate(entity, propMeta) { | |||
const rawLabel = getBestLabel(entity.labels) || entity.id; | |||
const label = cleanTemplateLabel(rawLabel); | |||
const prefilledClaims = []; | |||
const userDefinedClaims = []; | |||
for (const [pid, statements] of Object.entries(entity.claims || {})) { | |||
if (!Array.isArray(statements) || !statements.length) continue; | |||
const prop = propMeta[pid] || { id: pid, label: pid, datatype: 'string' }; | |||
// first non-placeholder statement (if any) | |||
const firstNonPlaceholder = statements | |||
.map(st => st.mainsnak) | |||
.find(ms => ms && ms.snaktype === 'value' && !isPlaceholderSnak(ms)); | |||
if (firstNonPlaceholder) { | |||
// NOTE: to keep compatibility with your prefillStatement (which assumes wikibase-item), | |||
// we only prefill non-placeholder values for wikibase-item. Others go to userDefined. | |||
if (prop.datatype === 'wikibase-item') { | |||
const v = firstNonPlaceholder.datavalue?.value; // {id: 'Qxx'} | |||
prefilledClaims.push({ | |||
property: { id: prop.id, label: prop.label, datatype: prop.datatype }, | |||
value: { mainVal: { id: v?.id, datatype: 'wikibase-item' } } | |||
}); | |||
} else { | |||
userDefinedClaims.push({ property: { id: prop.id, label: prop.label }, datatype: prop.datatype }); | |||
} | |||
} else { | |||
// all statements are "_" placeholders → user fills it | |||
userDefinedClaims.push({ property: { id: prop.id, label: prop.label }, datatype: prop.datatype }); | |||
} | |||
} | |||
return { key: slugify(label), label, prefilledClaims, userDefinedClaims }; | |||
} | |||
async function buildWbTemplatesFromQuery(query) { | |||
const qids = await searchTemplates(query); | |||
if (!qids.length) return {}; | |||
const entities = await fetchEntities(qids); | |||
const propMeta = await fetchPropertyMeta(entities); | |||
const map = {}; | |||
Object.values(entities).forEach(e => { | |||
const tpl = transformToTemplate(e, propMeta); | |||
map[tpl.key] = { label: tpl.label, prefilledClaims: tpl.prefilledClaims, userDefinedClaims: tpl.userDefinedClaims }; | |||
}); | |||
return map; | |||
} | |||
// ---- 4) Build WbTemplates dynamically ---- | |||
// If you need to debug, console.log(WbTemplates) after the await. | |||
const WbTemplates = await buildWbTemplatesFromQuery(SEARCH_QUERY); | |||
// Expose globally so your existing helpers can access it | |||
window.WbTemplates = WbTemplates; | |||
// ========================= | |||
// YOUR EXISTING FUNCTIONS | |||
// (left untouched, just moved below the builder so they can use WbTemplates) | |||
// ========================= | |||
function statementExists(property, value) { | |||
items.toArray().some(item => !!$(item).data().propertyId && $(item).data().statementgroupview.options.value.getKey() === property && | |||
$(item).data().statementgroupview.options.value.getItemContainer().toArray().some(claimItem => | $(item).data().statementgroupview.options.value.getItemContainer().toArray().some(claimItem => | ||
claimItem.getClaim().getMainSnak().getValue().getSerialization() === value)); | |||
} | } | ||
function getPropertyEntity(property) { | function getPropertyEntity(property) { | ||
return { | |||
"id": `${property.id}`, | "id": `${property.id}`, | ||
"title": `Property:${property.id}`, | "title": `Property:${property.id}`, | ||
"datatype": property.datatype, | "datatype": property.datatype, | ||
"label": property.label, | "label": property.label, | ||
}; | |||
} | } | ||
function prefillStatement(claim, onlyProperty) { | function prefillStatement(claim, onlyProperty) { | ||
if (onlyProperty) { | |||
pendingStatement(claim.property); | |||
return; | |||
} | |||
let statementListView = $(".wikibase-statementgrouplistview").first().data().wikibaseStatementgrouplistview; | |||
statementListView.enterNewItem(); | |||
let items = statementListView.listview.items(); | |||
let item = items.last().data(); | |||
let sv = item.statementgroupview.statementlistview._listview.items().first().data().statementview; | |||
let snak = sv.$mainSnak.data().snakview; | |||
let es = getPropertyEntity(claim.property); | |||
let selector = snak._getPropertySelector(); | |||
selector.element.val(es.label); | |||
selector.element.attr("title", es.label); | |||
selector._trigger('change'); | |||
selector._select(es); | |||
snak._variation._valueView.element.one( | |||
snak._variation._valueView.widgetEventPrefix + 'afterstartediting', | snak._variation._valueView.widgetEventPrefix + 'afterstartediting', | ||
function() { | function () { | ||
let valSelector = snak._variation._valueView._expert.$input.data().entityselector; | |||
let valval = getPropertyEntity(claim.value.mainVal); | |||
valSelector.element.val(valval.label); | |||
valSelector._trigger("change"); | |||
valSelector._select(valval); | |||
if (!!claim.qualifiers) { | |||
let qlistview = sv._qualifiers; | |||
qlistview.enterNewItem(); | |||
var qsnaklistview = qlistview.value()[qlistview.value().length - 1]; | |||
qsnaklistview.enterNewItem(); | |||
let qslv = $(sv._qualifiers.items().first()).data().snaklistview; | |||
let qsnak = qslv._listview.items().first().data().snakview; | |||
let qsel = qsnak._getPropertySelector(); | |||
qsel.element.val(es.label); | |||
qsel._trigger('change'); | |||
qsel._select(es); | |||
qsnak._variation._valueView.element.one( | |||
qsnak._variation._valueView.widgetEventPrefix + 'afterstartediting', | |||
function () { | |||
let qvalSelector = qsnak._variation._valueView._expert.$input.data().entityselector; | |||
let qvalval = getPropertyEntity(claim.value.mainVal); | |||
qvalSelector.element.val(qvalval.label); | |||
qvalSelector._trigger("change"); | |||
valSelector._trigger("change"); | |||
qvalSelector._select(qvalval); | |||
} | |||
); | |||
qsnak._variation._valueView.element.one( | |||
qsnak._variation._valueView.widgetEventPrefix + 'change', | |||
function () { | |||
let toolbar = item.statementgroupview.statementlistview._listview.items().last().data().edittoolbar; | |||
toolbar._controller.stopEditing(false); | |||
}); | |||
} | |||
} | } | ||
); | |||
snak._variation._valueView.element.one( | |||
snak._variation._valueView.widgetEventPrefix + 'change', | snak._variation._valueView.widgetEventPrefix + 'change', | ||
function() { | function () { | ||
let toolbar = item.statementgroupview.statementlistview._listview.items().last().data().edittoolbar; | |||
toolbar._controller.stopEditing(false); | |||
}); | }); | ||
} | } | ||
function pendingStatement(claim) { | function pendingStatement(claim) { | ||
let prop = claim.property; | |||
return new Promise((resolve, reject) => { | |||
let statementListView = $(".wikibase-statementgrouplistview") | |||
.first() | |||
.data().wikibaseStatementgrouplistview; | |||
statementListView.enterNewItem(); | |||
let items = statementListView.listview.items(); | |||
let item = items.last().data(); | |||
let sv = item.statementgroupview.statementlistview._listview.items(); | |||
let snak = $(sv).data().statementview.$mainSnak.data().snakview; | |||
let selector = snak._getPropertySelector(); | |||
// Set up event listeners and chain them to resolve when finished. | |||
selector.element.on('entityselectorselected', (event, entityId) => { | |||
$(snak._variation).on("afterdraw", function () { | |||
snak._variation._valueView.element.one( | |||
snak._variation._valueView.widgetEventPrefix + 'afterstartediting', | |||
function () { | |||
if (claim.datatype === "wikibase-item") { | |||
let valSelector = snak._variation._valueView._expert.$input.data().entityselector; | |||
let jqe = snak._variation._valueView._expert.$input.data().jqueryEventSpecialEachchange; | |||
valSelector.element.val("temp"); | |||
valSelector._trigger("change"); | |||
valSelector._select({ id: "Q80", datatype: "wikibase-item" }); | |||
jqe.handlers[0].call(); | |||
jqe.handlers[1].call(); | |||
function applyTemplate(templateName) { | valSelector.element.val(""); | ||
valSelector._trigger("change"); | |||
jqe.handlers[0].call(); | |||
jqe.handlers[1].call(); | |||
} | |||
else { | |||
let valSelector = snak._variation._valueView._expert.$input.data().inputautoexpand; | |||
let jqe = snak._variation._valueView._expert.$input.data().jqueryEventSpecialEachchange; | |||
if (claim.datatype === "globe-coordinate") { | |||
valSelector.$input.val("0,0"); | |||
jqe.handlers[0].call(); | |||
jqe.handlers[1].call(); | |||
} | |||
else { | |||
valSelector.$input.val("__"); // shows the placeholder behavior | |||
jqe.handlers[0].call(); | |||
jqe.handlers[1].call(); | |||
} | |||
valSelector.$input.val(""); | |||
jqe.handlers[0].call(); | |||
jqe.handlers[1].call(); | |||
} | |||
// Once processing is complete, resolve the Promise. | |||
resolve(); | |||
} | |||
); | |||
}); | |||
}); | |||
selector.element.val(prop.label); | |||
selector._trigger('change'); | |||
selector._select(prop); | |||
}); | |||
} | |||
function addDetails(templateName) { | |||
var claims = WbTemplates[templateName].userDefinedClaims; | |||
var chain = Promise.resolve(); | |||
claims.forEach(function (claim) { | |||
chain = chain.then(function () { | |||
return pendingStatement(claim); | |||
}); | |||
}); | |||
return chain; | |||
} | |||
function applyTemplate(templateName) { | |||
WbTemplates[templateName].prefilledClaims.forEach(claim => { | |||
prefillStatement(claim, false); | prefillStatement(claim, false); | ||
}); | }); | ||
} | } | ||
// ---- 5) UI: render only after WbTemplates is ready ---- | |||
function initTemplatingUI() { | |||
// If nothing came back, don't render the UI | |||
if (!WbTemplates || !Object.keys(WbTemplates).length) { | |||
console.warn('No templates found for query:', SEARCH_QUERY); | |||
return; | |||
} | |||
$(".wikibase-entitytermsview").append(` | $(".wikibase-entitytermsview").append(` | ||
<div class="wikibase-templating-section"> | <div class="wikibase-templating-section"> | ||
<div class="wikibase-templating-section-title"><span class="magic-wand-icon"></span> RDA Entity template</div> | |||
<div class="wikibase-templating-help"><span class="settings-text">Select an RDA entity class to apply predefined template entries and additional entity details.</span></div> | |||
<div class="wikibase-templating-form oo-ui-labelElement oo-ui-fieldLayout"> | |||
<div class=""><label for="wbtemplate">Entity class</label></div> | |||
<div> | |||
<select id="wbtemplate" class="cdx-select"></select> | |||
</div> | |||
</div> | |||
<div class="mw-htmlform-submit-buttons"> | |||
<button id="applyTemplateBtn" class="cdx-button">Apply template</button> | |||
<button id="addDetailsBtn" class="cdx-button">Add details</button> | |||
</div> | |||
</div> | </div> | ||
`); | `); | ||
Object.entries(WbTemplates).forEach( | // Populate selector | ||
Object.entries(WbTemplates).forEach(([key, tpl]) => { | |||
$('#wbtemplate').append($('<option>', { value: key, text: tpl.label })); | |||
}); | |||
// Wire buttons | |||
$("#applyTemplateBtn").on("click", function () { | |||
applyTemplate($("#wbtemplate").find(":selected").val()); | |||
}); | |||
$("#addDetailsBtn").on("click", function () { | |||
addDetails($("#wbtemplate").find(":selected").val()); | |||
}); | |||
} | |||
// finally render | |||
initTemplatingUI(); | |||
})().catch(console.error); | |||
}) | |||
}); | }); | ||
Revision as of 18:23, 17 September 2025
/* eslint-env browser, es6 */
/* global mw, $ */
mw.loader.using(['mediawiki.api']).then(function () {
(async function () {
const api = new mw.Api();
const userLang = mw.config.get('wgUserLanguage') || 'en';
// ---- 1) CONFIG: how we find your template items ----
// Use exactly what works in the search box:
const SEARCH_QUERY = 'haswbstatement:P1=Q196450';
// If needed, you can hardcode Q-IDs instead and skip searchTemplates().
// ---- 2) HELPERS used by the builder ----
const chunk = (arr, n) => { const out=[]; for (let i=0;i<arr.length;i+=n) out.push(arr.slice(i,i+n)); return out; };
const getBestLabel = (labels) =>
(labels?.[userLang]?.value) || (labels && Object.values(labels)[0]?.value) || undefined;
// decide if "_" means "placeholder" for string-like types
const isPlaceholderSnak = (snak) => {
if (!snak || snak.snaktype !== 'value' || !snak.datavalue) return false;
const dt = snak.datatype;
const v = snak.datavalue.value;
if (dt === 'string' || dt === 'external-id' || dt === 'url') return v === '_';
if (dt === 'monolingualtext') return v?.text === '_';
return false;
};
const cleanTemplateLabel = (raw) => (raw || '')
.replace(/^RDA\s*/i, '')
.replace(/\s*Template$/i, '')
.trim();
const slugify = (s) => (s || '')
.normalize('NFKD')
.replace(/[\u0300-\u036f]/g, '')
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '');
// ---- 3) BUILDER: search → fetch entities → fetch property meta → transform → WbTemplates ----
async function searchTemplates(query) {
const res = await api.get({
action: 'query',
list: 'search',
srsearch: query,
// make API behave like Special:Search
srbackend: 'CirrusSearch',
srqiprofile: 'wikibase',
srlimit: 50,
formatversion: 2
});
return (res.query?.search || [])
.map(h => (h.title.match(/Q\d+$/) || [])[0])
.filter(Boolean);
}
async function fetchEntities(ids) {
const map = {};
for (const group of chunk(ids, 50)) {
const res = await api.get({
action: 'wbgetentities',
ids: group.join('|'),
props: 'labels|claims',
languages: userLang
});
Object.assign(map, res.entities || {});
}
return map;
}
async function fetchPropertyMeta(entityMap) {
const pids = new Set();
Object.values(entityMap).forEach(e => Object.keys(e.claims || {}).forEach(pid => pids.add(pid)));
const map = {};
for (const group of chunk([...pids], 50)) {
const res = await api.get({
action: 'wbgetentities',
ids: group.join('|'),
props: 'labels|datatype',
languages: userLang
});
Object.assign(map, res.entities || {});
}
// normalize
const meta = {};
for (const [pid, ent] of Object.entries(map)) {
meta[pid] = {
id: pid,
label: getBestLabel(ent.labels) || pid,
datatype: ent.datatype // e.g., 'wikibase-item', 'string', 'globe-coordinate'
};
}
return meta;
}
function transformToTemplate(entity, propMeta) {
const rawLabel = getBestLabel(entity.labels) || entity.id;
const label = cleanTemplateLabel(rawLabel);
const prefilledClaims = [];
const userDefinedClaims = [];
for (const [pid, statements] of Object.entries(entity.claims || {})) {
if (!Array.isArray(statements) || !statements.length) continue;
const prop = propMeta[pid] || { id: pid, label: pid, datatype: 'string' };
// first non-placeholder statement (if any)
const firstNonPlaceholder = statements
.map(st => st.mainsnak)
.find(ms => ms && ms.snaktype === 'value' && !isPlaceholderSnak(ms));
if (firstNonPlaceholder) {
// NOTE: to keep compatibility with your prefillStatement (which assumes wikibase-item),
// we only prefill non-placeholder values for wikibase-item. Others go to userDefined.
if (prop.datatype === 'wikibase-item') {
const v = firstNonPlaceholder.datavalue?.value; // {id: 'Qxx'}
prefilledClaims.push({
property: { id: prop.id, label: prop.label, datatype: prop.datatype },
value: { mainVal: { id: v?.id, datatype: 'wikibase-item' } }
});
} else {
userDefinedClaims.push({ property: { id: prop.id, label: prop.label }, datatype: prop.datatype });
}
} else {
// all statements are "_" placeholders → user fills it
userDefinedClaims.push({ property: { id: prop.id, label: prop.label }, datatype: prop.datatype });
}
}
return { key: slugify(label), label, prefilledClaims, userDefinedClaims };
}
async function buildWbTemplatesFromQuery(query) {
const qids = await searchTemplates(query);
if (!qids.length) return {};
const entities = await fetchEntities(qids);
const propMeta = await fetchPropertyMeta(entities);
const map = {};
Object.values(entities).forEach(e => {
const tpl = transformToTemplate(e, propMeta);
map[tpl.key] = { label: tpl.label, prefilledClaims: tpl.prefilledClaims, userDefinedClaims: tpl.userDefinedClaims };
});
return map;
}
// ---- 4) Build WbTemplates dynamically ----
// If you need to debug, console.log(WbTemplates) after the await.
const WbTemplates = await buildWbTemplatesFromQuery(SEARCH_QUERY);
// Expose globally so your existing helpers can access it
window.WbTemplates = WbTemplates;
// =========================
// YOUR EXISTING FUNCTIONS
// (left untouched, just moved below the builder so they can use WbTemplates)
// =========================
function statementExists(property, value) {
items.toArray().some(item => !!$(item).data().propertyId && $(item).data().statementgroupview.options.value.getKey() === property &&
$(item).data().statementgroupview.options.value.getItemContainer().toArray().some(claimItem =>
claimItem.getClaim().getMainSnak().getValue().getSerialization() === value));
}
function getPropertyEntity(property) {
return {
"id": `${property.id}`,
"title": `Property:${property.id}`,
"datatype": property.datatype,
"label": property.label,
};
}
function prefillStatement(claim, onlyProperty) {
if (onlyProperty) {
pendingStatement(claim.property);
return;
}
let statementListView = $(".wikibase-statementgrouplistview").first().data().wikibaseStatementgrouplistview;
statementListView.enterNewItem();
let items = statementListView.listview.items();
let item = items.last().data();
let sv = item.statementgroupview.statementlistview._listview.items().first().data().statementview;
let snak = sv.$mainSnak.data().snakview;
let es = getPropertyEntity(claim.property);
let selector = snak._getPropertySelector();
selector.element.val(es.label);
selector.element.attr("title", es.label);
selector._trigger('change');
selector._select(es);
snak._variation._valueView.element.one(
snak._variation._valueView.widgetEventPrefix + 'afterstartediting',
function () {
let valSelector = snak._variation._valueView._expert.$input.data().entityselector;
let valval = getPropertyEntity(claim.value.mainVal);
valSelector.element.val(valval.label);
valSelector._trigger("change");
valSelector._select(valval);
if (!!claim.qualifiers) {
let qlistview = sv._qualifiers;
qlistview.enterNewItem();
var qsnaklistview = qlistview.value()[qlistview.value().length - 1];
qsnaklistview.enterNewItem();
let qslv = $(sv._qualifiers.items().first()).data().snaklistview;
let qsnak = qslv._listview.items().first().data().snakview;
let qsel = qsnak._getPropertySelector();
qsel.element.val(es.label);
qsel._trigger('change');
qsel._select(es);
qsnak._variation._valueView.element.one(
qsnak._variation._valueView.widgetEventPrefix + 'afterstartediting',
function () {
let qvalSelector = qsnak._variation._valueView._expert.$input.data().entityselector;
let qvalval = getPropertyEntity(claim.value.mainVal);
qvalSelector.element.val(qvalval.label);
qvalSelector._trigger("change");
valSelector._trigger("change");
qvalSelector._select(qvalval);
}
);
qsnak._variation._valueView.element.one(
qsnak._variation._valueView.widgetEventPrefix + 'change',
function () {
let toolbar = item.statementgroupview.statementlistview._listview.items().last().data().edittoolbar;
toolbar._controller.stopEditing(false);
});
}
}
);
snak._variation._valueView.element.one(
snak._variation._valueView.widgetEventPrefix + 'change',
function () {
let toolbar = item.statementgroupview.statementlistview._listview.items().last().data().edittoolbar;
toolbar._controller.stopEditing(false);
});
}
function pendingStatement(claim) {
let prop = claim.property;
return new Promise((resolve, reject) => {
let statementListView = $(".wikibase-statementgrouplistview")
.first()
.data().wikibaseStatementgrouplistview;
statementListView.enterNewItem();
let items = statementListView.listview.items();
let item = items.last().data();
let sv = item.statementgroupview.statementlistview._listview.items();
let snak = $(sv).data().statementview.$mainSnak.data().snakview;
let selector = snak._getPropertySelector();
// Set up event listeners and chain them to resolve when finished.
selector.element.on('entityselectorselected', (event, entityId) => {
$(snak._variation).on("afterdraw", function () {
snak._variation._valueView.element.one(
snak._variation._valueView.widgetEventPrefix + 'afterstartediting',
function () {
if (claim.datatype === "wikibase-item") {
let valSelector = snak._variation._valueView._expert.$input.data().entityselector;
let jqe = snak._variation._valueView._expert.$input.data().jqueryEventSpecialEachchange;
valSelector.element.val("temp");
valSelector._trigger("change");
valSelector._select({ id: "Q80", datatype: "wikibase-item" });
jqe.handlers[0].call();
jqe.handlers[1].call();
valSelector.element.val("");
valSelector._trigger("change");
jqe.handlers[0].call();
jqe.handlers[1].call();
}
else {
let valSelector = snak._variation._valueView._expert.$input.data().inputautoexpand;
let jqe = snak._variation._valueView._expert.$input.data().jqueryEventSpecialEachchange;
if (claim.datatype === "globe-coordinate") {
valSelector.$input.val("0,0");
jqe.handlers[0].call();
jqe.handlers[1].call();
}
else {
valSelector.$input.val("__"); // shows the placeholder behavior
jqe.handlers[0].call();
jqe.handlers[1].call();
}
valSelector.$input.val("");
jqe.handlers[0].call();
jqe.handlers[1].call();
}
// Once processing is complete, resolve the Promise.
resolve();
}
);
});
});
selector.element.val(prop.label);
selector._trigger('change');
selector._select(prop);
});
}
function addDetails(templateName) {
var claims = WbTemplates[templateName].userDefinedClaims;
var chain = Promise.resolve();
claims.forEach(function (claim) {
chain = chain.then(function () {
return pendingStatement(claim);
});
});
return chain;
}
function applyTemplate(templateName) {
WbTemplates[templateName].prefilledClaims.forEach(claim => {
prefillStatement(claim, false);
});
}
// ---- 5) UI: render only after WbTemplates is ready ----
function initTemplatingUI() {
// If nothing came back, don't render the UI
if (!WbTemplates || !Object.keys(WbTemplates).length) {
console.warn('No templates found for query:', SEARCH_QUERY);
return;
}
$(".wikibase-entitytermsview").append(`
<div class="wikibase-templating-section">
<div class="wikibase-templating-section-title"><span class="magic-wand-icon"></span> RDA Entity template</div>
<div class="wikibase-templating-help"><span class="settings-text">Select an RDA entity class to apply predefined template entries and additional entity details.</span></div>
<div class="wikibase-templating-form oo-ui-labelElement oo-ui-fieldLayout">
<div class=""><label for="wbtemplate">Entity class</label></div>
<div>
<select id="wbtemplate" class="cdx-select"></select>
</div>
</div>
<div class="mw-htmlform-submit-buttons">
<button id="applyTemplateBtn" class="cdx-button">Apply template</button>
<button id="addDetailsBtn" class="cdx-button">Add details</button>
</div>
</div>
`);
// Populate selector
Object.entries(WbTemplates).forEach(([key, tpl]) => {
$('#wbtemplate').append($('<option>', { value: key, text: tpl.label }));
});
// Wire buttons
$("#applyTemplateBtn").on("click", function () {
applyTemplate($("#wbtemplate").find(":selected").val());
});
$("#addDetailsBtn").on("click", function () {
addDetails($("#wbtemplate").find(":selected").val());
});
}
// finally render
initTemplatingUI();
})().catch(console.error);
});