Component-Builder/admin/assets/js/search.js

946 lines
30 KiB
JavaScript

/**
* @package Joomla.Component.Builder
*
* @created 30th April, 2015
* @author Llewellyn van der Merwe <https://dev.vdm.io>
* @git Joomla Component Builder <https://git.vdm.dev/joomla/Component-Builder>
* @copyright Copyright (C) 2015 Vast Development Method. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
/* JS Document */
/**
* JS Function to execute the search
*/
const doSearch = async (signal, tables) => {
try {
// build form
const formData = new FormData();
// load the result table
const resultsTable = new DataTable('#search_results_table');
// get the search mode
let typeSearch = modeObject.querySelector('input[type=\'radio\']:checked').value;
// set some search values
let searchValue = searchObject.value;
let replaceValue = replaceObject.value;
let matchValue = matchObject.checked ? 1 : 0;
let wholeValue = wholeObject.checked ? 1 : 0;
let regexValue = regexObject.checked ? 1 : 0;
// add the form data
formData.append('table_name', '');
formData.append('type_search', typeSearch);
formData.append('search_value', searchValue);
formData.append('replace_value', replaceValue);
formData.append('match_case', matchValue);
formData.append('whole_word', wholeValue);
formData.append('regex_search', regexValue);
// update the URL
updateUrlQuery(searchValue, replaceValue, matchValue, wholeValue, regexValue, typeSearch);
let abort_this_search_values = false;
// reset the progress bar
searchProgressBarObject.style.width = '0%';
searchProgressBarObject.innerHTML = '0%';
// show the progress bar
searchProgressObject.style.display = '';
// hidde the search button
startSearchButton.style.display = 'none';
// show the stop search button
stopSearchButton.style.display = '';
// start search timer
startSearchTimer();
// reset our global counters
fieldCount = 0;
lineCount = 0;
// set our local counters
let total = 0;
let progress = tables.length;
let index;
for (index = 0; index < progress; index++) {
let tableName = tables[index];
// add the table name
formData.set('table_name', tableName);
let options = {
signal: signal,
method: 'POST', // *GET, POST, PUT, DELETE, etc.
body: formData
}
if (abort_this_search_values) {
break;
}
const response = await fetch(UrlAjax + 'doSearch', options).then(response => {
total++;
// return the json response
if (response.ok) {
return response.json();
} else {
UIkit.notify(Joomla.JText._('COM_COMPONENTBUILDER_THE_SEARCH_PROCESS_HAD_AN_ERROR_WITH_TABLE') + ' ' + tableName, {pos:'top-right', status:'danger'});
}
}).then((data) => {
if (typeof data.success !== 'undefined') {
UIkit.notify(data.success, {pos:'top-right', timeout : 200, status:'success'});
//} else if (typeof data.not_found !== 'undefined') {
// UIkit.notify(data.not_found, {pos:'bottom-right', timeout : 200});
}
if (typeof data.items !== 'undefined') {
addTableItems(resultsTable, data.items, typeSearch);
}
if (typeof data.fields_count !== 'undefined') {
fieldCount += data.fields_count;
}
if (typeof data.line_count !== 'undefined') {
lineCount += data.line_count;
}
// calculate the percent
let percent = 100.0 * (total / progress);
// update the progress bar
searchProgressObject.style.display = ''; // always make sure it still shows...
searchProgressBarObject.style.width = percent.toFixed(2) + '%';
searchProgressBarObject.innerHTML = percent.toFixed(2) + '%';
// when complete hide the progress bar
if (progress == total) {
let total_field_line = ' ' + fieldCount + ' ' + Joomla.JText._('COM_COMPONENTBUILDER_FIELDS_THAT_HAD') + ' ' + lineCount + ' ' + Joomla.JText._('COM_COMPONENTBUILDER_LINES') + ' ';
if (progress == 1) {
searchProgressBarObject.innerHTML = Joomla.JText._('COM_COMPONENTBUILDER_SEARCHING') + ' ' + tableName + total_field_line + Joomla.JText._('COM_COMPONENTBUILDER_AND_FINISHED_THE_SEARCH_IN') + ' ' + getSearchLenght() + ' ' + Joomla.JText._('COM_COMPONENTBUILDER_SECONDS');
} else {
searchProgressBarObject.innerHTML = Joomla.JText._('COM_COMPONENTBUILDER_SEARCHING') + ' ' + progress + ' ' + Joomla.JText._('COM_COMPONENTBUILDER_TABLES_WITH') + total_field_line + Joomla.JText._('COM_COMPONENTBUILDER_AND_FINISHED_THE_SEARCH_IN') + ' ' + getSearchLenght() + ' ' + Joomla.JText._('COM_COMPONENTBUILDER_SECONDS');
}
// show the search button
startSearchButton.style.display = '';
// hidde the stop search button
stopSearchButton.style.display = 'none';
setTimeout(function () {
// hide the progress bar again
searchProgressObject.style.display = 'none';
}, 13000);
}
}).catch(error => {
console.log(error);
if (error.name === "AbortError") {
abort_this_search_values = true;
}
});
}
} catch (error) {
console.log(error);
} finally {
// Executed regardless if we caught the error
}
};
/**
* JS Function to start search timer
*/
const startSearchTimer = () => {
startSearchTime = new Date();
};
/**
* JS Function to get search lenght
*/
const getSearchLenght = () => {
// set ending time
endSearchTime = new Date();
// get diff in ms
var timeDiff = endSearchTime - startSearchTime;
// strip the ms
timeDiff /= 1000;
// get seconds
return Math.round(timeDiff);
};
/**
* JS Function to fetch selected item
*/
const getSelectedItem = async (table, row, field, line) => {
try {
// get the search mode
let mode = modeObject.querySelector('input[type=\'radio\']:checked').value;
// build form
const formData = new FormData();
formData.append('field_name', field);
formData.append('row_id', row);
formData.append('table_name', table);
formData.append('search_value', searchObject.value);
formData.append('replace_value', replaceObject.value);
formData.append('match_case', matchObject.checked ? 1 : 0);
formData.append('whole_word', wholeObject.checked ? 1 : 0);
formData.append('regex_search', regexObject.checked ? 1 : 0);
// get search value
if (mode == 1) {
// calling URL
postURL = UrlAjax + 'getSearchValue';
} else {
// add the line value
formData.append('line_nr', line);
// calling URL
postURL = UrlAjax + 'getReplaceValue';
}
let options = {
method: 'POST', // *GET, POST, PUT, DELETE, etc.
body: formData
}
const response = await fetch(postURL, options).then(response => {
if (response.ok) {
return response.json();
}
}).then((data) => {
if (typeof data.success !== 'undefined') {
UIkit.notify(data.success, {pos:'top-right', status:'success'});
}
if (typeof data.value !== 'undefined') {
addSelectedItem(data.value, table, row, field, line);
}
}).catch(error => {
console.log(error);
});
} catch (error) {
console.log(error);
} finally {
// Executed regardless if we caught the error
}
};
/**
* JS Function to check if we should save/update the all current found items
*/
const replaceAllCheck = () => {
// get the current searc and replace values
let searchValue = searchObject.value;
let replaceValue = replaceObject.value;
// load question
let question = Joomla.JText._('COM_COMPONENTBUILDER_YOUR_ARE_ABOUT_TO_UPDATE_BALLB_VALUES_THAT_CAN_BE_FOUND_IN_THE_DATABASE') + '<br />' +
Joomla.JText._('COM_COMPONENTBUILDER_YOU_WILL_REPLACE') + ': [<span class="found_code">' + htmlentities(searchValue) + '</span>] ' +
Joomla.JText._('COM_COMPONENTBUILDER_WITH') + ': [<span class="found_code">' + htmlentities(replaceValue) + '</span>]<br />' +
Joomla.JText._('COM_COMPONENTBUILDER_THIS_CAN_NOT_BE_UNDONE_BYOU_HAVE_BEEN_WARNEDB') + '<br /><br />' +
Joomla.JText._('COM_COMPONENTBUILDER_ARE_YOU_THEREFORE_ABSOLUTELY_SURE_YOU_WANT_TO_CONTINUE');
// do check
UIkit.modal.confirm(question, function () {
// show the search settings again
showSearch();
// Create new controller and issue new request
controller_replace = new AbortController();
// check if any specific table was set
let tables = [];
let table = tableObject.value;
if (table != -1) {
tables.push(table);
replaceAll(controller_replace.signal, tables);
} else {
replaceAll(controller_replace.signal, searchTables);
}
}, {labels: { Ok: Joomla.JText._('COM_COMPONENTBUILDER_YES_UPDATE_ALL'), Cancel: Joomla.JText._('COM_COMPONENTBUILDER_NO') }});
};
/**
* JS Function to execute the search
*/
const replaceAll = async (signal, tables) => {
try {
// build form
const formData = new FormData();
// get the search mode
let typeSearch = modeObject.querySelector('input[type=\'radio\']:checked').value;
// set some search values
let searchValue = searchObject.value;
let replaceValue = replaceObject.value;
let matchValue = matchObject.checked ? 1 : 0;
let wholeValue = wholeObject.checked ? 1 : 0;
let regexValue = regexObject.checked ? 1 : 0;
// add the form data
formData.append('table_name', '');
formData.append('type_search', typeSearch);
formData.append('search_value', searchValue);
formData.append('replace_value', replaceValue);
formData.append('match_case', matchValue);
formData.append('whole_word', wholeValue);
formData.append('regex_search', regexValue);
// reset the progress bar
replaceProgressBarObject.style.width = '0%';
// show the progress bar
replaceProgressObject.style.display = '';
let abort_this_replace_values = false;
let total = 0;
let progress = tables.length;
let index;
for (index = 0; index < progress; index++) {
let tableName = tables[index];
// add the table name
formData.set('table_name', tableName);
let options = {
signal: signal,
method: 'POST', // *GET, POST, PUT, DELETE, etc.
body: formData
}
if (abort_this_replace_values) {
break;
}
const response = await fetch(UrlAjax + 'replaceAll', options).then(response => {
total++;
if (response.ok) {
return response.json();
} else {
UIkit.notify(Joomla.JText._('COM_COMPONENTBUILDER_THE_REPLACE_PROCESS_HAD_AN_ERROR_WITH_TABLE') + ' ' + tableName, {pos:'top-right', status:'danger'});
}
}).then((data) => {
if (typeof data.success !== 'undefined') {
UIkit.notify(data.success, {pos:'top-right', timeout : 200, status:'success'});
} else if (typeof data.error !== 'undefined') {
UIkit.notify(data.error, {pos:'bottom-right', timeout : 200});
}
// calculate the percent
let percent = 100.0 * (total / progress);
// update the progress bar
replaceProgressBarObject.style.width = percent.toFixed(2) + '%';
// when complete hide the progress bar
if (progress == total) {
setTimeout(function () {
// hide the progress bar again
replaceProgressObject.style.display = 'none';
// we clear the table again
clearAll();
// if not reqex we reverse the search for you so you can see the update was a success
if (regexValue == 0) {
// set the replace value as the search value
UIkit.modal.confirm(Joomla.JText._('COM_COMPONENTBUILDER_WOULD_YOU_LIKE_TO_DO_A_REVERSE_SEARCH'), function(){
startNewSearch(replaceValue, searchValue, matchValue, wholeValue, regexValue, 2);
}, function () {
UIkit.modal.confirm(Joomla.JText._('COM_COMPONENTBUILDER_WOULD_YOU_LIKE_TO_REPEAT_THE_SAME_SEARCH'), function(){
startSearch();
}, function () {
clearSearch();
}, {labels: { Ok: Joomla.JText._('COM_COMPONENTBUILDER_YES'), Cancel: Joomla.JText._('COM_COMPONENTBUILDER_NO') }});
}, {labels: { Ok: Joomla.JText._('COM_COMPONENTBUILDER_YES'), Cancel: Joomla.JText._('COM_COMPONENTBUILDER_NO') }});
} else {
// else we search it again just to prove its changed
UIkit.modal.confirm(Joomla.JText._('COM_COMPONENTBUILDER_WOULD_YOU_LIKE_TO_REPEAT_THE_SAME_SEARCH'), function(){
startSearch();
}, function () {
clearSearch();
}, {labels: { Ok: Joomla.JText._('COM_COMPONENTBUILDER_YES'), Cancel: Joomla.JText._('COM_COMPONENTBUILDER_NO') }});
}
}, 3000);
}
}).catch(error => {
console.log(error);
if (error.name === "AbortError") {
abort_this_replace_values = true;
}
});
}
} catch (error) {
console.log(error);
} finally {
// Executed regardless if we caught the error
}
};
/**
* JS Function to check if we should save/update the current selected item
*/
const setValueCheck = (row, field, table) => {
// load question
let question = Joomla.JText._('COM_COMPONENTBUILDER_YOUR_ARE_ABOUT_TO_UPDATE_ROW') + ' (' + row + ') -> (' + field + ') ' +
Joomla.JText._('COM_COMPONENTBUILDER_FIELD_IN_THE') + ' (' + table + ') ' + Joomla.JText._('COM_COMPONENTBUILDER_TABLE') + '.<br /><br />' +
Joomla.JText._('COM_COMPONENTBUILDER_THIS_CAN_NOT_BE_UNDONE_ARE_YOU_SURE_YOU_WANT_TO_CONTINUE');
// do check
UIkit.modal.confirm(question, function () {
setValue(row, field, table);
}, {labels: { Ok: Joomla.JText._('COM_COMPONENTBUILDER_YES'), Cancel: Joomla.JText._('COM_COMPONENTBUILDER_NO') }});
};
/**
* JS Function to set the current selected item
*/
const setValue = async (row, field, table) => {
try {
// get the value from the editor
let value = editorObject.getValue();
// build form
const formData = new FormData();
formData.append('value', value);
formData.append('row_id', row);
formData.append('field_name', field);
formData.append('table_name', table);
let options = {
method: 'POST', // *GET, POST, PUT, DELETE, etc.
body: formData
}
const response = await fetch(UrlAjax + 'setValue', options).then(response => {
if (response.ok) {
return response.json();
}
}).then((data) => {
if (typeof data.success !== 'undefined') {
UIkit.notify(data.success, {pos:'top-right', status:'success'});
clearSelectedItem();
tableActiveObject.remove().draw();
}
}).catch(error => {
console.log(error);
});
} catch (error) {
console.log(error);
} finally {
// Executed regardless if we caught the error
}
};
/**
* JS Function to add item to the editor
*/
const addSelectedItem = async (value, table, row, field, line) => {
// display area
if (value.length > 1)
{
// add value to editor
editorObject.setValue(value);
// set item details notice area
itemNoticeObject.style.display = '';
itemEditButtonObject.innerHTML = editButtonSelected;
itemTableNameObject.innerHTML = table;
itemRowIdObject.innerHTML = row;
itemFieldNameObject.innerHTML = field;
itemLineNumberObject.innerHTML = line;
// set button and editor line if we have a line number
if (typeof line == 'number') {
// show and set the save button
buttonUpdateItemObject.style.display = '';
buttonUpdateItemObject.setAttribute('onclick',"setValueCheck(" + row + ", '" + field + "', '" + table + "');");
// get top of the code line
let top = editorObject.charCoords({line: line, ch: 0}, "local").top;
// scroll to the line
editorObject.scrollTo(null, top - 12);
// select the line
editorObject.setCursor(line - 1);
} else {
// no line so no data we can't save this data
buttonUpdateItemObject.setAttribute('onclick', "");
buttonUpdateItemObject.style.display = 'none';
}
}
};
/**
* JS Function to clear item from the editor and hide it
*/
const clearSelectedItem = async () => {
// display area
editorObject.setValue('');
// clear notice area
itemNoticeObject.style.display = 'none';
itemEditButtonObject.innerHTML = '...';
itemTableNameObject.innerHTML = '...';
itemRowIdObject.innerHTML = '...';
itemFieldNameObject.innerHTML = '...';
itemLineNumberObject.innerHTML = '...';
// clear update button
buttonUpdateItemObject.setAttribute('onclick', '');
};
/**
* JS Function to clear table items
*/
const clearTableItems = async () => {
let table = new DataTable('#search_results_table');
// clear search
table.search('').columns().search( '' );
// clear items
table.clear().draw( true );
// hide the update all items
buttonUpdateAllStyleDisplay('none');
};
/**
* JS Function to clear all details of the search
*/
const clearAll = async () => {
// clear all details
clearTableItems();
clearSelectedItem();
searchedObject.innerHTML = '....';
};
/**
* JS Function to clear the search and replace values
*/
const clearSearch = async () => {
// clear the search and replace values
searchObject.value = '';
replaceObject.value = '';
};
/**
* JS Function to set the search and replace values
*/
const startNewSearch = async (search, replace = '', match = 0, whole = 0, regex = 0, mode = 1) => {
// redirect to a new search
window.location.href = getSearchURL(search, replace, match, whole, regex, mode);
};
/**
* JS Function to update the URL of the browser with the search query
*/
const updateUrlQuery = async (search, replace = '', match = 0, whole = 0, regex = 0, mode = 1) => {
// update the url query
window.history.pushState({}, '', getSearchURL(search, replace, match, whole, regex, mode));
};
/**
* JS Function to get the current search URL
*/
const getSearchURL = (search, replace = '', match = 0, whole = 0, regex = 0, mode = 1) => {
// check if its a single table search
let table = tableObject.value;
let table_name = '';
if (table != -1) {
table_name = '&table_name=' + urlencode(table);
}
// update the type of search
if (mode == 1) {
return UrlSearch + table_name +
'&search_value=' + urlencode(search) +
'&type_search=1&match_case=' + match +
'&whole_word=' + whole +
'&regex_search=' + regex;
} else if (mode == 2) {
return UrlSearch + table_name +
'&search_value=' + urlencode(search) +
'&replace_value=' + urlencode(replace) +
'&type_search=2&match_case=' + match +
'&whole_word=' + whole +
'&regex_search=' + regex;
}
return UrlSearch + table_name;
};
/**
* JS Function to check if a element has a class
*/
const hasClass = (elementObject, classNaam) => {
return !!elementObject.className.match(new RegExp('(\\s|^)' + classNaam + '(\\s|$)'));
};
/**
* JS Function add a class from an element
*/
const addClass = (elementObject, classNaam) => {
if (!hasClass(elementObject, classNaam)) {
elementObject.className += " " + classNaam;
}
};
/**
* JS Function remove a class from an element
*/
const removeClass = (elementObject, classNaam) => {
if (hasClass(elementObject, classNaam)) {
var reg = new RegExp('(\\s|^)' + classNaam + '(\\s|$)');
elementObject.className = elementObject.className.replace(reg, ' ');
}
};
/**
* JS Function to add items to the table
*/
const addTableItems = async (table, items, typeSearch) => {
table.rows.add(items).draw( false );
if (typeSearch == 2) {
buttonUpdateAllStyleDisplay(''); // TODO should only show once all items are loaded
} else {
buttonUpdateAllStyleDisplay('none'); // TODO should only show once all items are loaded
}
};
/**
* JS Function to update the update all button
*/
const buttonUpdateAllStyleDisplay = async (value) => {
buttonUpdateAllObject.forEach((buttonObject) => {
buttonObject.style.display = value;
});
};
/**
* JS Function to execute (A) on search/replace text change , (B) on search options changes
*/
const startSearch = (field, forced = false) => {
// get replace value if set
const replaceValue = replaceObject.value;
if (replaceValue.length > 0) {
// set the searched value
replacedObject.innerHTML = htmlentities(replaceValue);
} else {
replacedObject.innerHTML = '';
}
// get search value
const searchValue = searchObject.value;
if (searchValue.length > 2 || (searchValue.length > 0 && forced)) {
// Cancel any ongoing requests
if (controller) controller.abort();
// we clear the table again
clearAll();
// set the searched value
searchedObject.innerHTML = htmlentities(searchValue);
// Create new controller and issue new request
controller = new AbortController();
// check if any specific table was set
let tables = [];
let table = tableObject.value;
if (table != -1) {
tables.push(table);
doSearch(controller.signal, tables);
} else {
doSearch(controller.signal, searchTables);
}
} else {
// Clear the table
clearAll();
}
};
/**
* JS Function to stop a search
*/
const stopSearch = () => {
// Cancel any ongoing requests
if (controller) controller.abort();
// show the search button
startSearchButton.style.display = '';
// hidde the stop search button
stopSearchButton.style.display = 'none';
// remove the progress bar at some point
setTimeout(function () {
// hide the progress bar again
searchProgressObject.style.display = 'none';
}, 13000);
}
/**
* JS Function to hide search settings and show table search
*/
const showSearch = () => {
searchSettingsObject.style.display = '';
searchDetailsObject.style.display = 'none';
replaceDetailsObject.style.display = 'none';
tableSearchObject.style.display = 'none';
tableLengthObject.style.display = 'none';
};
/**
* JS Function to show search settings and hide table search
*/
const hideSearch = () => {
searchSettingsObject.style.display = 'none';
searchDetailsObject.style.display = '';
tableSearchObject.style.display = '';
tableLengthObject.style.display = '';
// check if we are in replace mode
let mode = modeObject.querySelector('input[type=\'radio\']:checked').value;
if (mode == 2) {
replaceDetailsObject.style.display = '';
}
};
function htmlentities(string, quoteStyle, charset, doubleEncode) {
// discuss at: https://locutus.io/php/htmlentities/
// original by: Kevin van Zonneveld (https://kvz.io)
// revised by: Kevin van Zonneveld (https://kvz.io)
// revised by: Kevin van Zonneveld (https://kvz.io)
// improved by: nobbler
// improved by: Jack
// improved by: Rafał Kukawski (https://blog.kukawski.pl)
// improved by: Dj (https://locutus.io/php/htmlentities:425#comment_134018)
// bugfixed by: Onno Marsman (https://twitter.com/onnomarsman)
// bugfixed by: Brett Zamir (https://brett-zamir.me)
// input by: Ratheous
// note 1: function is compatible with PHP 5.2 and older
// example 1: htmlentities('Kevin & van Zonneveld')
// returns 1: 'Kevin &amp; van Zonneveld'
// example 2: htmlentities("foo'bar","ENT_QUOTES")
// returns 2: 'foo&#039;bar'
const hashMap = getHtmlTranslationTable('HTML_ENTITIES', quoteStyle)
string = string === null ? '' : string + ''
if (!hashMap) {
return false
}
if (quoteStyle && quoteStyle === 'ENT_QUOTES') {
hashMap["'"] = '&#039;'
}
doubleEncode = doubleEncode === null || !!doubleEncode
const regex = new RegExp('&(?:#\\d+|#x[\\da-f]+|[a-zA-Z][\\da-z]*);|[' +
Object.keys(hashMap)
.join('')
// replace regexp special chars
.replace(/([()[\]{}\-.*+?^$|/\\])/g, '\\$1') + ']',
'g')
return string.replace(regex, function (ent) {
if (ent.length > 1) {
return doubleEncode ? hashMap['&'] + ent.substr(1) : ent
}
return hashMap[ent]
})
}
function getHtmlTranslationTable(table, quoteStyle) { // eslint-disable-line camelcase
// discuss at: https://locutus.io/php/get_html_translation_table/
// original by: Philip Peterson
// revised by: Kevin van Zonneveld (https://kvz.io)
// bugfixed by: noname
// bugfixed by: Alex
// bugfixed by: Marco
// bugfixed by: madipta
// bugfixed by: Brett Zamir (https://brett-zamir.me)
// bugfixed by: T.Wild
// improved by: KELAN
// improved by: Brett Zamir (https://brett-zamir.me)
// input by: Frank Forte
// input by: Ratheous
// note 1: It has been decided that we're not going to add global
// note 1: dependencies to Locutus, meaning the constants are not
// note 1: real constants, but strings instead. Integers are also supported if someone
// note 1: chooses to create the constants themselves.
// example 1: get_html_translation_table('HTML_SPECIALCHARS')
// returns 1: {'"': '&quot;', '&': '&amp;', '<': '&lt;', '>': '&gt;'}
const entities = {}
const hashMap = {}
let decimal
const constMappingTable = {}
const constMappingQuoteStyle = {}
let useTable = {}
let useQuoteStyle = {}
// Translate arguments
constMappingTable[0] = 'HTML_SPECIALCHARS'
constMappingTable[1] = 'HTML_ENTITIES'
constMappingQuoteStyle[0] = 'ENT_NOQUOTES'
constMappingQuoteStyle[2] = 'ENT_COMPAT'
constMappingQuoteStyle[3] = 'ENT_QUOTES'
useTable = !isNaN(table)
? constMappingTable[table]
: table
? table.toUpperCase()
: 'HTML_SPECIALCHARS'
useQuoteStyle = !isNaN(quoteStyle)
? constMappingQuoteStyle[quoteStyle]
: quoteStyle
? quoteStyle.toUpperCase()
: 'ENT_COMPAT'
if (useTable !== 'HTML_SPECIALCHARS' && useTable !== 'HTML_ENTITIES') {
throw new Error('Table: ' + useTable + ' not supported')
}
entities['38'] = '&amp;'
if (useTable === 'HTML_ENTITIES') {
entities['160'] = '&nbsp;'
entities['161'] = '&iexcl;'
entities['162'] = '&cent;'
entities['163'] = '&pound;'
entities['164'] = '&curren;'
entities['165'] = '&yen;'
entities['166'] = '&brvbar;'
entities['167'] = '&sect;'
entities['168'] = '&uml;'
entities['169'] = '&copy;'
entities['170'] = '&ordf;'
entities['171'] = '&laquo;'
entities['172'] = '&not;'
entities['173'] = '&shy;'
entities['174'] = '&reg;'
entities['175'] = '&macr;'
entities['176'] = '&deg;'
entities['177'] = '&plusmn;'
entities['178'] = '&sup2;'
entities['179'] = '&sup3;'
entities['180'] = '&acute;'
entities['181'] = '&micro;'
entities['182'] = '&para;'
entities['183'] = '&middot;'
entities['184'] = '&cedil;'
entities['185'] = '&sup1;'
entities['186'] = '&ordm;'
entities['187'] = '&raquo;'
entities['188'] = '&frac14;'
entities['189'] = '&frac12;'
entities['190'] = '&frac34;'
entities['191'] = '&iquest;'
entities['192'] = '&Agrave;'
entities['193'] = '&Aacute;'
entities['194'] = '&Acirc;'
entities['195'] = '&Atilde;'
entities['196'] = '&Auml;'
entities['197'] = '&Aring;'
entities['198'] = '&AElig;'
entities['199'] = '&Ccedil;'
entities['200'] = '&Egrave;'
entities['201'] = '&Eacute;'
entities['202'] = '&Ecirc;'
entities['203'] = '&Euml;'
entities['204'] = '&Igrave;'
entities['205'] = '&Iacute;'
entities['206'] = '&Icirc;'
entities['207'] = '&Iuml;'
entities['208'] = '&ETH;'
entities['209'] = '&Ntilde;'
entities['210'] = '&Ograve;'
entities['211'] = '&Oacute;'
entities['212'] = '&Ocirc;'
entities['213'] = '&Otilde;'
entities['214'] = '&Ouml;'
entities['215'] = '&times;'
entities['216'] = '&Oslash;'
entities['217'] = '&Ugrave;'
entities['218'] = '&Uacute;'
entities['219'] = '&Ucirc;'
entities['220'] = '&Uuml;'
entities['221'] = '&Yacute;'
entities['222'] = '&THORN;'
entities['223'] = '&szlig;'
entities['224'] = '&agrave;'
entities['225'] = '&aacute;'
entities['226'] = '&acirc;'
entities['227'] = '&atilde;'
entities['228'] = '&auml;'
entities['229'] = '&aring;'
entities['230'] = '&aelig;'
entities['231'] = '&ccedil;'
entities['232'] = '&egrave;'
entities['233'] = '&eacute;'
entities['234'] = '&ecirc;'
entities['235'] = '&euml;'
entities['236'] = '&igrave;'
entities['237'] = '&iacute;'
entities['238'] = '&icirc;'
entities['239'] = '&iuml;'
entities['240'] = '&eth;'
entities['241'] = '&ntilde;'
entities['242'] = '&ograve;'
entities['243'] = '&oacute;'
entities['244'] = '&ocirc;'
entities['245'] = '&otilde;'
entities['246'] = '&ouml;'
entities['247'] = '&divide;'
entities['248'] = '&oslash;'
entities['249'] = '&ugrave;'
entities['250'] = '&uacute;'
entities['251'] = '&ucirc;'
entities['252'] = '&uuml;'
entities['253'] = '&yacute;'
entities['254'] = '&thorn;'
entities['255'] = '&yuml;'
}
if (useQuoteStyle !== 'ENT_NOQUOTES') {
entities['34'] = '&quot;'
}
if (useQuoteStyle === 'ENT_QUOTES') {
entities['39'] = '&#39;'
}
entities['60'] = '&lt;'
entities['62'] = '&gt;'
// ascii decimals to real symbols
for (decimal in entities) {
if (entities.hasOwnProperty(decimal)) {
hashMap[String.fromCharCode(decimal)] = entities[decimal]
}
}
return hashMap
}
function urlencode (str) {
// discuss at: https://locutus.io/php/urlencode/
// original by: Philip Peterson
// improved by: Kevin van Zonneveld (https://kvz.io)
// improved by: Kevin van Zonneveld (https://kvz.io)
// improved by: Brett Zamir (https://brett-zamir.me)
// improved by: Lars Fischer
// improved by: Waldo Malqui Silva (https://fayr.us/waldo/)
// input by: AJ
// input by: travc
// input by: Brett Zamir (https://brett-zamir.me)
// input by: Ratheous
// bugfixed by: Kevin van Zonneveld (https://kvz.io)
// bugfixed by: Kevin van Zonneveld (https://kvz.io)
// bugfixed by: Joris
// reimplemented by: Brett Zamir (https://brett-zamir.me)
// reimplemented by: Brett Zamir (https://brett-zamir.me)
// note 1: This reflects PHP 5.3/6.0+ behavior
// note 1: Please be aware that this function
// note 1: expects to encode into UTF-8 encoded strings, as found on
// note 1: pages served as UTF-8
// example 1: urlencode('Kevin van Zonneveld!')
// returns 1: 'Kevin+van+Zonneveld%21'
// example 2: urlencode('https://kvz.io/')
// returns 2: 'https%3A%2F%2Fkvz.io%2F'
// example 3: urlencode('https://www.google.nl/search?q=Locutus&ie=utf-8')
// returns 3: 'https%3A%2F%2Fwww.google.nl%2Fsearch%3Fq%3DLocutus%26ie%3Dutf-8'
str = (str + '')
return encodeURIComponent(str)
.replace(/!/g, '%21')
.replace(/'/g, '%27')
.replace(/\(/g, '%28')
.replace(/\)/g, '%29')
.replace(/\*/g, '%2A')
.replace(/~/g, '%7E')
.replace(/%20/g, '+')
}