User:So9q/CreateNewEntity.js
(Redirected from User:So9q/Gadget-CreateNewEntity.js)
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.
/**
* Forked from https://www.wikidata.org/w/index.php?title=User:Efly/Gadget-CreateNewItem.js&oldid=688915103 by [[User:Efly]]
* @original_author Efly, forked by @author So9q to add support for lexemes.
--
* Add a "create new item" link in the dropdown menu for when you want to
* add an item to a property, but the item does not exist.
*
* Known bugs:
* 1) there is a little delay from the not-found message is displayed
* until we replace it. This is caused by a timeout hack to await population
* of the list.
* 2) one click on "create new item" results in more than one tab opened (in Firefox).
* The cause of this is unknown
*
* https://phabricator.wikimedia.org/T107693
*
* Install by adding: importScript('User:So9q/CreateNewEntity.js'); //Linkback: [[User:So9q/CreateNewEntity.js]]
* to your common.js
*
*/
const EntityType = {
Item: 'Item',
Lexeme: 'Lexeme',
}
const languages = {
"Q9035": {
name: "Danish",
code: "da"
},
"Q9027": {
name: "Swedish",
code: "sv"
},
"Q1860": {
name: "English",
code: "en"
},
"Q143": {
name: "Esperanto",
code: "eo"
},
"Q150": {
name: "French",
code: "fr"
},
"Q188": {
name: "German",
code: "de"
},
"Q652": {
name: "Italian",
code: "it"
},
"Q8641": {
name: "Yiddish",
code: "yi"
},
"Q2417210": {
name: "Old Swedish",
code: "mis"
},
"Q25433": {
name: "Low German",
code: "mis"
},
"Q35505": {
name: "Old Norse",
code: "mis"
},
"Q1163234": {
name: "Medieval Latin",
code: "mis",
},
"Q1503113": {
name: "Late Latin",
code: "mis",
},
"Q1248221": {
name: "New Latin",
code: "mis"
},
"Q1473289": {
name: "Middle French",
code: "mis"
},
"Q35497": {
name: "Ancient Greek",
code: "mis"
},
"Q36510": {
name: "Modern Greek",
code: "el"
}
};
( function ( $, mw ) {
'use strict';
var newEntityLabel = "test";
var newEntityLabelNotFound = "";
function checkNotFound( textarea, firstLi ) {
//console.log("checkNotFound running");
//console.log("textarea is:");
//console.log(textarea);
if ($(textarea).hasClass("valueview-expert-wikibaseitem-input")) {
newEntityLabelNotFound = "Not found. Click to create a new item";
}
else if ($(textarea).hasClass("valueview-expert-wikibaselexeme-input")) {
newEntityLabelNotFound = "Not found. Click to create a new lexeme";
}
var replacementMade = false;
/* Since we have no idea which entityselector list belongs to which
* textarea we just pick the first and hope it is right
* see https://phabricator.wikimedia.org/T290298
*/
var li = $("li.ui-entityselector-notfound").first();
if (li.length == 1){
// Change the text
var $innerA = $( li ).find( 'a' ).first();
console.log($innerA);
$innerA.on( 'click', function () {
// why is this here?
li.remove();
createNewHandler( textarea );
// close the dialog automatically after click
return false;
} );
$innerA.text( newEntityLabelNotFound );
replacementMade = true;
}
console.log("replacementMade:" replacementMade);
return replacementMade;
}
function appendLi(list, textarea){
console.log("Appending to list");
if ($(textarea).hasClass("valueview-expert-wikibaseitem-input")) {
newEntityLabel = "Create new item";
}
else if ($(textarea).hasClass("valueview-expert-wikibaselexeme-input")) {
newEntityLabel = "Create new lexeme";
}
$( list ).append(
$('<li>')
.prop( 'class', 'ui-ooMenu-item ui-ooMenu-customItem create-new-entity' )
.prop( 'dir', 'auto' )
.prop( 'tabindex', '-1' )
.css('border-top-style', 'solid')
.css('background-color', 'light blue')
.append( $('<a>').prop( 'href', '#' )
.css('text-align', 'right')
.css('padding-right', '1.2em')
.css('font-weight', 'bold')
.text( newEntityLabel )
.click( function() {
createNewHandler( textarea );
// Hide the oo-menu
$( list ).hide();
return false;
})
)
);
}
function openNewLexemePopup( textarea ) {
console.log("openNewLexemePopup textarea is:");
console.log(textarea);
// .submit( requestDeletion )
var currentLabel = $(textarea).val();
console.log("openNewLexemePopup");
mw.loader.using( 'oojs-ui-core' ).done( function () {
function hide() {
popup.toggle( false );
}
var h3 = $("<h3>").text("Add new lexeme");
var div = $("<div>").prop('class','vector-menu-content')
.css('padding-left', '5px')
.css('padding-bottom', '10px')
.css('direction', 'rtl');
console.log("Generating the html");
for (let key in languages) {
const lexicalCategoryId = ['Q1084', 'Q24905', 'Q34698', 'Q184511', 'Q187931', 'Q62155', 'Q83034', 'Q111029'];
const lexicalCategoryName = ['noun', 'verb', 'adjective', 'idiom', 'phrase', 'affix', 'interjection', 'root'];
// one li for each language to hold the links,
var li = $('<li>').text(languages[key]["name"] ": ")
.css('padding-left', '5px');
$.each(lexicalCategoryId, function(j) {
// Add the links
$('<a>').text(lexicalCategoryName[j] " ").click( function() {
window.open('/wiki/Special:NewLexeme?lexeme-language=' key
'&lemma-language=' languages[key]["code"]
'&lexicalcategory=' lexicalCategoryId[j]
'&lemma=' currentLabel);
hide();
})
.appendTo(li);
});
li.appendTo(div);
}
// Append the finished div
var popupWindow = $('<div>').append(h3, div);
var popup = new OO.ui.PopupWidget( {
$content: popupWindow,
padded: true,
width: 450,
head: true
} );
popup.$element.attr( 'style', 'z-index: 9999999;' );
$(textarea).parent().append( popup.$element );
popup.toggle( true );
} );
}
// Measure when the function was last run to prevent opening multiple tabs
// on one click
var t0 = performance.now();
var t1 = 0;
var count = 0;
function createNewHandler( currentField ) {
// FIXME bug with handler running multiple times on one click
console.log("createNewHandler is running");
if ($(currentField).hasClass("valueview-expert-wikibaseitem-input")) {
window.open('/wiki/Special:NewItem?label=' $(currentField).val());
/* disabled
if (count == 0 && t1 == 0) {
t1 = performance.now();
console.log("opening window now");
window.open('/wiki/Special:NewItem?label=' $(currentField).val());
count = 1;
} else {
if (t1 - t0 > 2000) {
console.log("resetting count");
// reset count
count = 0;
t1 = 0;
// call itself
createNewHandler (currentField);
} else {
console.log("repeated opening of tab prevented");
}
} */
} else if ($(currentField).hasClass("valueview-expert-wikibaselexeme-input")) {
openNewLexemePopup( currentField );
//window.open('/wiki/Special:NewLexeme?lemma=' currentField.val())
}
else {
console.log("ERROR: Could not detect input type")
}
}
function appendToSuitableLexemeLists(textarea){
// Goes through the lists and appends if not an unsuitable list
var lists = $( "ul.ui-entityselector-list" );
$(lists).each(function(){
//console.log("Going through the lists");
var listLength = $(this).children().length;
// check if suggestions-special-menu
var suggestionsSpecial = $(this).find("div.suggestions-special");
//console.log(suggestionsSpecial);
//console.log("suggestionsSpecial.length=" suggestionsSpecial.length);
if (listLength > 0 && suggestionsSpecial.length === 0) {
console.log(`Found suitable list with ${listLength} elements`);
console.log(this);
// Check if we already have appended to this list before
var alreadyAppended = $(this).find(".create-new-entity")
console.log(alreadyAppended)
console.log("length:" $(alreadyAppended).length)
if ($(alreadyAppended).length === 0) {
console.log("We did not append yet")
// after pasting the LID we don't want the script to append
if (listLength == 1) {
var spans = $(this).find("span.ui-entityselector-label");
// console.log(spans);
var spanText = $(spans).first().text();
// console.log("span=" spanText);
// This matches the suggestion that has not been indexed yet
// e.g. "L1 (L1)"
var spanPattern = new RegExp("L\d \(L\d \)");
var lidFound = spanPattern.test(spanText);
if (lidFound) {
// continue
return;
}
}
var inputValue = $(textarea).val().trim();
// This matches e.g. "test (L1)"
var nikkiPattern = new RegExp(". \(L\d \)");
var testNikkiSuggestion = nikkiPattern.test(inputValue);
var nakedLid = new RegExp("L\d ");
var testNakedLid = nakedLid.test(inputValue);
if (inputValue != "") {
if (testNikkiSuggestion === false && testNakedLid === false) {
console.log("Value longer than 0 chars and is not a LID");
appendLi(this, textarea);
} else {
if (testNikkiSuggestion === true){
console.log(`testNikkiSuggestion was true for "${inputValue}"`);
} else if (testNakedLid === true) {
console.log(`testNakedLid was true for "${inputValue}"`);
}
}
}
} else {
console.log("We already appended to this list");
}
}
});
}
function init() {
/***************************
Handle main search input box
****************************/
// the searchbox only searches items so that is the only thing we
// support for now
// inspired by https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
// Select the node that will be observed for mutations
/* DISABLED for now
const targetNode = document.querySelector("#searchInput");
//console.log(targetNode);
// Options for the observer (which mutations to observe)
const config = { attributes: true, childList: false, subtree: false };
var value = "";
// Callback function to execute when mutations are observed
const callback = function(mutationsList, observer) {
// Use traditional 'for loops' for IE 11
for(const mutation of mutationsList) {
if (mutation.type === 'attributes') {
//console.log('The ' mutation.attributeName ' attribute was modified.');
if (mutation.attributeName == "class"){
value = $(targetNode).val();
// We only fire when the user has typed something in.
if (value !== "") {
console.log('class changed value is: ' value);
checkNotFound(targetNode, true);
}
}
}
}
};
// Create an observer instance linked to the callback function
const observer = new MutationObserver(callback);
// Start observing the target node for configured mutations
observer.observe(targetNode, config);
*/
/**************************************
Handle new lexeme/item statement input boxes
***************************************/
/*
This is tricky because we only want to fire when the user is
trying to add a statement but just observing the textarea is not enough.
We need to observe $("ul.ui-entityselector-list") for changes after the
user begins to add a statement
*/
console.log("Trying to observe new lexeme/item textareas");
// "observer" from Elfly:
var textarea;
var willEntitySelectorListUpdate = false;
$( document ).on( 'input propertychange paste', '.wikibase-snakview-value-container:has(.ui-suggester-input)', function () {
textarea = $( this ).find( '.ui-suggester-input' ).first();
// console.log("Entering: " currentFieldText);
willEntitySelectorListUpdate = true;
} );
$( document ).on( 'DOMSubtreeModified', '.ui-entityselector-list', function () {
if ( willEntitySelectorListUpdate ) {
// Now we know the user has started adding something
var textareaType;
if ($(textarea).hasClass("valueview-expert-wikibaselexeme-input")){
textareaType = EntityType.Lexeme;
}
else if ($(textarea).hasClass("valueview-expert-wikibaseitem-input")){
textareaType = EntityType.Item;
}
window.setTimeout(function () {
var firstLi = $( this ).find( 'li' ).first();
// debug
console.log(firstLi);
if (textareaType == "Lexeme" && textarea.length > 0) {
console.log("Found lexeme input textarea :)");
console.log("Running checkNotFound");
// First check if we got a not-found list
var found = checkNotFound(textarea, firstLi);
console.log("found was:" found);
if (!found) {
// Append to bottom of suggester list
appendToSuitableLexemeLists(textarea);
}
// Stop the loop
willEntitySelectorListUpdate = false
} else if (textareaType == "Item" && textarea.length > 0){
console.log("Found item input textarea :)");
// TODO add checkNotFound
var lists = $( "ul.ui-entityselector-list" );
$(lists).each(function(){
console.log("Running foreach");
var listLength = $(this).children().length;
// check if suggestions-special-menu
var suggestionsSpecial = $(this).find("div.suggestions-special");
//console.log(suggestionsSpecial);
//console.log("suggestionsSpecial.length=" suggestionsSpecial.length);
if (listLength > 0 && suggestionsSpecial.length === 0) {
console.log(`Found suitable list with ${listLength} elements`);
// remove earlier appends to this list
$(this).find(".create-new-entity").remove();
var inputValue = $(textarea).val().trim();
if (inputValue != "") {
console.log("Value longer than 0 chars");
console.log("Running checkNotFound");
// First check if we got a not-found list
var found = checkNotFound(textarea);
console.log("found was:" found);
if (!found) {
// Append to bottom of suggester list
appendLi(this, textarea);
}}
// Stop the loop
willEntitySelectorListUpdate = false;
} else if (suggestionsSpecial.length > 0){
console.log("Skipping suggestion-special list with length:" listLength);
}
});
} else {
console.log("Could not find any input textarea");
}
// Timeout in miliseconds
}, 250);
}
});
}
$( function () {
mw.hook( 'wikibase.entityPage.entityLoaded' ).add( init );
} );
}( jQuery, mediaWiki ) );