/** * @package Joomla.Component.Builder * * @created 30th April, 2015 * @author Llewellyn van der Merwe * @git 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; // 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', matchObject.checked ? 1 : 0); formData.append('whole_word', wholeObject.checked ? 1 : 0); formData.append('regex_search', regexObject.checked ? 1 : 0); let abort_this_search_values = false; // reset the progress bar searchProgressBarObject.style.width = '0%'; searchProgressBarObject.innerHTML = '0%'; // show the progress bar searchProgressObject.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 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'); } 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 = () => { // load question let question = Joomla.JText._('COM_COMPONENTBUILDER_YOUR_ARE_ABOUT_TO_REPLACE_BALLB_SEARCH_RESULTS') + '
' + Joomla.JText._('COM_COMPONENTBUILDER_THIS_CAN_NOT_BE_UNDONE_BYOU_HAVE_BEEN_WARNEDB') + '

' + 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(){ setSearch(replaceValue, searchValue, matchValue, wholeValue, regexValue, 2); }, function () { UIkit.modal.confirm(Joomla.JText._('COM_COMPONENTBUILDER_WOULD_YOU_LIKE_TO_REPEAT_THE_SAME_SEARCH'), function(){ onChange(); }, 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(){ onChange(); }, 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') + '.

' + 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'); table.clear().draw( true ); // hide the update all items buttonUpdateAllObject.style.display = '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 setSearch = async (search, replace = '', match = 0, whole = 0, regex = 0, mode = 1) => { // update the type of search if (mode == 1) { window.location.href = UrlSearch + '&search_value=' + search + '&type_search=1&match_case=' + match + '&whole_word=' + whole + '®ex_search=' + regex; } else if (mode == 2) { window.location.href = UrlSearch + '&search_value=' + search + '&replace_value=' + replace + '&type_search=2&match_case=' + match + '&whole_word=' + whole + '®ex_search=' + regex; } }; /** * 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) { buttonUpdateAllObject.style.display = ''; // TODO should only show once all items are loaded } else { buttonUpdateAllObject.style.display = 'none'; // TODO should only show once all items are loaded } }; /** * JS Function to execute (A) on search/replace text change , (B) on search options changes */ const onChange = () => { // 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) { // 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 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 & van Zonneveld' // example 2: htmlentities("foo'bar","ENT_QUOTES") // returns 2: 'foo'bar' const hashMap = getHtmlTranslationTable('HTML_ENTITIES', quoteStyle) string = string === null ? '' : string + '' if (!hashMap) { return false } if (quoteStyle && quoteStyle === 'ENT_QUOTES') { hashMap["'"] = ''' } 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: {'"': '"', '&': '&', '<': '<', '>': '>'} 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'] = '&' if (useTable === 'HTML_ENTITIES') { entities['160'] = ' ' entities['161'] = '¡' entities['162'] = '¢' entities['163'] = '£' entities['164'] = '¤' entities['165'] = '¥' entities['166'] = '¦' entities['167'] = '§' entities['168'] = '¨' entities['169'] = '©' entities['170'] = 'ª' entities['171'] = '«' entities['172'] = '¬' entities['173'] = '­' entities['174'] = '®' entities['175'] = '¯' entities['176'] = '°' entities['177'] = '±' entities['178'] = '²' entities['179'] = '³' entities['180'] = '´' entities['181'] = 'µ' entities['182'] = '¶' entities['183'] = '·' entities['184'] = '¸' entities['185'] = '¹' entities['186'] = 'º' entities['187'] = '»' entities['188'] = '¼' entities['189'] = '½' entities['190'] = '¾' entities['191'] = '¿' entities['192'] = 'À' entities['193'] = 'Á' entities['194'] = 'Â' entities['195'] = 'Ã' entities['196'] = 'Ä' entities['197'] = 'Å' entities['198'] = 'Æ' entities['199'] = 'Ç' entities['200'] = 'È' entities['201'] = 'É' entities['202'] = 'Ê' entities['203'] = 'Ë' entities['204'] = 'Ì' entities['205'] = 'Í' entities['206'] = 'Î' entities['207'] = 'Ï' entities['208'] = 'Ð' entities['209'] = 'Ñ' entities['210'] = 'Ò' entities['211'] = 'Ó' entities['212'] = 'Ô' entities['213'] = 'Õ' entities['214'] = 'Ö' entities['215'] = '×' entities['216'] = 'Ø' entities['217'] = 'Ù' entities['218'] = 'Ú' entities['219'] = 'Û' entities['220'] = 'Ü' entities['221'] = 'Ý' entities['222'] = 'Þ' entities['223'] = 'ß' entities['224'] = 'à' entities['225'] = 'á' entities['226'] = 'â' entities['227'] = 'ã' entities['228'] = 'ä' entities['229'] = 'å' entities['230'] = 'æ' entities['231'] = 'ç' entities['232'] = 'è' entities['233'] = 'é' entities['234'] = 'ê' entities['235'] = 'ë' entities['236'] = 'ì' entities['237'] = 'í' entities['238'] = 'î' entities['239'] = 'ï' entities['240'] = 'ð' entities['241'] = 'ñ' entities['242'] = 'ò' entities['243'] = 'ó' entities['244'] = 'ô' entities['245'] = 'õ' entities['246'] = 'ö' entities['247'] = '÷' entities['248'] = 'ø' entities['249'] = 'ù' entities['250'] = 'ú' entities['251'] = 'û' entities['252'] = 'ü' entities['253'] = 'ý' entities['254'] = 'þ' entities['255'] = 'ÿ' } if (useQuoteStyle !== 'ENT_NOQUOTES') { entities['34'] = '"' } if (useQuoteStyle === 'ENT_QUOTES') { entities['39'] = ''' } entities['60'] = '<' entities['62'] = '>' // ascii decimals to real symbols for (decimal in entities) { if (entities.hasOwnProperty(decimal)) { hashMap[String.fromCharCode(decimal)] = entities[decimal] } } return hashMap }