User:Daniel Quinlan/Scripts/CitationGenerator.js

async function generateCitation { var textarea = document.getElementById('wpTextbox1'); var selection = textarea.value.substring(textarea.selectionStart, textarea.selectionEnd); var link = await getLink(selection);

if (link) { try { var citation = await fetchCitation(link); if (citation) { useCitation(textarea, selection, citation); }       } catch (error) { if (error && error.hasOwnProperty('title') && error.hasOwnProperty('message')) { basicDialog(error.title, error.message); } else { basicDialog('Unexpected error', error.toString); }       }    } }

async function getLink(selection) { var linkRegexes = [ /\{\{cite(?:\s+|\s.*\|\s*)url\s*=\s*(.*?)(?:\s*\|.*?)?\}\}/i, /\[(https?:\/\/\S+?)(?=[\s\]]|<\/\w+[^>]*>|$)/i, /\b(https?:\/\/\S+?)(?=\s|<\/\w+[^>]*>|$)/i, ];

var matchedLink = null;

for (var i = 0; i < linkRegexes.length; i++) { var linkMatch = selection.match(linkRegexes[i]); if (linkMatch && linkMatch[1]) { return linkMatch[1].trim; }   }

var promptLink = await basicPrompt("Enter citation link"); return promptLink.trim; }

async function fetchCitation(link) { var $fetchingDialog = basicDialog('Fetching citation from Citer tool...', link);

try { var dateFormat = encodeURIComponent("%Y-%m-%d");

var controller = new AbortController; var signal = controller.signal;

var timeoutId = setTimeout( => {           controller.abort;        }, 30000); // 30 seconds

// use "input_type" of "html" and "user_input" of {"html": "", "url": ""} to submit HTML var requestBody = { input_type: "url-doi-isbn", user_input: link };

var response = await fetch(`https://citer.toolforge.org/?date_format=${dateFormat}`, {           method: 'POST',            body: JSON.stringify(requestBody),            signal: signal        });

clearTimeout(timeoutId);

if (!response.ok) { var title = 'Citer error (status code: ' + response.status + ')'; var message = 'Failed to fetch citation text.'; try { var data = await response.json; if (Array.isArray(data) && data.length >= 1) { message = data[0]; }           } catch { throw { title: title, message: message }; }           throw { title: title, message: message }; }

var data = await response.json;

if (Array.isArray(data) && data.length >= 3) { var citationText = reviseCitation(data[2]); return citationText; } else { throw { title: 'Citer error', message: 'Invalid response from Citer tool.' }; }   } catch (error) { if (error.name === 'AbortError') { throw { title: 'Citer error', message: 'Request timed out.' }; }       throw error; } finally { $fetchingDialog.dialog('close'); } }

dialogDefaults = { modal: true, width: '50%', minHeight: '1em', position: { my: "center top", at: "center top", of: "form#editform" } };

function basicPrompt(title) { return new Promise((resolve, reject) => {       var $dialogContent = $(' ')            .append($(' ').css('width', '100%'))            .css('font-size', 'larger');

var dialog = $dialogContent.dialog({           ...dialogDefaults,            title: title,            close: function  {                $dialogContent.remove;            }        });

$dialogContent.find('input[type="text"]').keydown(function(event) {           if (event.keyCode === 13) {                var answer = $(this).val;                dialog.dialog('close');                resolve(answer);            }        }); }); }

function basicDialog(title, text) { var $dialogContent = $(' ').css({       'font-family': 'monospace',        'overflow-wrap': 'break-word',    }).text(text).dialog({        ...dialogDefaults,        title: title,        close: function  {            $dialogContent.remove;        }    }); return $dialogContent; }

function reviseCitation(citation) { // remove duplicate author if found var lastRegex = /\{\{cite(?:\s+|\s.*\|\s*)last\s*=\s*(.*?)\s*\|.*\|\s*last2\s*=\s*\1\s*/i; var firstRegex = /\{\{cite(?:\s+|\s.*\|\s*)first\s*=\s*(.*?)\s*\|.*\|\s*first2\s*=\s*\1\s*/i; if (citation.match(lastRegex) && citation.match(firstRegex)) { citation = citation.replace(/\|\s*last2\s*=\s*(.*?)\s*\|/, '|'); citation = citation.replace(/\|\s*first2\s*=\s*(.*?)\s*\|/, '|'); }

// reduce spacing citation = citation.replace(/ +\| (?=[\w-]+\s*=)/g, ' |'); citation = citation.replace(/(\|\s*[\w-]+)\s*=\s+/g, '$1=');

return citation; }

function useCitation(textarea, selection, citation) { var options = { edit: selection ? "Replace selection with citation" : "Insert citation", copy: "Copy to clipboard", cancel: "Cancel" };

promptWithOptions(citation, textarea, options, function(choice, editedText) {       switch (choice) {            case "edit":                var textBefore = textarea.value.substring(0, textarea.selectionStart);                var textAfter = textarea.value.substring(textarea.selectionEnd);                textarea.value = textBefore + editedText + textAfter;                $(textarea).trigger('change');                break;            case "copy":                copyToClipboard(editedText);                break;            default:                break;        }    }); }

function promptWithOptions(message, textarea, options, callback) { var $dialogContent = $(' ');

var selectedText = textarea.value.substring(textarea.selectionStart, textarea.selectionEnd); if (textarea.selectionStart !== textarea.selectionEnd) { var $selectedText = $('').css({           'padding-bottom': '0.25em',        }); $selectedText.append($('').text('Selected text:')); $selectedText.append($('').text(selectedText).css({ 'font-family': 'monospace', 'margin-left': '0' }));       $dialogContent.append($selectedText); }

var $textarea = $(' ').val(message).css({       'font-family': 'monospace',        'width': '100%',        'height': '10em',    }); $dialogContent.append($textarea);

var $buttonContainer = $(' ').addClass('button-container').css({       display: 'flex',        'justify-content': 'space-around',        'margin-top': '1em',        'margin-bottom': '0.25em'    });

Object.keys(options).forEach(function (key) {       var buttonText = options[key];        var $button = $(' ').text(buttonText).addClass('option-button').css({ padding: '0.5em 0.5em', 'font-size': 'medium' });       $button.click(function  { $(this).closest('.ui-dialog-content').dialog('close'); var editedText = $textarea.val; callback(key, editedText); });       $buttonContainer.append($button);    });

$dialogContent.append($buttonContainer);

var customCss = '.ui-dialog-title { font-size: medium; }'; $(' ').text(customCss).appendTo($dialogContent);

$dialogContent.dialog({       ...dialogDefaults,        title: 'Citation editor',        width: textarea.clientWidth * 0.5,        close: function  {            $dialogContent.remove;        }    }); }

function copyToClipboard(text) { var textarea = document.createElement("textarea"); textarea.value = text; document.body.appendChild(textarea); textarea.select; document.execCommand("copy"); document.body.removeChild(textarea); }

$(function {    if (mw.config.get('wgAction') === 'edit' || mw.config.get('wgAction') === 'submit') {        mw.util.addPortletLink('p-tb', '#', 'Generate citation', 'ca-generate-citation', 'Generate a citation');        $("#ca-generate-citation").click(function(event) { event.preventDefault; generateCitation; });   } });