User:Danski454/stubsearch.js

//

//from a request by Insertcleverphrasehere

//based on User:Ais523/stubtagtab2.js, which is in turn based on User:MC10/stubtagtab.js

//variables

var SSunfilteredArray = ["stub"];//does not contain parent

var stubTags = {"stub":{display:"{{Stub}}", parents:["stubs"], children:null, descendants:null, template:"Stub"},"stubs":{display:"Stubs", parents:null, children:[], descendants:["stub"], template:null}};//generic stub is not loaded by the normal method

var SSfilteredArray = ["stub"];

var stublists = ['Architecture', 'Commerce', 'Culture', 'Education', 'Geography', 'Government, law, and politics',

'History', 'Leisure', 'Military and weaponry', 'Organizations', 'People',

'Religion, mythology, faiths, and beliefs', 'Science', 'Sports', 'Technology', 'Transport',

'Miscellaneous'];

var stubTemplates = ["{{stub}}"];

var SSactive = false;

var SScurrent = stubTags["stubs"];

var SSoldid = null;

var SSnewid = null;

var SSpagesLoaded = 0;

var SSextraChildren = [{parent:"education stubs", children:["school stubs", "university stubs"]},

{parent:"company stubs", children:["Aeronautical company stubs", "Airline stubs", "Animation studio stubs", "Brand name products stubs", "Chemical company stubs",

"Disney stubs", "Energy company stubs", "Fashion company stubs", "Film company stubs", "Financial services company stubs", "Food company stubs", "Hotel stubs",

"Industrial company stubs", "Information technology company stubs", "Leisure company stubs", "Manufacturing company stubs", "Media company stubs",

"Motor vehicle company stubs", "Music industry company stubs", "Musical instrument company stubs", "Petroleum company stubs", "Publishing company stubs",

"Restaurant stubs", "Retail company stubs", "Service company stub", "Technological company stubs", "Telecommunications company stubs", "Toy company stubs",

"Transport company stubs", "Video game company stubs", "African company stubs", "Asian company stubs", "European company stubs", "Canadian company stubs",

"Mexican company stubs", "United States company stubs", "Australian company stubs", "South American company stubs"]},

{parent:"politics stubs", children:["Asia politics stubs", "Europe politics stubs", "Canada politics stubs", "United States politics stubs", "Mexican politics stubs",

"New Zealand politics stubs", "South America politics stubs", "Political party stubs", "Political people stubs", "Politician stubs"]},

{parent:"politician stubs", children:["African politician stubs", "Asian politician stubs", "European politician stubs", "North American politician stubs",

"Oceanian politician stubs", "South American politician stubs"]},

{parent:"history stubs", children:["American Old West stubs", "Ancient Near East stubs", "Ancient Egypt stubs", "Ancient Greece stubs", "Ancient Rome stubs",

"Byzantine Empire stubs", "Kingdom of France stubs", "Mesoamerica stubs", "Nazi Germany stubs", "Netherlands Antilles stubs", "Ottoman Empire stubs",

"Pre-Columbian era stubs", "Soviet Union stubs", "Yugoslavia stubs", "African diaspora stubs", "East Slavic history stubs", "Jewish history stubs",

"African history stubs", "Asian history stubs", "European history stubs", "Canadian history stubs", "Mexican history stubs", "United States history stubs",

"South American history stubs", "Military history stubs"]},

{parent:"people stubs", children:["Academic biography stubs", "Artist stubs", "Astronaut stubs", "Business biography stubs", "Entertainer stubs", "Explorer stubs",

"Fashion biography stubs", "Graphic designer stubs", "Journalist stubs", "Medical biography stubs", "Model stubs", "Paramilitary biography stubs", "Piracy stubs",

"Religious biography stubs", "Scientist stubs", "Writer stubs", "African people stubs", "Asian people stubs", "European people stubs", "North American people stubs",

"Caribbean people stubs", "Central American people stubs", "Oceanian people stubs", "South American people stubs", "Nobility stubs", "Royalty stubs"]},

{parent:"scientist stubs", children:["Asian scientist stubs", "African scientist stubs", "Australian scientist stubs", "European scientist stubs", "North American scientist stubs",

"South American scientist stubs"]},

{parent:"science stubs", children:["scientist stubs", "Biology stubs", "Botany stubs", "Animal stubs", "Astronomy stubs", "Chemistry stubs", "Atmospheric science stubs",

"Geology stubs", "Oceanography stubs", "Physics stubs", "Health stubs", "Anthropology stubs", "Library and information science stubs", "Sociology stubs", "Natural science stubs"]},

{parent:"chordate stubs", children:["Amphibian stubs", "Bird stubs", "Fish stubs", "Mammal stubs", "Reptile stubs"]}, //Amphibians, Birds, Fish, Mammals, and Reptiles

{parent:"protostome stubs", children:["Mollusc stubs"]},

{parent:"arthropod stubs", children:["insect stubs", "Arachnid stubs"]},

{parent:"technology stubs", children:["Computing stubs", "Software stubs", "Telecommunications stubs", "Automotive part stubs", "Automotive technology stubs", "Electronics stubs",

"Energy stubs", "Engineering stubs", "Industry stubs", "Metalworking stubs", "Robotics stubs", "Sound technology stubs", "Tool stubs", "Woodworking stubs"]},

{parent:"transport stubs", children:["Africa transport stubs", "Asia transport stubs", "Europe transport stubs", "North America transportation stubs", "Oceania transport stubs",

"South America transport stubs", "Space stubs", "Vehicle stubs", "Water transport stubs", "Aviation stubs", "Rail transport stubs", "Road stubs"]},

{parent:"military stubs", children:["African military stubs", "Argentine military stubs", "Asian military stubs", "Australian military stubs", "Canadian military stubs",

"European military stubs", "Mexican military stubs", "United States military stubs", "Military personnel stubs"]},

{parent:"organization stubs", children:["Trade union stubs", "Art organization stubs", "Automotive organization stubs", "Business organization stubs",

"Criminal organization stubs", "Education organization stubs", "Environmental organization stubs", "Film organization stubs", "Fraternity and sorority stubs",

"Freemasonry stubs", "International organization stubs", "Lesbian, gay, bisexual, transgender (LGBT) organization stubs", "Linguistics organization stubs",

"Medical organization stubs", "Music organization stubs", "Non-profit organization stubs", "Paramilitary organization stubs", "Political organisation stubs",

"Professional association stubs", "Religious organization stubs", "Scientific organization stubs", "Youth organization stubs", "African organization stubs",

"Asian organization stubs", "Australian organisation stubs", "Canadian organization stubs", "European organisation stubs", "Mexican organization stubs",

"New Zealand organisation stubs", "South American organization stubs", "United States organization stubs"]}];

//functions

function SSloadDiff(){

window.location.href = mw.config.get('wgScript') + '?diff=' + SSnewid + '&oldid=' + SSoldid;

}

function SSreduceArray(unreducedArray){

var seen = {};

return unreducedArray.filter(function(item) {

return seen.hasOwnProperty(item) ? false : (seen[item] = true);

});

}

function SSescape(text, html){

if (html){

return text

.replace('"', """)

.replace("&", "&")

.replace("'", "'")

.replace("<", "<")

.replace(">", ">");

} else {

return text

.replace('"', "\\x22")

.replace("&", "\\x26")

.replace("'", "\\x27")

.replace("<", "\\x3c")

.replace(">", "\\x3e");

}

}

function SSgenerateDescendants(start){

if (stubTags[start].descendants !== null && stubTags[start].descendants.length !== 0){//already generated descendants

return stubTags[start].descendants.concat([start]);

}

if (stubTags[start].children === null || stubTags[start].children.length === 0){//no descendants

return [start];//for the rest, we are the only descendant

}

stubTags[start].children.forEach( function (value, index, array) {

stubTags[start].descendants = stubTags[start].descendants.concat(SSgenerateDescendants(value));//add children's descendants to our own

});

stubTags[start].descendants = SSreduceArray(stubTags[start].descendants);//remove duplicates

return stubTags[start].descendants.concat([start]);

}

function SSaddchild(parent, child){

if (stubTags.hasOwnProperty(parent)){//check parent exists

if (stubTags[parent].template !== null){//check if parent is a template, if so call this on its parents

stubTags[parent].parents.forEach(function (value, index, array) { SSaddchild(value, child); });

return;

}

//check if parent already has child

if (stubTags[parent].children.indexOf(child) !== -1){

return;

}

stubTags[parent].children.push(child);//add child to parents children

} else {//create a parent

stubTags[parent] = {display:parent, parents:[], children:[child], descendants:[], template:null};

stubTags.stubs.descendants.push(parent);//add to stubs' descendants

}

if (stubTags.hasOwnProperty(child)){//check if child exists

stubTags[child].parents.push(parent);//add parent to child

} else {//create child

stubTags[child] = {display:child, parents:[parent], children:[], descendants:[], template:null};

stubTags.stubs.descendants.push(child);//add to stubs' descendants

}

}

function SSconvertToTemplate(parent, template){

if (stubTags[parent].children === null){//already a template

return;

}

stubTags[parent].template = template;//set up template and display

stubTags[parent].display = '{{'+ template +'}}';

stubTemplates.push(('{{'+ template +'}}'));

stubTags[parent].children.forEach( function (value, index, array) {//remove all children's parent reference

index = stubTags[value].parents.indexOf(parent);//don't need index

if (index > -1) {

stubTags[value].parents.splice(index, 1);

}

SSaddchild(parent, value);//try to readd this as a child of parent

});

stubTags[parent].children = null;//clear the array

}

function SSdoExtraChildren(){

$.each(SSextraChildren, function (){ //do children in array

var parent = this.parent;

this.children.forEach(function (value, index, array){

SSaddchild(parent, value.toLowerCase());

});

});

addStubType("Unlisted stubs", "cat", "stubs");//special case for unlisted stubs

}

function stubFilter(){

filterString = document.getElementById("stub-search-search").value;

//Special case: no filterString then only show direct children

if (filterString.length === 0){

SSfilteredArray = SScurrent.children;

SSrerenderList();

return;

}

var filteringArray = SSunfilteredArray; //copy unfiltered array

filterString = filterString.toLowerCase(); //force lowercase

var filterArray = filterString.split(" "); //split string

for (var filterIndex = 0; filterIndex < filterArray.length; filterIndex++) {

SSfilteredArray = [];//clear the filtered output

filterString = filterArray[filterIndex];

for (var i = 0; i < filteringArray.length; i++) {

var testString = filteringArray[i];

if (testString.indexOf(filterString) > -1){

SSfilteredArray.push(testString);

}

}

filteringArray = SSfilteredArray;

}

SSrerenderList();

}

function loadStubs(){

for (var i = 0; i < stublists.length; i++) {

var stubType = stublists[i];

$.ajax({

url: mw.config.get("wgServer") + mw.config.get("wgScriptPath") + '/api.php?action=parse&prop=text&text=' + encodeURIComponent('__NOTOC____NOEDITSECTION__\{\{Wikipedia:WikiProject Stub sorting/Stub types/' + stubType + '}}') + '&format=json',

dataType: "json",

success: stubSearchLoadSome,

error: function() {

SSpagesLoaded++;//increment loaded pages on failure so the menu auto loads

console.log("Stub search failed to load a stub list.");

}

});

}

//special case for unlisted stubs

$.ajax({

url: mw.config.get("wgServer") + mw.config.get("wgScriptPath") + '/api.php?action=parse&prop=text&text=' + encodeURIComponent('__NOTOC____NOEDITSECTION__\{\{User:Danski454/unlisted stubs}}') + '&format=json',

dataType: "json",

success: stubSearchLoadSome,

error: function() {

SSpagesLoaded++;//increment loaded pages on failure so the menu auto loads

console.log("Stub search failed to load a stub list.");

}

});

SSdoExtraChildren();

}

function stubSearchLoadSome(data) {

var parseData = $(data.parse.text["*"]);

var parent = "stubs";

if (parseData.find("h1, h2").length){

switch (parseData.find("h1, h2").first().text().toLowerCase()){

case "sports":

parent = "sports stubs";

break;

case "culture":

parent = "culture stubs";

break;

case "contents":

parent = "architecture stubs";

break;

}

} else if (parseData.find("h3").length === 0) {//special case for unlisted stubs

parent = "unlisted stubs";

}

parseData.children("ul").children('li').each( function () {

var val = $(this);//'this' must be converted to jQuery

while (val.find("small").length !== 0) {//remove small tags (copied from toggleSmall.js)

var tag = val.find("small").first();

tag.before(tag.html());

tag.remove();

}

$('dl').remove();//remove description lists

generateStubData(val, parent);

});

if (parent !== "stubs" && parent !== "unlisted stubs"){

stubTags[parent].children.shift();//remove from its own children

stubTags[parent].parents = [];//remove from its own parents

SSaddchild("stubs", parent);//make a child of stubs

}

SSpagesLoaded++;

if (SSpagesLoaded == stublists.length + 1){//one list is not listed in the array

stubFilter();

}

}

function addStubType(obj, type, parent){

var name = obj.toLowerCase();

SSaddchild(parent, name);//this creates the object

if (type === "template"){

SSconvertToTemplate(name, obj);

} else {

stubTags[name].display = obj;//set display name

}

SSunfilteredArray.push(name);

SSfilteredArray.push(name);

}

function generateStubData(data, parent){

var cat;

cat = data.children("b").first().text();//categories are in bold

if (cat !== ""){

addStubType(cat, "cat", parent);

parent = cat.toLowerCase();

}

data.children("ul").children('li').each( function () {

generateStubData($(this), parent);

});

data.children("a").each( function () {//template links are not wrapped

var tag = $(this);

if (tag[0].previousElementSibling !== null && tag[0].previousElementSibling.innerText.endsWith("as a child of {{")){//get text before element and make sure it does not say x is a child of y

return true;

}

if (tag[0].previousElementSibling !== null && tag[0].previousElementSibling.innerText.endsWith("child of {{")){//get text before element and handle next element is child of x

var next = tag.next();//get next list item

if (next.is("ul")){//if next is a list get the first item of it

next = next.children("li").first();

}

SSaddchild(tag.text().toLowerCase(), next.children('b').first().text().toLowerCase());//make next link a child of us

return true;

}

if (tag.attr("href").search(/(wiki\/|=)Template:/) === -1){//must link to a template

return true;

}

addStubType(tag.text(), "template", parent);

});

}

function SSrerenderList(){

$('#stub-search-scroll').empty();

SSfilteredArray = SSreduceArray(SSfilteredArray); //remove duplicates

SSfilteredArray.forEach(function (value, index, array) {

var tag = stubTags[value];

if (index > 0){

$('#stub-search-scroll').append("•");

}

$('#stub-search-scroll').append('');

});

}

function SSrenderTemplate(){

$('#stub-search-scroll').empty();

$('#stub-search-scroll').append('Preview loading');

$.ajax({

url: mw.config.get("wgServer") + mw.config.get("wgScriptPath") + '/api.php?action=parse&prop=text&text=' + encodeURIComponent('__NOTOC____NOEDITSECTION__\{\{' + SScurrent.template + '}}') + '&format=json',

dataType: "json",

success: function (data){

$('#stub-preview').before(' Load changes
');

$('#stub-preview').html($(data.parse.text["*"]));

}

});

}

function stubSearchUp(){

var stub = $('#stub-search-parent-select option:selected').val();

selectStub(stub);

}

function loadStubSearch(){

if (SSactive){

return;

}

SSactive = true;

switch ( mw.config.get('skin') ){

case 'cologneblue':

$('#mw-content-text').before('

');

break;

case 'timeless':

$('#bodyContent').before('

');

break;

default:

$('#bodyContent').before('

');

}

$('#stub-search-up, #stub-search-parent-select, #stub-search-diff-button').hide();//up button, drop down and load diff button default to hidden

loadStubs();

}

function selectStub(name){

SSgenerateDescendants(name);//generate the descendants, if they have not already been.

SScurrent = stubTags[name];

$('#stub-search-cat').text(SScurrent.display);

document.getElementById("stub-search-search").value = "";//clear search

$('#stub-search-parent-select').empty();//clear old parents selector

if (SScurrent.parents !== null){

$('#stub-search-up, #stub-search-parent-select').show();//show parent selector and button

if (SScurrent.parents[0] === "unlisted stubs") {//special case

$('#stub-search-parent-select').append('')

}

SScurrent.parents.forEach(function (value, index, array){

$('#stub-search-parent-select').append('')

});

} else {

$('#stub-search-up, #stub-search-parent-select').hide();//hide parent selector and button

}

if (SScurrent.template === null){

SSunfilteredArray = SScurrent.descendants;

stubFilter();

} else {

SSrenderTemplate();

}

}

function addStub(){

replaceStub(true);//functions merged

}

function replaceStub(keepOld){//adds a stub template, optionally keeping old ones, this is copied from WP:US/G

$.getJSON(

mw.util.wikiScript('api'),

{

format: 'json',

action: 'query',

prop: 'revisions',

rvprop: 'content',

rvlimit: 1,

titles: mw.config.get( 'wgPageName' )

}

)

.done(function ( data ) {

var page, wikitext;

try {

for ( page in data.query.pages ) {

wikitext = data.query.pages[page].revisions[0]['*'];

replaceStubWikitext( wikitext, keepOld );

}

} catch ( e ) {

console.log(1);

alert( 'Edit failed' );

}

})

.fail( function() {

console.log(2);

alert( 'Edit failed' );

});

}

function replaceStubWikitext(data, keepOld){

added_text = '\n\n\n{{'+ SScurrent.template +'}}';

stubTemplates.forEach(function (value, index, array){

var re = new RegExp(value, "gi");

if (keepOld && data.match(re) !== null){

added_text = added_text + '\n' + value;//readd stub template to bottom

}

data = data.replace(re, "");//replace stubs with nothing using unescaped regex

});

data = data.replace(/\s+$/g, "");//remove trailing whitepace, do not use m flag

data = data + added_text;

console.log(data);

var summary = 'Replaced stub templates with {{'+ SScurrent.template +'}} using a tool';

if (keepOld){

summary = 'Added {{'+ SScurrent.template +'}} using a tool';

}

//save data

$.ajax({

url: mw.util.wikiScript( 'api' ),

type: 'POST',

dataType: 'json',

data: {

format: 'json',

action: 'edit',

title: mw.config.get( 'wgPageName' ),

text: data,

summary: summary,

token: mw.user.tokens.get( 'csrfToken' )

}

})

.done (function( data ) {

if ( data && data.edit && data.edit.result && data.edit.result == 'Success') {

if (SSoldid === null){

if (data.edit.oldrevid !== undefined){

SSoldid = data.edit.oldrevid;

} else {

alert("Unable to determine oldid");

}

}

if ($('#stub-search-load-diff').is(':checked') && data.edit.newrevid !== undefined){

if (SSoldid !== null && data.edit.oldrevid !== undefined){

window.location.href = mw.config.get('wgScript') + '?diff=' + data.edit.newrevid + '&oldid=' + SSoldid;//load diff

} else {

alert("Unable to load diff");

}

} else {

if (keepOld){

alert( 'Successfully added {{'+ SScurrent.template +'}} to the article' );

} else {

alert( 'Successfully replaced stubs with {{'+ SScurrent.template +'}}' );

}

if (data.edit.newrevid !== undefined){

SSnewid = data.edit.newrevid;

} else {

alert("Unable to determine newid");

}

if (SSoldid !== null && SSnewid !== null && $('#stub-search-diff-button').is(':hidden')){

$('#stub-search-diff-button').show();//add a load diff button if we have not loaded the diff

}

selectStub('stubs');//reset the UI, user probably wans to add another template

}

} else {

console.log(3);

alert( 'Edit failed' );

}

})

.fail ( function() {

console.log(4);

alert( 'Edit failed' );

});

}

$(document).ready(function(){

if ( mw.config.get( 'wgNamespaceNumber' ) === 0 || mw.config.get( 'wgPageName' ) === "Wikipedia:Sandbox" ) {//usable on articles and the sandbox (for testing and practice)

var portletLink = mw.util.addPortletLink('p-cactions', '#', 'Stub search', 'ca-stub-search', 'Tag this article as a stub', '');

$(portletLink).click(loadStubSearch);

}

});

//