@ -14,38 +14,36 @@
* /
const doSearch = async ( signal , tables ) => {
try {
let searchValue = document . querySelector ( 'input[name="search_value"]' ) . value ;
let replaceValue = document . querySelector ( 'input[name="replace_value"]' ) . value ;
// build form
const formData = new FormData ( ) ;
// load the result table
const resultsTable = new DataTable ( '#search_results_table' ) ;
// set some search values
let searchValue = searchObject . value ;
let replaceValue = replaceObject . value ;
// add the form data
formData . append ( 'table_name' , '' ) ;
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 ) ;
// Display 'loading' message in search results message div
document . getElementById ( 'search-mssg-box' ) . innerHTML =
'<progress id="search-loading-progressbar" class="uk-progress" value="10" max="100" style="width: 200px; display: inline-block; margin: 0 !important;"></progress>' +
' <div id="search-loading-spinner" style="margin: -4px 12px 0;" uk-spinner="ratio: 0.8"></div>' +
' <span id="search-loading-percent">0%</span> ' +
'Loading for search text: <b style="font-size: 2em;">' + searchValue + '</b>'
;
// Clear results table
let search _loading _percent = document . getElementById ( 'search-loading-percent' ) ;
let tbl _obj _body = document . getElementById ( 'search-results-tbl-tbody' ) ;
tbl _obj _body . innerHTML = '' ;
let abort _this _search _value = false ;
let total = 0 ;
let index ;
for ( index = 0 ; index < searchTables . length ; index ++ ) {
const formData = new FormData ( ) ;
let tableName = searchTables [ index ] ;
for ( index = 0 ; index < tables . length ; index ++ ) {
formData . append ( 'table_name' , '' ) ;
formData . append ( 'search_value' , searchValue ) ;
formData . append ( 'replace_value' , replaceValue ) ;
formData . append ( 'match_case' , document . querySelector ( 'input[name="match_case"]' ) . checked ? 1 : 0 ) ;
formData . append ( 'whole_word' , document . querySelector ( 'input[name="whole_word"]' ) . checked ? 1 : 0 ) ;
formData . append ( 'regex_search' , document . querySelector ( 'input[name="regex_search"]' ) . checked ? 1 : 0 ) ;
formData . append ( 'table_name' , tableName ) ;
let tableName = tables [ index ] ;
// add the table name
formData . set ( 'table_name' , tableName ) ;
let url = document . getElementById ( 'adminForm' ) . getAttribute ( 'action' ) + '&layout=dosearch' ;
let options = {
signal : signal ,
method : 'POST' , // *GET, POST, PUT, DELETE, etc.
@ -56,71 +54,19 @@ const doSearch = async (signal, tables) => {
console . log ( 'Aborting this searchValue:' + searchValue ) ;
break ;
}
console . log ( total + ' -- SEARCHING: ' + searchValue + ' @[' + tableName + ']' ) ;
const response = await fetch ( url , options )
// Note: response.text() is a promise ...
. then ( response => {
total ++ ;
//console.log(total + ' ' + sTables.length);
if ( sTables . length == total ) setTimeout ( function ( ) {
document . getElementById ( 'search-mssg-box' ) . innerHTML = '<div class="alert alert-success" role="alert"><strong>Enter</strong> your text.</div>' ;
} , 200 ) ;
response . text ( ) . then ( data => {
console . log ( '++ Fetched for ' + searchValue + ' [' + tableName + ']' ) ;
let percent = 100.0 * ( total / sTables . length ) ;
search _loading _percent . innerHTML = '' + percent . toFixed ( 2 ) + '%' ;
document . getElementById ( 'search-loading-progressbar' ) . value = percent ;
let use _json = false , json _data = false , items = false ;
if ( use _json ) {
try {
json _data = data ? JSON . parse ( data ) : false ;
items = json _data ? json _data . items : false ;
} catch ( error ) {
console . log ( error ) ;
}
}
// Very fast and low memory display HTML table row prepared server-side via PHP instead of JS !!
if ( ! json _data ) {
tbl _obj _body . innerHTML = tbl _obj _body . innerHTML + data ;
}
// Very slow fast and very high memory: Display HTML table rows by creating them now in browser (client-side) via JS instead of using PHP
if ( json _data && items ) {
let table _rows = '' ;
for ( const [ row _num , row _field _vals ] of Object . entries ( items ) ) {
for ( const [ fname , fvals ] of Object . entries ( row _field _vals ) ) {
for ( const [ line , fval ] of Object . entries ( fvals ) ) {
let lnk = 'getFSText(this, \'' + tableName + '\', ' + row _num + ', \'' + fname + '\', line)' ;
let val = fval ;
val = val . replaceAll ( marker _start , '<b>' ) ;
val = val . replaceAll ( marker _end , '</b>' ) ;
table _rows = table _rows + '<tr onclick="' + lnk + '; return false" style="cursor: pointer;">' +
'<td>' + val + '</td>' +
'<td>' + tableName + '</td>' +
'<td>' + fname + '</td>' +
'<td>' + row _num + '</td>' +
'<td>' + line + '</td>' +
'</tr>' ;
}
}
}
tbl _obj _body . innerHTML = tbl _obj _body . innerHTML + table _rows ;
} // END IF json_data && items
} ) ;
} )
. catch ( error => {
total ++ ;
if ( sTables . length == total ) document . getElementById ( 'search-mssg-box' ) . innerHTML = '<div class="alert alert-success" role="alert"><strong>Enter</strong> your text.</div>' ;
// Stop further searches for this search value
if ( error . name === "AbortError" ) abort _this _search _value = true ;
error . name === "AbortError"
? console . log ( " ... ABORTED fetch() for: " + searchValue )
: console . log ( error . toString ( ) ) ;
} ) ;
const response = await fetch ( Url + 'doSearch' , options ) . then ( response => {
total ++ ;
if ( response . ok ) {
return response . json ( ) ;
}
} ) . then ( ( data ) => {
if ( typeof data . items !== 'undefined' ) {
console . log ( '++ Fetched for ' + searchValue + ' [' + tableName + ']' ) ;
addTableItems ( resultsTable , data . items ) ;
}
} ) . catch ( error => {
console . log ( error ) ;
} ) ;
}
} catch ( error ) {
console . log ( error ) ;
@ -130,183 +76,133 @@ const doSearch = async (signal, tables) => {
} ;
/ * *
* JS Function to execute the search
* JS Function to fetch selected item
* /
const getFSText = async ( el , table _name , row _id , field _name , line ) => {
let sibling = el . parentNode . firstElementChild ;
do {
sibling != el
? sibling . classList . remove ( 'active' )
: sibling . classList . add ( 'active' ) ;
} while ( sibling = sibling . nextElementSibling ) ;
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 ( 'table_name' , table _name ) ;
formData . append ( 'get_full_search_text' , 1 ) ;
formData . append ( 'row_id' , row _id ) ;
formData . append ( 'field_name' , field _name ) ;
// get search value
if ( mode == 1 ) {
formData . append ( 'field_name' , field ) ;
formData . append ( 'row_id' , row ) ;
formData . append ( 'table_name' , table ) ;
// calling URL
getURL = Url + 'getSearchValue' ;
} else {
formData . append ( 'field_name' , field ) ;
formData . append ( 'row_id' , row ) ;
formData . append ( 'line_nr' , line ) ;
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 ) ;
// calling URL
getURL = Url + 'getReplaceValue' ;
}
//let url = `https://jsonplaceholder.typicode.com/posts/${searchValue}`,
let url = document . getElementById ( 'adminForm' ) . getAttribute ( 'action' ) + '&layout=dosearch' ;
let options = {
method : 'POST' , // *GET, POST, PUT, DELETE, etc.
mode : 'cors' , // no-cors, *cors, same-origin
cache : 'no-cache' , // *default, no-cache, reload, force-cache, only-if-cached
credentials : 'same-origin' , // include, *same-origin, omit
/ *
headers : {
'Accept' : 'application/json' ,
'Content-Type' : 'application/json' , //'application/x-www-form-urlencoded',
} , * /
redirect : 'follow' , // manual, *follow, error
referrerPolicy : 'no-referrer' , // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
// body: JSON.stringify(formData) // body data type must match "Content-Type" header
body : formData
}
// Clear full text box
document . getElementById ( 'match-full-text-box' ) . innerHTML = 'Loading ...' ;
const response = await fetch ( url , options )
// Note: response.text() is a promise ...
. then ( response => {
response . text ( ) . then ( data => {
console . log ( "Fetched full text for row: " + row _id + ' field name: ' + field _name + ' for Table: ' + table _name ) ;
document . getElementById ( 'match-full-text-box' ) . innerHTML = '<div id="match-full-text-header">'
+ table _name + ' @ ' + field _name + ': ' + row _id + '</div><textarea id="match-full-text">' + data + '</textarea>' ;
document . getElementById ( 'match-full-text-box' ) . style . display = '' ;
attachCodeMirror ( jQuery ( '#match-full-text' ) , null ) ;
cm _toggle _fully _searchable ( 'match-full-text' , 1 ) ;
cm _jumpToLine ( 'match-full-text' , line ) ;
//cm_toggle_plain_textarea('match-full-text-box', 1);
} ) ;
} )
. catch ( error => {
// Stop further searches for this search value
if ( error . name === "AbortError" ) abort _this _search _value = true ;
error . name === "AbortError"
? console . log ( " ... ABORTED fetch() for: " + searchValue )
: console . log ( error . toString ( ) ) ;
} ) ;
const response = await fetch ( getURL , options ) . then ( response => {
if ( response . ok ) {
return response . json ( ) ;
}
} ) . then ( ( data ) => {
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
}
} ;
const cm _jumpToLine = function ( tag _id , row )
{
console . log ( '#' + tag _id ) ;
let codeMirrorEl = jQuery ( '#' + tag _id ) . next ( '.CodeMirror' ) ;
if ( ! codeMirrorEl . length ) return ;
let codeMirrorRef = jQuery ( '#' + tag _id ) . next ( '.CodeMirror' ) . get ( 0 ) . CodeMirror ;
let t = codeMirrorRef . charCoords ( { line : row , ch : 0 } , "local" ) . top ;
let middleHeight = codeMirrorRef . getScrollerElement ( ) . offsetHeight / 2 ;
codeMirrorRef . scrollTo ( null , t - middleHeight - 5 ) ;
}
/* Attach CodeMirror with optional settings */
const cm _toggle _fully _searchable = function ( el , toggle )
{
if ( typeof CodeMirror === 'undefined' ) { alert ( 'CodeMirror not loaded' ) ; return ; }
jQuery ( [ el ] ) . each ( function ( i , tag _id ) {
let codeMirrorEl = jQuery ( '#' + tag _id ) . next ( '.CodeMirror' ) ;
if ( ! codeMirrorEl . length ) return ;
let codeMirrorRef = jQuery ( '#' + tag _id ) . next ( '.CodeMirror' ) . get ( 0 ) . CodeMirror ;
codeMirrorRef . setOption ( 'viewportMargin' , toggle ? '9999' : '' ) ;
} ) ;
/ * *
* JS Function to add item to the editor
* /
const addSelectedItem = async ( value , table , row , field , line ) => {
// display area
if ( value . length > 1 )
{
editorObject . setValue ( value ) ;
editorNoticeObject . innerHTML = 'Table: <b>' + table + '</b>(id:<b>' + row + '</b>) | Field: <b>' + field + '</b>(line:<b>' + line + '</b>)' ;
}
}
const cm _toggle _plain _textarea = function ( el , toggle )
{
if ( typeof CodeMirror === 'undefined' ) { alert ( 'CodeMirror not loaded' ) ; return ; }
jQuery ( [ el ] ) . each ( function ( i , tag _id ) {
let codeMirrorEl = jQuery ( '#' + tag _id ) . next ( '.CodeMirror' ) ;
if ( toggle ) {
let options = jQuery ( '#' + tag _id ) . data ( 'options' ) ;
options . viewportMargin = jQuery ( '#cm_toggle_fully_searchable_btn input' ) . prop ( 'checked' ) ? '9999' : '' ;
jQuery ( '#' + tag _id ) . prev ( 'p' ) . show ( ) ;
attachCodeMirror ( jQuery ( '#' + tag _id ) , options ) ;
}
else if ( codeMirrorEl . length )
{
let codeMirrorRef = codeMirrorEl . get ( 0 ) . CodeMirror ;
jQuery ( '#' + tag _id ) . prev ( 'p' ) . hide ( ) ;
jQuery ( '#' + tag _id ) . css ( { width : '100%' , height : '400px' } ) ;
codeMirrorRef . toTextArea ( ) ;
// Remove codemirror container in case that removing it failed
if ( jQuery ( '#' + tag _id ) . next ( '.CodeMirror' ) . length ) {
jQuery ( '#' + tag _id ) . next ( '.CodeMirror' ) . remove ( ) ;
}
}
} ) ;
/ * *
* JS Function to clear item from the editor and hide it
* /
const clearSelectedItem = async ( ) => {
// display area
editorObject . setValue ( '' ) ;
editorNoticeObject . innerHTML = '' ;
}
const attachCodeMirror = function ( txtareas , CMoptions , mode )
{
CMoptions = typeof CMoptions !== 'undefined' && CMoptions ? CMoptions : {
mode : mode || 'application/x-httpd-php' ,
indentUnit : 2 ,
lineNumbers : true ,
matchBrackets : true ,
lineWrapping : true ,
onCursorActivity : function ( CM )
{
CM . setLineClass ( hlLine , null ) ;
hlLine = CM . setLineClass ( CM . getCursor ( ) . line , 'activeline' ) ;
}
} ;
/ * *
* JS Function to clear table items
* /
const clearTableItems = async ( ) => {
let table = new DataTable ( '#search_results_table' ) ;
table . clear ( ) . draw ( true ) ;
}
var editor , theArea ;
txtareas . each ( function ( i , txtarea )
{
theArea = jQuery ( txtarea ) ;
theArea . removeClass ( ) ; // Remove all classes from the textarea
editor = CodeMirror . fromTextArea ( theArea . get ( 0 ) , CMoptions ) ;
editor . refresh ( ) ;
} ) ;
/ * *
* JS Function to clear all details of the search
* /
const clearAll = async ( ) => {
// clear all details
clearTableItems ( ) ;
clearSelectedItem ( ) ;
}
return txtareas . length == 1 ? editor : true ;
/ * *
* JS Function to add items to the table
* /
const addTableItems = async ( table , items ) => {
table . rows . add ( items ) . draw ( false ) ;
}
/ * *
* JS Function to execute ( A ) on search text change , ( B ) on search options changes
* /
const onChange = ( ) => {
const searchValue = search ValueInp . value ;
const searchValue = search Object . value ;
if ( searchValue . length > 2 ) {
// Cancel any ongoing requests
if ( controller ) controller . abort ( ) ;
// we clear the table again
clearAll ( ) ;
// Create new controller and issue new request
controller = new AbortController ( ) ;
doSearch ( controller . signal , sTables ) ;
// 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 any message in search results message div
//document.getElementById('search-mssg-box').innerHTML = '';
// Clear the table
clearAll ( ) ;
}
} ;
const previewReplace = ( ) => {
const replaceValue = replaceValueInp . value ;
console . log ( replaceValueInp ) ;
if ( replaceValue . length ) {
document . getElementById ( 'search-mssg-box' ) . innerHTML = replaceValue ;
}
}
// Do the search on key up of search or replace input element
searchValueInp . onkeyup = onChange ;
replaceValueInp . onkeyup = previewReplace ;
// Do the search on key up of search input element
caseSensitiveLbl . onchange = onChange ;
completeWordLbl . onchange = onChange ;
regexpSearchLbl . onchange = onChange ;