/**
* SeekQuarry/Yioop --
* Open Source Pure PHP Search Engine, Crawler, and Indexer
*
* Copyright (C) 2009 - 2026 Chris Pollett chris@pollett.org
*
* LICENSE:
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* END LICENSE
*
* @author Eswara Rajesh Pinapala (edited Chris Pollett)
* @license https://www.gnu.org/licenses/ GPL3
* @link https://www.seekquarry.com/
* @copyright 2014-2021
* @filesource
*/
/*
* This script adds buttons to textareas on a page which when clicked insert
* the relevant way to format something using wiki syntax.
*
* The editor automatically renders the editor buttons using the following list
* of button names for configuration. These can be set on the data-buttons
* attribute of the textarea
* {
* "wikibtn-bold"
* "wikibtn-italic"
* "wikibtn-underline"
*" wikibtn-strike"
* "wikibtn-nowiki"
* "wikibtn-hyperlink"
* "wikibtn-bullets"
* "wikibtn-numbers"
* "wikibtn-hr"
* "wikibtn-heading"
* "wikibtn-search"
* "wikibtn-table"
* "wikibtn-slide"
* "wikibtn-definitionlist"
* }
*/
/**
* Used to contain formatting info about all buttons on the wiki editor
* @var Array
*/
var editor_all_buttons = [];
/**
* Used to store formatting info buttons on a particular textarea
* @var Array
*/
var editor_buttons = [];
/**
* Object that buffers selection information.
* @var Object
*/
var editor_buffer = {};
/**
* Object containing all icons for editor toolbar
* @var Object
*/
var editor_toolbar_icons = {
"wikibtn-bold": "<b>B</b>",
"wikibtn-italic": "<i>I</i>",
"wikibtn-underline": "<u>U</u>",
"wikibtn-strike": "<s>S</s>",
"wikibtn-nowiki": `<div class='wiki-icon-nowiki'>W</div>`,
"wikibtn-hyperlink": "🔗",
"wikibtn-bullets": `<div class="wikibtn-icon-bullets"
><div>⋮</div></div>≡`,
"wikibtn-numbers": `<div class='wikibtn-icon-numbers'
><div>1</div><div>2</div><div>3</div></div>≡`,
"wikibtn-hr": "⎯",
"wikibtn-heading": "H",
"wikibtn-search-widget": "🔎",
"wikibtn-table": "⊞",
"wikibtn-slide": "🧑🏫",
"wikibtn-definitionlist": "≝",
"wikibtn-leftaligned": `<div class="wikibtn-icon-leftaligned"
><span class="icon-first">_</span><span class="icon-second"
>_</span><span class="icon-third">_</span><span class="icon-fourth"
>_</span></div>`,
"wikibtn-centeraligned": `<div class="wikibtn-icon-centeraligned"
><span class="icon-first">_</span><span class="icon-second"
>_</span><span class="icon-third">_</span><span class="icon-fourth"
>_</span></div>`,
"wikibtn-rightaligned": `<div class="wikibtn-icon-rightaligned"
><span class="icon-first">_</span><span class="icon-second"
>_</span><span class="icon-third">_</span><span class="icon-fourth"
>_</span></div>`,
};
/*
* Add buttons for common wiki operation before the textareas on a page.
*/
function editorizeAll()
{
var text_areas = tag("textarea");
var len = text_areas.length;
var ids = new Array();
for (i = 0; i < len; i++)
{
if (text_areas[i].id){
editorize(text_areas[i].id);
}
}
}
/**
* Adds Wiki editor buttons to a textarea with the given id
*
* @param String id identifier of the text area to have wiki buttons added to
*/
function editorize(id)
{
/*
check if the editor div exists.
*/
var node = elt(id);
if (node === null) {
/*
If the div to render wiki_editor is not found, do nothing.
*/
alert("No textarea found with id = " + id);
return false;
}else{
node.addEventListener("focus", function(event)
{
enableKBShortcuts(id);
}, true);
}
var button_string = "";
/*
Initialize tool bar wiki_buttons object
*/
editor_buttons[id] = {};
/*
filter the buttons according to the data-buttons
attribute.
*/
filterButtons(id);
/*
Buttons are rendered below.
*/
for (var prop in editor_buttons[id]) {
var no_buttons = ['wikibtn-search', 'wikibtn-table', 'wikibtn-heading'];
if (editor_buttons[id].hasOwnProperty(prop)
&& (no_buttons.indexOf(prop) === -1)) {
translate_prop = tl[prop.replace(/wikibtn-/, 'wiki_js_')];
button_string +=
'<div class="' + prop +' icon-button-container">' +
'<a class="icon-anchor-button">' +
'<span role="img" ' +
'aria-label="' + translate_prop +
'" onclick="wikifySelection(\'' +prop + '\', \''+ id +'\');">'+
editor_toolbar_icons[prop] +
'</span></a></div>';
}
}
/*
Render the wiki-popup-prompt div used to prompt
user input.
*/
var editor_toolbar = '<div id="wiki-popup-prompt-' + id +
'" class="wiki-popup-prompt" style="display: none;"></div>' +
'<div class="wiki-buttons">' + button_string;
/*
check if heading was desired and render if was.
*/
if (editor_buttons[id].hasOwnProperty('wikibtn-heading')) {
translate_prop = tl['wiki_js_heading'];
editor_toolbar += '<select id="wiki-heading-' +id +
'" title="' + translate_prop +
'" value="heading" ' +
'onchange="addWikiHeading(\''+ id +'\');">' +
'<option selected="" disabled="">'+
tl['wiki_js_heading'] + '</option>' +
'<option value="1">H1</option>' +
'<option value="2">H2</option>' +
'<option value="3">H3</option>' +
'<option value="4">H4</option>' +
'</select>';
}
/*
check if table was desired and render if was.
*/
if (editor_buttons[id].hasOwnProperty('wikibtn-table')) {
translate_prop = tl['wiki_js_add_wiki_table'];
editor_toolbar += '<div class="wikibtn-table icon-button-container">' +
'<a class="icon-anchor-button">' +
'<span role = "img" ' +
'aria-label = "' +translate_prop +
'" onclick = "addWikiTable(\''+ id +'\');">'+
editor_toolbar_icons["wikibtn-table"] +
'</span></a></div>';
}
/*
check if search was desired and render if was.
*/
if (editor_buttons[id].hasOwnProperty('wikibtn-search')) {
translate_prop = tl['wiki_js_add_search'];
editor_toolbar += '<div class="wikibtn-search-widget ' +
'icon-button-container">' +
'<a class="icon-anchor-button">' +
'<span role="img" aria-label="' + translate_prop +
'" onclick = "addWikiSearch(\''+ id +'\');">'+
editor_toolbar_icons["wikibtn-search-widget"] +
'</span></a></div>';
}
editor_toolbar += '</div>';
/*
Insert the toolbar div before the textarea
*/
node.insertAdjacentHTML("beforebegin", editor_toolbar);
return;
}
/**
* Method to return standard buttons as an object based on the current
* render engine (mediawiki, markdown, etc).
*
* @return Object containing button configurations with their markup syntax
*/
function getStandardButtonsObject()
{
if (tl?.wiki_js_render_engine === "markdown") {
return {
'wikibtn-bold': ['**', '**'],
'wikibtn-italic': ['*', '*'],
'wikibtn-strike': ['~~', '~~'],
'wikibtn-nowiki': ['```', '```'],
'wikibtn-hyperlink': ['[', '](url)'],
'wikibtn-bullets': ['* ' + tl['wiki_js_bullet'] + ' \n'],
'wikibtn-numbers': ['1. ' + tl['wiki_js_enum'] + '. \n'],
'wikibtn-hr': ['---\n'],
'wikibtn-heading': '', // Handled separately
'wikibtn-table': '',
'wikibtn-slide': ['=' + tl['wiki_js_slide_sample_title'] + '=\n'
+ '* ' + tl['wiki_js_slide_sample_bullet'] + '\n'
+ '* ' + tl['wiki_js_slide_sample_bullet'] + '\n'
+ '* ' + tl['wiki_js_slide_sample_bullet'] + '\n'
+ '....' + '\n'],
'wikibtn-definitionlist': ['**Term**: Definition\n'],
};
}
return {
'wikibtn-bold': ['---', '---'],
'wikibtn-italic': ['--', '--'],
'wikibtn-underline': ['<u>', '</u>'],
'wikibtn-strike': ['<s>', '</s>'],
'wikibtn-nowiki': ['<nowiki>', '</nowiki>'],
'wikibtn-hyperlink': ['[[', ']]'],
'wikibtn-slide': ['=' + tl['wiki_js_slide_sample_title'] + '=\n'
+ '* ' + tl['wiki_js_slide_sample_bullet'] + '\n'
+ '* ' + tl['wiki_js_slide_sample_bullet'] + '\n'
+ '* ' + tl['wiki_js_slide_sample_bullet'] + '\n'
+ '....' + '\n'],
'wikibtn-bullets': ['* ' + tl['wiki_js_bullet'] + ' \n'],
'wikibtn-numbers': ['# ' + tl['wiki_js_enum'] + ' \n'],
'wikibtn-definitionlist': [
'; ' + tl['wiki_js_definitionlist_item']
+ ' : ' + tl['wiki_js_definitionlist_definition']
+ '' + '\n'
+ '; ' + tl['wiki_js_definitionlist_item']
+ ' : ' + tl['wiki_js_definitionlist_definition']
+ '' + '\n'],
'wikibtn-leftaligned': ['{{left|','}}'],
'wikibtn-centeraligned': ['{{center|','}}'],
'wikibtn-rightaligned': ['{{right|','}}'],
'wikibtn-hr': ['---- \n']
};
}
/**
* Filters the buttons according to exclusion/inclusion rules.
* If the 'data-buttons' attribute value starts with 'all' - Only the buttons
* need to be excluded should be followed, with their name specified with a '!'
* prefix.
* Any buttons with no '!' prefix will eb ignored
* example : all,!bol,!italic will render editor with all buttns excluding bold
* and italic
*
* If the 'data-buttons' attribute value does not starts with 'all', any button
* names followed will only be included in the editor.
* Any buttons with '!' prefix will be ignored.
* example : bol,italic will render editor only with bold and italic buttons.
*
* @param String id identifier of the textarea that we want editor buttons on
*/
function filterButtons(id)
{
editor_all_buttons[id] = getStandardButtonsObject();
editor_all_buttons[id]['wikibtn-heading'] = '';
editor_all_buttons[id]['wikibtn-search'] = '';
editor_all_buttons[id]['wikibtn-table'] = '';
var wiki_text_div = elt(id);
var wiki_buttons = wiki_text_div.getAttribute("data-buttons");
if (wiki_buttons) {
var wiki_buttons_array = wiki_buttons.split(',');
var buttons_array_length = wiki_buttons_array.length;
var included_buttons = new Array();
var excluded_buttons = new Array();
var exc = false;
if (wiki_buttons_array[0].trim() === 'all') {
exc = true;
}
if (wiki_buttons_array[0].trim() === 'none') {
editor_buttons[id] = [];
return;
}
for (var i = 0; i < buttons_array_length; i++) {
wiki_buttons_array[i] = wiki_buttons_array[i].trim();
var firstChar = wiki_buttons_array[i].charAt(0);
if (wiki_buttons_array[i] && (exc === true) && firstChar === '!') {
wiki_buttons_array[i] = wiki_buttons_array[i].substr(1);
if (editor_all_buttons[id].hasOwnProperty(
wiki_buttons_array[i])) {
excluded_buttons.push(wiki_buttons_array[i]);
delete editor_all_buttons[id][
wiki_buttons_array[i]];
}
} else if (wiki_buttons_array[i] && exc === false) {
if (editor_all_buttons[id].hasOwnProperty(
wiki_buttons_array[i])) {
included_buttons.push(wiki_buttons_array[i]);
editor_buttons[id][wiki_buttons_array[i]] =
editor_all_buttons[id][wiki_buttons_array[i]];
}
}
}
} else {
editor_buttons[id] = editor_all_buttons[id];
}
if (Object.keys(editor_buttons[id]).length === 0) {
editor_buttons[id] = editor_all_buttons[id];
}
}
/**
* This function returns the selected text from text_area.
* The returned Object has properties for
* selected text, entire prefix and entire suffix strings to the
* selected text.
*
* @param String id the identifier for the textarea to get the selected text for
* @return Object with properties of the selected text set
*/
function getSelection(id)
{
/*
Select the DOM element
*/
var text_area = elt(id);
/*
IE?
*/
if (document.selection) {
var selection_bookmark =
document.selection.createRange().getBookmark();
var sel = text_area.createTextRange();
sel.moveToBookmark(selection_bookmark);
var sleft = text_area.createTextRange();
sleft.collapse(true);
sleft.setEndPoint("EndToStart", sel);
text_area.selectionStart = sleft.text.length;
text_area.selectionEnd = sleft.text.length + sel.text.length;
text_area.selectedText = sel.text;
var selected_text_prefix = text_area.value.substring(0,
text_area.selectionStart);
var selection = sel.text;
var selected_text_suffix = text_area.value.substring(
text_area.selectionEnd, text_area.textLength);
} else if (typeof (text_area.selectionStart) !== "undefined") {
/**
Things are pretty straightforward in Mozilla based
browsers & IE > 10.
We can get the selectionStart & selectionEnd character
position directly.
Then compute the prefix and suffix using substr method.
*/
var selected_text_prefix = text_area.value.substr(0,
text_area.selectionStart);
var selection = text_area.value.substr(
text_area.selectionStart,
text_area.selectionEnd - text_area.selectionStart);
var selected_text_suffix = text_area.value.substr(
text_area.selectionEnd);
}
var obj = {};
obj.selection = selection;
obj.selected_text_prefix = selected_text_prefix;
obj.selected_text_suffix = selected_text_suffix;
setCaretPosition(text_area, text_area.selectionEnd);
return obj;
}
/**
* This function can be used to set
* the caret position in a text field object like a text area.
*
* @param element text_field in which the caret is to be set.
* @param int pos position of the caret to be set.
* @return undefined
*/
function setCaretPosition(text_field, pos)
{
if (text_field.setSelectionRange) {
text_field.focus();
text_field.setSelectionRange(pos, pos);
} else if (text_field.createTextRange) {
var range = text_field.createTextRange();
range.collapse(true);
range.moveEnd('character', pos);
range.moveStart('character', pos);
range.select();
}
}
/**
* Looks up the name'd wiki formatting task, then uses it type to
* render it in the textarea with identifier id
*
* @param String name identifier of the wiki task to be performed
* @param String id identifier of the textarea to add wiki code for the given
* task
*/
function wikifySelection(name, id)
{
var length = 0;
length = editor_buttons[id][name].length;
if (length === 2) {
wikify(editor_buttons[id][name][0].replace(new RegExp('-', 'g'), "\'"),
editor_buttons[id][name][1].replace(new RegExp('-', 'g'), "\'"),
name, id);
} else if (length === 1) {
insertTextAtCursor(editor_buttons[id][name][0], id);
}
}
/**
* This creates a wiki string from wiki_prefix, wiki_suffix, task_name or
* currently selected text and then replaces the currently selected text in the
* DOM element id with this string. Currently, selected text is computed using
* getSelection(id).
*
* @param String wiki_prefix prefix to be added to wikify the selected text.
* @param String wiki_suffix suffix to be added to wikify the selected text.
* @param String task_name name of the task to render default selection text.
* @param String id of textarea to wikify
*/
function wikify(wiki_prefix, wiki_suffix, task_name, id)
{
var br = '';
/*
Select the DOM element - wikiText
*/
var text_area = elt(id);
var obj = getSelection(id);
var selection = obj.selection;
var selected_text_prefix = obj.selected_text_prefix;
var selected_text_suffix = obj.selected_text_suffix;
if (!selection && wiki_prefix === '[[') {
wiki_popup_prompt = elt('wiki-popup-prompt-'+id);
if (wiki_popup_prompt.hasChildNodes()) {
/*
Remove childnodes, if any exist.
*/
while (wiki_popup_prompt.hasChildNodes()) {
wiki_popup_prompt.removeChild(wiki_popup_prompt.lastChild);
}
}
wiki_popup_prompt.appendChild(createHyperlinkForm(id));
editor_buffer = obj;
toggleDisplay('wiki-popup-prompt-' + id);
}
if (!selection && wiki_prefix !== '[[') {
br = '\n';
selection = tl[task_name.replace('wikibtn-', 'wiki_js_')];
}
if (!selection && wiki_prefix !== '[[') {
selection = task_name;
}
/*
Now Add the wrap the selected text between the wiki stuff,
and then wrap the selected
text between actual selected text's prefix and suffix.
Replace the text_area contents with the result.
*/
if (selection) {
var tmp = selected_text_prefix + wiki_prefix + selection + wiki_suffix
+ br;
text_area.value = tmp + selected_text_suffix;
setCaretPosition(text_area, tmp.length);
}
}
/**
* This is a special function for processing user input
* when the user clicks on hyperlink, and inserts a hyper link.
*
* @param String id of textarea the wiki editor instance is associated with
* @return String
*/
function addWikiHyperlink(id)
{
toggleDisplay('wiki-popup-prompt-' + id);
var title = elt('wikify-link-text-' + id).value;
var link = elt('wikify-link-url-' + id).value;
if (!link && !title) {
return false;
}
if (!link || !title) {
out = "" + link + title;
} else {
out = link + "|" + title;
}
insertTextAtCursor("[[" + out + "]]" + '\n', id);
}
/**
* This function takes in number of rows, columns and
* header_text(if desired) and constructs a table in wiki markup.
*
* @param int rows number of rows intended in the table.
* @param int cols number of cols intended in the table.
* @param string example_text placeholder text to be inserted in each
* table field.
* @param string header_text if header text is intended, placeholder
* text for column header.
* @return string
*/
function createWikiTable(rows, cols, example_text, header_text)
{
var br = "\n";
var table = "{|" + br;
for (i = 0; i < rows; i++) {
if (header_text && i === 0) {
table = table + "|- " + br;
table = table + "! ";
for (j = 0; j < cols; j++) {
table = table + header_text + " ";
if (j < (cols - 1)) {
table = table + "!!" + " ";
} else {
table = table + br;
}
}
}
table = table + "|- " + br;
table = table + "| ";
for (j = 0; j < cols; j++) {
table = table + example_text + " ";
if (j < (cols - 1)) {
table = table + "||" + " ";
} else {
table = table + br;
}
}
}
table = table + "|}";
return table;
}
/**
* Inserts wiki search widget in textarea identified by id
*
* @param String id of textarea to insert wiki search widget
*/
function addWikiSearch(id)
{
wiki_popup_prompt = elt('wiki-popup-prompt-'+id);
if (wiki_popup_prompt.hasChildNodes()) {
/*
Remove child nodes, if any exist.
*/
while (wiki_popup_prompt.hasChildNodes()) {
wiki_popup_prompt.removeChild(wiki_popup_prompt.lastChild);
}
}
wiki_popup_prompt.appendChild(createSearchWidgetForm(id));
toggleDisplay('wiki-popup-prompt-'+id);
}
/**
* Gets the size of the search widget to load.
*
* @param String id identifier of the textarea to put search form on
*/
function useInputForSearch(id)
{
toggleDisplay('wiki-popup-prompt-'+id);
size_elt = elt('wiki-search-size-'+id);
var size = size_elt.options[size_elt.selectedIndex].value;
insertTextAtCursor( "{{search:default|size:" + size+ "|placeholder:" +
tl['wiki_js_placeholder'] + "}}\n", id);
}
/**
* Util function to Stringify an JS Object
*
* @param Object js_object the javascript object to be converted to string.
* @return String
*/
function objToString(js_object)
{
var json_string = [];
for (var property in js_object) {
if (js_object.hasOwnProperty(property)) {
json_string.push('"' + property + '"' + ':' + js_object[property] );
}
}
json_string.push();
return '{' + json_string.join(',') + '}';
}
/**
* This is invoked by the editor to insert a wiki table.
* This functions takes care okf the user input for rows/columns/etc
* and leverages createWikiTable function to construct the table.
*
* @param String id
*/
function addWikiTable(id)
{
wiki_popup_prompt = elt('wiki-popup-prompt-'+id);
if (wiki_popup_prompt.hasChildNodes()) {
/*
Remove childnodes, if any exist.
*/
while (wiki_popup_prompt.hasChildNodes()) {
wiki_popup_prompt.removeChild(wiki_popup_prompt.lastChild);
}
}
wiki_popup_prompt.appendChild(createTableForm(id));
toggleDisplay('wiki-popup-prompt-'+id);
}
/**
* Prompt/Draws a form to get input from a user, then uses that input to draw
* a wiki table in the textarea with identifier id
*
* @param String id string identifier of the textarea to draw table in
*/
function useInputForTable(id)
{
var rows = elt('wiki-rows-'+id).value;
var cols = elt('wiki-cols-'+id).value;
var head_checked = elt('wiki-insert-heading-'+id).checked;
addWikiTableFromInput(rows, cols, head_checked, id);
}
/**
* Using the input from the user, like number of rows,cols etc.
* builds a table in wiki markup and inserts at cursor.
* @param int rows number of rows desired
* @param int cols number of cols desired
* @param Boolean head_checked heading desired or not.
*/
function addWikiTableFromInput(rows, cols, head_checked, id)
{
toggleDisplay('wiki-popup-prompt-' + id);
if (!cols || !rows) {
return;
}
if (head_checked) {
var table = createWikiTable(rows, cols,
tl['wiki_js_example'], tl['wiki_js_table_title']);
} else {
table = createWikiTable(rows, cols, tl['wiki_js_example']);
}
insertTextAtCursor(table + '\n', id);
}
/**
* Accepts text as parameter to be inserted at caret's
* current position.
*
* @param string text string that is intended to be placed at current cursor.
*/
function insertTextAtCursor(text, textarea_id)
{
var field = elt(textarea_id);
if (document.selection) {
var range = document.selection.createRange();
if (!range || range.parentElement() !== field) {
field.focus();
range = field.createTextRange();
range.collapse(false);
}
range.text = text;
range.collapse(false);
range.select();
} else {
field.focus();
var val = field.value;
var selStart = field.selectionStart;
var caretPos = selStart + text.length;
field.value = val.slice(0, selStart) +
text + val.slice(field.selectionEnd);
field.setSelectionRange(caretPos, caretPos);
}
}
/**
* Used to mark-up selected text in textarea given by id with a heading whose
* size is decided by a select drop down
*
* @param String id of a text area that will mark
*/
function addWikiHeading(id)
{
select_object = elt("wiki-heading-" + id);
var heading_size = select_object.value;
var markup_text;
if (tl?.wiki_js_render_engine === "markdown") {
markup_text = fillChars("#", heading_size) + " ";
wikify(markup_text, "\n", "wikibtn-heading" + heading_size, id);
select_object.selectedIndex = 0;
return;
}
markup_text = fillChars("=", heading_size);
select_object.selectedIndex = 0;
wikify(markup_text, markup_text, "wikibtn-heading" + heading_size , id);
}
/**
* Fills a character array and returns as a string consisting given count
* of a given character.
*
* @param String c character to be filled into an array.
* @param int n number of times the character to be repeated.
* @return String consistring of n many c's
*/
function fillChars(c, n)
{
for (var e = ""; e.length < n; ) {
e += c;
}
return e;
}
/**
* Created an input prompt HTML element with 2 text fields and a checkbox.
* @param String id
* @return HTMLFormElement
*/
function createTableForm(id)
{
var wiki_prompt_for_table = ce("div");
var table_form =
'<div class="wiki-popup-content">' +
'<h2 class="center">'+tl['wiki_js_add_wiki_table']+'</h2>' +
'<form onsubmit="useInputForTable(\'' + id
+'\');return false;"><table><tr>'+
'<th class="float-opposite"><label for="wiki-rows-' + id + '" >' +
tl['wiki_js_for_table_rows'] + '</label></th>' +
'<td><input id="wiki-rows-' + id +
'" name="wiki-prompt-rows" '+ 'type="text"></td></tr>'+
'<tr><th class="float-opposite"><label for="wiki-cols-' + id + '">' +
tl['wiki_js_for_table_cols'] + '</label></th>'+
'<td><input id="wiki-cols-' + id +'" name="wiki-prompt-cols" '+
'type="text"></td></tr>'+
'<tr><th class="float-opposite"><label for="wiki-insert-heading-' +
id + '" >' + tl['wiki_js_prompt_heading'] + '</label></th>' +
'<td><input id="wiki-insert-heading-' + id + '"' +
'name="wiki-insert-heading" type="checkbox"></td></tr>' +
'</table>' +
'<div class="center"><button onmousedown="useInputForTable(\'' + id
+'\')" name="submit" class="button-box">'+ tl['wiki_js_submit'] +
'</button> ' +
'<button onmousedown="toggleDisplay(\'wiki-popup-prompt-' + id +
'\')" name="close" class="button-box" >' + tl['wiki_js_cancel'] +
'</button></form></div>';
wiki_prompt_for_table.innerHTML = table_form;
return wiki_prompt_for_table;
}
/**
* Creates an HTMLFormElement with two text fields to get the URL and text of
* a link
* @param String id identifier of the text area that we want to add a wiki link
* to
* @return HTMLFormElement containing form that we will use to get info from
* the user so that we can later add a wiki link
*/
function createHyperlinkForm(id)
{
var wiki_prompt_for_hyperlink = ce("div");
wiki_prompt_for_hyperlink.innerHTML =
'<div class="wiki-popup-content">' +
'<h2 class="center">'+tl['wiki_js_add_hyperlink']+'</h2>' +
'<form onsubmit="addWikiHyperlink(\''+
id +'\');return false;" '+
'><table><tr><th><label for="wikify-link-text-'+id+'">' +
tl['wiki_js_link_text'] + '</label></th>' +
'<td><input id="wikify-link-text-'+id+'" '+
'name="wikify-link-text" type="text"></td></tr>' +
'<tr><th><label for="wiki-link-url-'+id+'">' +
tl['wiki_js_link_url'] + '</label></th>' +
'<td><input id="wikify-link-url-'+id+'" ' +
'name="wikify-enter-link" type="text"></td></tr></table>' +
'<div class="center"><button onmousedown="addWikiHyperlink(\''+
id +'\')" ' + 'name="submit" class="button-box">'+
tl['wiki_js_submit'] +'</button> ' +
'<button onmousedown="toggleDisplay(\'wiki-popup-prompt-' + id +
'\')" name="close" class="button-box">' + tl['wiki_js_cancel'] +
'</button></div></form></div>';
return wiki_prompt_for_hyperlink;
}
/**
* Creates an an HTMLFormElement with a form to get the kind of search bar
* that is desired to be inserted into a wiki document
* @param String id identifier of the textarea used to insert wiki text
*/
function createSearchWidgetForm(id)
{
var search_elt = ce("div");
search_form = '<div class="wiki-popup-content">'+
'<h2 class="center">' + tl['wiki_js_add_search'] + '</h2>' +
'<div class="center"><form onsubmit="useInputForSearch(\'' + id +
'\');return false;">' +
'<select name="wiki_search_size" id="wiki-search-size-' + id + '" >' +
'<option disabled="" selected="" >' + tl['wiki_js_search_size'] +
'</option>'+
'<option value="small">' + tl['wiki_js_small'] + '</option>' +
'<option value="medium">' + tl['wiki_js_medium'] + '</option>' +
'<option value="large">' + tl['wiki_js_large'] + '</option>'+
'</select></div>' +
'<div class="center"><button onmousedown="useInputForSearch(\'' + id +
'\');"'+ 'name="submit" class="button-box">' +
tl['wiki_js_submit'] + '</button> ' +
'<button onmousedown="toggleDisplay(\'wiki-popup-prompt-' + id +
'\')" name="close" class="button-box">' + tl['wiki_js_cancel'] +
'</button></form></div></div>';
search_elt.innerHTML = search_form;
return search_elt;
}
/**
* Sets up keyboard events for a wiki textarea after it is in focus
* @param String id identifier of the textarea used to handle keyboard events
* for
*/
function enableKBShortcuts(id)
{
var ctrl_down = false;
/**
* onkeyup handler: clears the Ctrl-down flag when the user
* releases the Control key.
*
* @param {KeyboardEvent} e
*/
document.onkeyup = function keyUp(e)
{
if (e.which == 17) ctrl_down = false;
};
/**
* onkeydown handler: tracks Ctrl-down state and routes
* Ctrl-B / Ctrl-I / Ctrl-U into the bold / italic /
* underline wikify helpers.
*
* @param {KeyboardEvent} e
*/
document.onkeydown = function keyDown(e)
{
if (e.which == 17) {
ctrl_down = true;
}
if (e.which == 66 && ctrl_down == true) {
wikifySelection('wikibtn-bold', id);
return false;
}
if (e.which == 73 && ctrl_down == true) {
wikifySelection('wikibtn-italic', id);
return false;
}
if (e.which == 85 && ctrl_down == true) {
wikifySelection('wikibtn-underline', id);
return false;
}
};
}
/**
* Used to add the wiki text for including a resource into a wiki page
* @param String resource_name string name of resource to be added
* @param String id identifier of the textarea used to insert wiki text
* @param String sub_path path in resource folder to reasource
*/
function addToPage(resource_name, textarea_id, sub_path)
{
if (typeof sub_path === 'undefined' || sub_path == '') {
wikify("((resource:", "|" + tl['wiki_js_resource_description'] +
resource_name.replace('"', '"') + "))",
resource_name.replace('"', '"'), textarea_id);
} else {
wikify("((resource:", "|" +sub_path +
"|" + tl['wiki_js_resource_description'] +
resource_name.replace('"', '"') + "))",
resource_name.replace('"', '"'), textarea_id);
}
}
/*
* Set up event listeners to manage column splitter resizes when we have
* tabular output such as in media lists
*/
document.addEventListener('DOMContentLoaded', function () {
const direction =
document.getElementsByTagName('html')[0].getAttribute('dir');
const direction_sign = (direction == 'rtl') ? -1 : 1;
let split_adjusters = document.getElementsByClassName('split-adjuster');
// Query the element
for (const split_adjuster of split_adjusters) {
let left_elt = split_adjuster.previousElementSibling;
let right_elt = split_adjuster.nextElementSibling;
// The current position of mouse
let x = 0;
let y = 0;
let left_width = 0;
/**
* mousedown handler on a column split adjuster: records
* the starting cursor position and the left column's
* current width, then arms the mousemove / mouseup
* handlers on the document.
*
* @param {MouseEvent} e
*/
let mouseDownHandler = function (e) {
x = e.clientX;
y = e.clientY;
left_width = left_elt.getBoundingClientRect().width;
document.addEventListener('mousemove', mouseMoveHandler);
document.addEventListener('mouseup', mouseUpHandler);
};
/**
* mousemove handler bound during a split-adjuster drag:
* resizes the left column's width based on cursor delta,
* clamped to a minimum of 10%.
*
* @param {MouseEvent} e
*/
let mouseMoveHandler = function (e) {
const dx = direction_sign*(e.clientX - x);
const dy = e.clientY - y;
const new_left_width = Math.max(((left_width + dx) * 100) /
split_adjuster.parentNode.getBoundingClientRect().width, 10);
left_elt.style.width = `${new_left_width}%`;
split_adjuster.style.cursor = 'col-resize';
document.body.style.cursor = 'col-resize';
left_elt.style.userSelect = 'none';
left_elt.style.pointerEvents = 'none';
right_elt.style.userSelect = 'none';
right_elt.style.pointerEvents = 'none';
};
/**
* mouseup handler bound during a split-adjuster drag:
* resets cursor styles, restores text selection, and
* detaches itself plus the mousemove handler from the
* document.
*/
let mouseUpHandler = function () {
split_adjuster.style.removeProperty('cursor');
document.body.style.removeProperty('cursor');
left_elt.style.removeProperty('user-select');
left_elt.style.removeProperty('pointer-events');
right_elt.style.removeProperty('user-select');
right_elt.style.removeProperty('pointer-events');
document.removeEventListener('mousemove', mouseMoveHandler);
document.removeEventListener('mouseup', mouseUpHandler);
};
split_adjuster.addEventListener('mousedown', mouseDownHandler);
}
});
/**
* Toggles the visibility of a wiki-page AI summary box. The
* first time it's shown, the summary is fetched asynchronously
* via the api/summarize endpoint and inserted on completion.
*
* @param {string} csrf_token CSRF token for the API call
* @param {string} base_url controller base URL ("?" suffix as
* appropriate)
* @param {string|number} summary_id the wiki page / thread id
* whose summary is being toggled
*/
function toggleAISummary(csrf_token, base_url, summary_id)
{
var summary_box = document.getElementById("ai-summary-" + summary_id);
if (!summary_box) return;
if (summary_box.style.display === "none" || summary_box.style.display === "") {
summary_box.style.display = "block";
summary_box.innerHTML = "<p>" + tl['wiki_js_summarizing'] + "</p>";
var url = base_url + "?thread_id=" + encodeURIComponent(summary_id) +
"&c=api&a=summarize&YIOOP_TOKEN=" +
encodeURIComponent(csrf_token);
fetch(url, { method: "POST",
headers: {
"User-Agent": "Yioop-Web-Client/1.0",
"Content-Type": "application/x-www-form-urlencoded"
},
credentials: "include"
})
.then(function(response) {
if (!response.ok) {
throw new Error("HTTP error! Status: " + response.status);
}
return response.json();
})
.then(function(data) {
if (data.SUMMARY) {
summary_box.innerHTML = "<p>" + data.SUMMARY + "</p>";
} else if (data.ERRORS) {
summary_box.innerHTML = "<p>" + data.ERRORS.join(", ") + "</p>";
} else {
summary_box.innerHTML = "<p>" +
tl['wiki_js_summary_unavailable'] + "</p>";
}
})
.catch(function() {
summary_box.innerHTML = "<p>" +
tl['wiki_js_summary_unavailable'] + "</p>";
});
} else {
summary_box.style.display = "none";
}
}