User:Techwizzie/newInterface.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.
// Function to initialize the edit properties functionality
const instanceOfItemID = "P31";
const propertiesForTypeID = "P1963";
var statementsMap = {};
var guidToStatementMap = {};
var claimGUIDsToRemove = [];
var claimsToSet = [];
var propertiesForClasses = {};
function initializeEditProperties() {
var menuList = $("#right-navigation").find(".vector-menu-content-list");
var editEntityDiv = $("<li>").attr({
id: "ca-edit-entity",
class: "vector-tab-noicon mw-list-item",
});
var editFieldSpan = $("<span>").text("Edit Properties");
var anchor = $("<a>").append(editFieldSpan);
anchor.on("click", function (e) {
menuList.find("li").each(function () {
$(this).removeClass("selected");
});
$("#ca-edit-entity").addClass("selected");
handleEditPropertiesClick();
});
if (mw.config.get("wgPageContentModel") === "wikibase-item") {
editEntityDiv.append(anchor);
menuList.append(editEntityDiv);
}
}
// Function to generate guid
function generateGUID(pageId) {
var chars = "0123456789abcdefghijklmnopqrstuvwxyz";
var guid = pageId + "$";
for (var i = 0; i < 36; i++) {
if (i === 8 || i === 13 || i === 18 || i === 23) {
guid += "-";
} else if (i === 14) {
guid += "4";
} else if (i === 19) {
guid += chars.charAt(Math.floor(Math.random() * chars.length));
} else {
guid += chars.charAt(Math.floor(Math.random() * chars.length));
}
}
return guid;
}
function handleEditPropertiesClick() {
$("#mw-content-text").empty();
var pageName = mw.config.get("wgPageName");
if (mw.config.get("wgPageContentModel") === "wikibase-item") {
var itemID = pageName;
retrieveEntityProperties(itemID);
}
}
function retrieveEntityProperties(itemID) {
var api = new mw.Api();
var requestParams = {
action: "wbgetentities",
format: "json",
ids: itemID,
};
var result = api.get(requestParams);
result.done(function (res) {
var entity = res.entities[itemID];
var instancesOf = getInstanceOfProperties(entity);
retrieveTypeProperties(api, requestParams, instancesOf, entity);
});
}
// Function to retrieve instance of properties
function getInstanceOfProperties(entity) {
var instancesOf = [];
if (entity.claims[instanceOfItemID]) {
entity.claims[instanceOfItemID].forEach(function (val) {
instancesOf.push(val.mainsnak.datavalue.value.id);
});
}
return instancesOf;
}
// Function to retrieve type properties from Wikidata API
function retrieveTypeProperties(api, requestParams, instancesOf, entity) {
var typesResponse = api.get(
$.extend({}, requestParams, { ids: instancesOf })
);
typesResponse.done(function (res) {
var entities = res.entities;
var propertiesForType = {};
var typePropertyMap = {};
instancesOf.forEach(function (instance) {
var et = entities[instance];
propertiesForClasses[instance] = [];
if (et.claims[propertiesForTypeID]) {
et.claims[propertiesForTypeID].forEach(function (val) {
if (!propertiesForType[instance]) {
propertiesForType[instance] = [];
}
propertiesForType[instance].push(val.mainsnak.datavalue.value.id);
propertiesForClasses[instance].push(val.mainsnak.datavalue.value.id);
typePropertyMap[val.mainsnak.datavalue.value.id] = instance;
});
}
});
var propertyIDs = Object.keys(entity.claims);
propertyIDs.splice(propertyIDs.indexOf(instanceOfItemID), 1);
var valuesMap = getValuesMap(entity, propertyIDs);
retrieveLabels(
api,
requestParams,
propertyIDs,
instancesOf,
valuesMap,
typePropertyMap,
propertiesForType
);
});
}
// Function to retrieve values map
function getValuesMap(entity, propertyIDs) {
var valuesMap = {};
propertyIDs.forEach(function (propertyID) {
valuesMap[propertyID] = [];
if (entity.claims[propertyID]) {
statementsMap[propertyID] = entity.claims[propertyID];
statementsMap[propertyID].map(function (statement) {
guidToStatementMap[statement.id] = statement;
});
entity.claims[propertyID].forEach(function (statement) {
if (statement.mainsnak.datavalue) {
valuesMap[propertyID].push(statement.mainsnak.datavalue.value);
}
});
}
});
return valuesMap;
}
function newTextInput(value, datatype, statementID, guid) {
const pageId = mw.config.get("wbEntityId");
var textInput = new OO.ui.TextInputWidget({});
textInput.setValue(value);
const newGUID = guid ? guid : generateGUID(pageId);
textInput.$element.attr("data-guid", newGUID);
var field = new OO.ui.FieldLayout(textInput, {});
var deleteIcon = new OO.ui.IconWidget({
icon: "trash",
flags: "destructive",
});
var horizontalLayout = new OO.ui.HorizontalLayout({
items: [field, deleteIcon],
});
deleteIcon.$element.on("click", function (e) {
if (statementID) claimGUIDsToRemove.push(statementID);
horizontalLayout.$element.remove();
});
textInput.on("change", function (newValue) {
var modifiedStatement = {};
if (guidToStatementMap[newGUID]) {
modifiedStatement = guidToStatementMap[statementID];
} else {
modifiedStatement = {};
}
if (datatype === "quantity") {
modifiedStatement.mainsnak.datavalue.value.amount = newValue;
} else if (
datatype === "string" ||
datatype === "commonsMedia" ||
datatype === "globecoordinate"
) {
modifiedStatement.mainsnak.datavalue.value = newValue;
} else if (datatype === "time") {
modifiedStatement.mainsnak.datavalue.value.time = newValue;
}
console.log("Modified statement => ", modifiedStatement);
});
return horizontalLayout;
}
function newComboboxInput(value, statementID, guid) {
const pageId = mw.config.get("wbEntityId");
const lang = mw.config.get("wgContentLanguage");
var api = new mw.Api();
var allValues = [value];
var comboboxInput = new OO.ui.ComboBoxInputWidget({
autocomplete: true,
menu: {
filterFromInput: true,
},
});
const newGUID = guid ? guid : generateGUID(pageId);
comboboxInput.$element.attr("data-guid", newGUID);
comboboxInput.setOptions(
allValues.map(function (item) {
return new OO.ui.MenuOptionWidget({
data: item,
label: item,
});
})
);
comboboxInput.setValue(value);
comboboxInput.on("change", function (inputText) {
var requestParams = {
action: "wbsearchentities",
format: "json",
ids: [],
search: inputText,
language: lang,
type: "item",
limit: 20,
};
var values = api.get($.extend({}, requestParams, {}));
values.done(function (items) {
comboboxInput.setOptions(
items.search.map(function (item) {
if (item.display.label) {
return new OO.ui.MenuOptionWidget({
data: item.display.label.value + " (" + item.description + ")",
label: item.display.label.value + " (" + item.description + ")",
});
}
})
);
});
var modifiedStatement = {};
if (guidToStatementMap[newGUID]) {
modifiedStatement = guidToStatementMap[statementID];
} else {
modifiedStatement = {};
}
modifiedStatement.mainsnak.datavalue.value = inputText;
console.log("Modified statement => ", modifiedStatement);
});
var field = new OO.ui.FieldLayout(comboboxInput, {
align: "left",
});
var deleteIcon = new OO.ui.IconWidget({
icon: "trash",
flags: "destructive",
});
var horizontalLayout = new OO.ui.HorizontalLayout({
items: [field, deleteIcon],
});
deleteIcon.$element.on("click", function (e) {
if (statementID) claimGUIDsToRemove.push(statementID);
horizontalLayout.$element.remove();
});
return horizontalLayout;
}
// Function to retrieve labels from Wikidata API
function retrieveLabels(
api,
requestParams,
propertyIDs,
instancesOf,
valuesMap,
typePropertyMap,
propertiesForType,
shouldReturn = false
) {
var lang = mw.config.get("wgPageContentLanguage");
var batchSize = 50; // Maximum number of IDs per request
var chunks = [];
var i, j;
// Split propertyIDs and instancesOf into chunks of batchSize
for (i = 0, j = propertyIDs.length; i < j; i += batchSize) {
chunks.push(propertyIDs.slice(i, i + batchSize));
}
for (i = 0, j = instancesOf.length; i < j; i += batchSize) {
chunks.push(instancesOf.slice(i, i + batchSize));
}
// Create promises for each chunk and make asynchronous requests
var promises = chunks.map(function (chunk) {
var labelsResponse = api.get(
$.extend({}, requestParams, { props: "labels", ids: chunk })
);
return labelsResponse.then(function (res) {
var props = res.entities;
var properties = [];
var classLabels = {};
Object.keys(props).forEach(function (idx) {
var prop = props[idx];
if (instancesOf.indexOf(prop.id) === -1) {
properties.push({
id: prop.id,
datatype: prop.datatype,
label: prop.labels[lang] ? prop.labels[lang].value : "",
});
} else {
classLabels[prop.id] = prop.labels[lang]
? prop.labels[lang].value
: "";
}
});
return { properties: properties, classLabels: classLabels };
});
});
// Wait for all promises to resolve and process the results
return Promise.all(promises)
.then(function (results) {
var allProperties = [];
var allClassLabels = {};
// Merge the results
results.forEach(function (result) {
allProperties = allProperties.concat(result.properties);
Object.assign(allClassLabels, result.classLabels);
});
if (shouldReturn) {
return allClassLabels;
} else {
createPropertyDivs(
api,
allProperties,
allClassLabels,
valuesMap,
typePropertyMap,
instancesOf,
propertiesForType
);
}
})
.catch(function (error) {
console.error("Error fetching labels:", error);
});
}
// Function to create property divs
function createPropertyDivs(
api,
properties,
classLabels,
valuesMap,
typePropertyMap,
instancesOf,
propertiesForType
) {
var propsByTypeStyle =
"border-style:solid; width: max-content; border-width:0.5px; padding: 0 1rem 1rem 1rem; margin-top: 1rem;background: aliceblue;";
var lang = mw.config.get("wgPageContentLanguage");
var propertyDivs = {};
var requestParams = {
action: "wbgetentities",
format: "json",
};
Object.keys(propertiesForType).forEach(function (id) {
propertyDivs[id] = $("<div></div>").attr("style", propsByTypeStyle);
});
// Group properties into batches of 50 or fewer
var propertyBatches = chunkArray(properties, 50);
// Create promises for each batch and make asynchronous requests
var promises = propertyBatches.map(function (propertyBatch) {
var propertyBatchIDs = [];
propertyBatch.map(function (prop) {
propertyBatchIDs.push(prop.id);
});
// Fetch labels for the current batch of properties
var labelsResponse = api.get(
$.extend({}, requestParams, { props: "labels", ids: propertyBatchIDs })
);
return labelsResponse.then(function (res) {
var props = res.entities;
var properties = [];
var classLabelsBatch = {};
// Process each property in the batch
propertyBatchIDs.forEach(function (propID) {
var prop = props[propID];
if (prop) {
if (instancesOf.indexOf(prop.id) === -1) {
properties.push({
id: prop.id,
datatype: prop.datatype,
label: prop.labels[lang] ? prop.labels[lang].value : "",
});
} else {
classLabelsBatch[prop.id] = prop.labels[lang]
? prop.labels[lang].value
: "";
}
}
});
return { properties: properties, classLabelsBatch: classLabelsBatch };
});
});
// Wait for all promises to resolve and process the results
Promise.all(promises)
.then(function (results) {
var allProperties = [];
// Merge the results
results.forEach(function (result) {
allProperties = allProperties.concat(result.properties);
});
createPropertyDivElements(
api,
allProperties,
classLabels,
valuesMap,
typePropertyMap,
propertyDivs,
propertiesForType
);
})
.catch(function (error) {
console.error("Error fetching labels:", error);
});
}
// Helper function to chunk an array into smaller arrays
function chunkArray(array, size) {
return Array.from(
{ length: Math.ceil(array.length / size) },
function (_, index) {
return array.slice(index * size, index * size + size);
}
);
}
// Function to create property div elements
function createPropertyDivElements(
api,
properties,
classLabels,
valuesMap,
typePropertyMap,
propertyDivs,
propertiesForType
) {
const lang = mw.config.get("wgContentLanguage");
var propsByTypeStyle =
"border-style:solid; width: max-content; border-width:0.5px; padding: 0 1rem 1rem 1rem; margin-top: 1rem;background: aliceblue;";
var otherFieldsDiv = $("<div></div>").attr("style", propsByTypeStyle);
const generalProperties = properties.filter(function (item) {
return item.datatype !== "external-id";
});
generalProperties.forEach(function (prop) {
var fieldset = new OO.ui.FieldsetLayout({
classes: ["container"],
});
var labelWidget = new OO.ui.LabelWidget({
label: prop.label,
});
labelWidget.$element.css("font-weight", "bold");
var addIcon = new OO.ui.IconWidget({
icon: "add",
flags: "progressive",
label: "add",
});
var hLayout = new OO.ui.HorizontalLayout({
items: [labelWidget, addIcon],
});
fieldset.addItems(hLayout);
var fields = [];
if (prop.datatype === "string") {
for (var i = 0; i < statementsMap[prop.id].length; i++) {
const statementObject = statementsMap[prop.id][i];
if (statementObject.mainsnak.datavalue === undefined) {
continue;
}
const value = statementObject.mainsnak.datavalue.value;
var horizontalLayout = newComboboxInput(
value,
statementObject.id,
statementObject.id
);
fields.push(horizontalLayout);
}
fieldset.addItems(fields);
addIcon.$element.on("click", function (e) {
fields = [];
var horizontalLayout = newComboboxInput("");
fields.push(horizontalLayout);
fieldset.addItems(fields);
});
if (typePropertyMap[prop.id] !== undefined) {
propertyDivs[typePropertyMap[prop.id]].append(fieldset.$element);
} else {
otherFieldsDiv.append(fieldset.$element);
}
} else if (prop.datatype === "wikibase-item") {
for (var i = 0; i < statementsMap[prop.id].length; i++) {
const statementObject = statementsMap[prop.id][i];
if (statementObject.mainsnak.datavalue === undefined) {
continue;
}
const statementQID = statementObject.mainsnak.datavalue.value.id;
var searchParams = {
action: "wbsearchentities",
format: "json",
search: statementQID,
language: lang,
type: "item",
limit: 20,
};
var resp = api.get(searchParams);
resp
.done(function (res) {
const label = res.search[0].display.label
? res.search[0].display.label.value
: statementQID;
var horizontalLayout = newComboboxInput(
label,
statementObject.id,
statementObject.id
);
fields.push(horizontalLayout);
fieldset.addItems(fields);
if (typePropertyMap[prop.id] !== undefined) {
propertyDivs[typePropertyMap[prop.id]].append(fieldset.$element);
} else {
otherFieldsDiv.append(fieldset.$element);
}
})
.catch(function (ex) {
console.warn(ex);
});
addIcon.$element.on("click", function (e) {
fields = [];
var horizontalLayout = newComboboxInput("");
fields.push(horizontalLayout);
fieldset.addItems(fields);
});
}
} else {
if (prop.datatype === "quantity") {
for (var i = 0; i < statementsMap[prop.id].length; i++) {
const statementObject = statementsMap[prop.id][i];
if (statementObject.mainsnak.datavalue === undefined) {
continue;
}
const value = statementObject.mainsnak.datavalue.value;
var horizontalLayout = newTextInput(
value.amount,
prop.datatype,
statementObject.id,
statementObject.id
);
fields.push(horizontalLayout);
}
} else if (
prop.datatype === "commonsMedia" ||
prop.datatype === "globecoordinate"
) {
if (prop.datatype === "globecoordinate")
console.log(statementsMap[prop.id]);
statementsMap[prop.id].forEach(function (statementObject) {
const value = statementObject.mainsnak.datavalue.value;
var horizontalLayout = newTextInput(
value,
prop.datatype,
statementObject.id,
statementObject.id
);
fields.push(horizontalLayout);
});
} else if (prop.datatype === "time") {
statementsMap[prop.id].forEach(function (statementObject) {
const value = statementObject.mainsnak.datavalue.value.time;
var horizontalLayout = newTextInput(
value,
prop.datatype,
statementObject.id,
statementObject.id
);
fields.push(horizontalLayout);
});
}
fieldset.addItems(fields);
addIcon.$element.on("click", function (e) {
fields = [];
var horizontalLayout = newTextInput("", prop.datatype);
fields.push(horizontalLayout);
fieldset.addItems(fields);
});
if (typePropertyMap[prop.id] !== undefined) {
propertyDivs[typePropertyMap[prop.id]].append(fieldset.$element);
} else {
otherFieldsDiv.append(fieldset.$element);
}
}
});
idProperties = properties.filter(function (item) {
return item.datatype === "external-id";
});
console.log(idProperties);
idProperties.forEach(function (prop) {
var fieldset = new OO.ui.FieldsetLayout({
classes: ["container"],
});
var labelWidget = new OO.ui.LabelWidget({
label: prop.label,
});
labelWidget.$element.css("font-weight", "bold");
var addIcon = new OO.ui.IconWidget({
icon: "add",
flags: "progressive",
label: "add",
});
var horizontalLayout = new OO.ui.HorizontalLayout({
items: [labelWidget, addIcon],
});
fieldset.addItems(horizontalLayout);
var fields = [];
statementsMap[prop.id].forEach(function (statementObject) {
const value = statementObject.mainsnak.datavalue.value;
var horizontalLayout = newTextInput(
value,
prop.datatype,
statementObject.id
);
fields.push(horizontalLayout);
});
fieldset.addItems(fields);
var externalIDsDiv = $("<div></div>");
var title = $("<h3>").text("External ID(s)");
externalIDsDiv.append(title);
externalIDsDiv.append(fieldset.$element);
externalIDsDiv.css("background", "white");
// if (typePropertyMap[prop.id] !== undefined) {
// propertyDivs[typePropertyMap[prop.id]].append(externalIDsDiv.$element);
// } else {
// otherFieldsDiv.append(externalIDsDiv.$element);
// }
$("#mw-context-text").append(externalIDsDiv.$element);
console.log("It reaches here");
});
Object.keys(propertyDivs).forEach(function (propID) {
var sectionHeader = $("<h2></h2>");
sectionHeader.html(
'Fields for class <a href="https://www.wikidata.org/wiki/' +
propID +
'">' +
classLabels[propID] +
"</span>"
);
propertyDivs[propID].prepend(sectionHeader);
$("#mw-content-text").prepend(propertyDivs[propID]);
if (propertiesForClasses[propID].length === 0) {
propertyDivs[propID].$element.remove();
}
});
var sectionHeader = $("<h2></h2>");
sectionHeader.text("Other Fields");
otherFieldsDiv.prepend(sectionHeader);
$("#mw-content-text").append(otherFieldsDiv);
var title = $("<h2>").text("Edit Properties");
$("#mw-content-text").prepend(title);
var submitButton = new OO.ui.ButtonWidget({
framed: true,
flags: ["primary", "progressive"],
label: "Save changes",
});
submitButton.$element.css("width", "100%");
submitButton.on("click", function (e) {
console.log("Claims to remove: ", claimGUIDsToRemove);
console.log("GUID to statement map: ", guidToStatementMap);
// var api = new mw.Api();
// var requestParams = {
// action: 'wbremoveclaims',
// format: 'json',
// claim: claimGUIDsToRemove
// };
// var resp = api.get(requestParams);
// resp.done(function(){
// location.reload();
// });
});
$("#mw-content-text").append("<br>");
$("#mw-content-text").append(submitButton.$element);
}
$(document).ready(function () {
mw.loader.using("oojs-ui-core").done(function () {
initializeEditProperties();
});
});