Search for webmon

Want to share your OpenWrt / Gargoyle knowledge? Implemented a new feature? Let us know here.

Moderator: Moderators

fifonik
Posts: 165
Joined: Fri Dec 02, 2016 3:52 am
Location: Brisbane, AU

Search for webmon

Post by fifonik »

I've implemented search functionality for webmon page.

Happy to share the code if someone interested in.

Unfortunately, I cannot supply proper PR as do not know how to organise everything correctly for gargoyle.

In my case I've just inserted everything in single www/webmon.sh:
- One line in html (added input)
- A few JS functions at the end of the html in script-section

P.S. Should I just post it here?

Lantis
Moderator
Posts: 7080
Joined: Mon Jan 05, 2015 5:33 am
Location: Australia
Contact:

Re: Search for webmon

Post by Lantis »

Sure, post it here. If it's all in the html I will probably rework it but that's fine.
https://lantisproject.com/downloads/gargoylebuilds for the latest releases
Please be respectful when posting. I do this in my free time on a volunteer basis.
https://lantisproject.com/blog

fifonik
Posts: 165
Joined: Fri Dec 02, 2016 3:52 am
Location: Brisbane, AU

Re: Search for webmon

Post by fifonik »

www/webmon.sh

Code: Select all

					<span class="col-xs-12">
						<select id="domain_host_display" class="form-control" onchange="updateMonitorTable()"> <!-- note: select's class adjusted-->
							<option value="hostname"><%~ DspHn %></option>
							<option value="ip"><%~ DspHIP %></option>
						</select>
+						<input id="domain_search" type="text" class="form-control" placeholder="Filter" /> <!-- todo@ localisation -->
					</span>

...

	// to be added at the end of the file before resetData();
	function webmonSearch(input, container){
		const table = container.getElementsByTagName('table')[0];
		if (!table) return;

		// The 'table-striped' used to highlight odd/even rows using CSS
		// Unfortunately, this cannot be used if some rows are hidden
		// So removing the class and later assign odd/even classes directly to the rows
		table.classList.remove('table-striped');

		const tbody = table.getElementsByTagName('tbody')[0];
		if (!tbody) return;

		const s = input.value.trim();
		if (s == '') {
			for (let i = 0, k = tbody.rows.length; i < k; ++i) {
				tbody.rows[i].style.display = ''; // 'table-row' can also be used
				tbody.rows[i].className = i % 2 ? 'even' : 'odd';
			}
		} else {
			const S = s.toUpperCase();
			let visibleIndex = 0;
			for (let i = 0, k = tbody.rows.length; i < k; ++i) {
				const row = tbody.rows[i];
				let host = row.dataset.host;
				if(host == null){
					// Caching host/ip in dataset
					host = row.cells[0] && row.cells[0].firstChild ? row.cells[0].firstChild.nodeValue.toUpperCase() : '';
					row.dataset.host = host;
				}
				let value = row.dataset.value;
				if(value == null){
					// Caching value (domain/search) in dataset
					value = row.cells[2] && row.cells[2].firstChild ? row.cells[2].firstChild.getAttribute('href') : '';
					const proto = value.indexOf('://');
					if(proto > 0) value = value.slice(proto + 3).toUpperCase();
					row.dataset.value = value;
				}
				if(host.indexOf(S) >= 0 || value.indexOf(S) >= 0){
					row.style.display = ''; // 'table-row' can also be used
					row.className = visibleIndex++ % 2 ? 'even' : 'odd';
				} else {
					row.style.display = 'none';
				}
			}
		}
	}

	function webmonSearchInit(inputId, containerId){
		const input = document.getElementById(inputId);
		if(!input) return;

		const container = document.getElementById(containerId);
		if(!container){
			// No container for the table exists -- hide filter input
			input.style.display='none';
			return;
		}

		// todo@ add debounce
		input.addEventListener('keyup', evt => webmonSearch(input, container), false);

		const observer = new MutationObserver((mutationList, observer) => {
			if(input.value.trim() == '') return;
			for (const mutation of mutationList) {
				if (mutation.type === 'childList' && mutation.addedNodes) {
					// New table added, re-filtering
					webmonSearch(input, container);
					break;
				}
			}
		});

		observer.observe(container, { childList: true });
	}

	webmonSearchInit('domain_search', 'webmon_domain_table_container');

Lantis
Moderator
Posts: 7080
Joined: Mon Jan 05, 2015 5:33 am
Location: Australia
Contact:

Re: Search for webmon

Post by Lantis »

Haven't forgotten this. It's next on my list.
https://lantisproject.com/downloads/gargoylebuilds for the latest releases
Please be respectful when posting. I do this in my free time on a volunteer basis.
https://lantisproject.com/blog

fifonik
Posts: 165
Joined: Fri Dec 02, 2016 3:52 am
Location: Brisbane, AU

Re: Search for webmon

Post by fifonik »

That's OK, nobody forcing you to do this :)

ispyisail
Moderator
Posts: 5212
Joined: Mon Apr 06, 2009 3:15 am
Location: New Zealand

Re: Search for webmon

Post by ispyisail »

@Lantis

What's your view on ChatGPT for coding?

For example, I used ChatGPT to help explain the above code to me. I then asked it if it could improve the code.......

Just wondering

fifonik
Posts: 165
Joined: Fri Dec 02, 2016 3:52 am
Location: Brisbane, AU

Re: Search for webmon

Post by fifonik »

Just post the improved version! I'd like to see it.

P.S. I can explain my code to you if you like.

ispyisail
Moderator
Posts: 5212
Joined: Mon Apr 06, 2009 3:15 am
Location: New Zealand

Re: Search for webmon

Post by ispyisail »

fifonik wrote:
Sat Jul 20, 2024 4:27 am
Just post the improved version! I'd like to see it.

P.S. I can explain my code to you if you like.
It was more of a comment about ChatGPT, not related to your code. I don't code myself just to be clear

ispyisail
Moderator
Posts: 5212
Joined: Mon Apr 06, 2009 3:15 am
Location: New Zealand

Re: Search for webmon

Post by ispyisail »

Here is the improved version of the code:

Code: Select all

<span class="col-xs-12">
    <select id="domain_host_display" class="form-control" onchange="updateMonitorTable()">
        <option value="hostname"><%~ DspHn %></option>
        <option value="ip"><%~ DspHIP %></option>
    </select>
    <input id="domain_search" type="text" class="form-control" placeholder="Filter" />
</span>

Code: Select all

// Debounce function to limit the rate at which a function can fire
function debounce(func, wait) {
    let timeout;
    return function(...args) {
        clearTimeout(timeout);
        timeout = setTimeout(() => func.apply(this, args), wait);
    };
}

function filterTableRows(input, table) {
    const tbody = table.querySelector('tbody');
    if (!tbody) return;

    const searchTerm = input.value.trim().toUpperCase();
    const rows = Array.from(tbody.rows);

    rows.forEach((row, index) => {
        const host = row.dataset.host || row.cells[0]?.textContent.trim().toUpperCase() || '';
        const value = row.dataset.value || (row.cells[2]?.querySelector('a')?.getAttribute('href') || '').split('://')[1]?.toUpperCase() || '';

        row.dataset.host = host;
        row.dataset.value = value;

        const isVisible = host.includes(searchTerm) || value.includes(searchTerm);
        row.style.display = isVisible ? '' : 'none';
        row.className = isVisible ? (index % 2 ? 'even' : 'odd') : '';
    });
}

function initWebmonSearch(inputId, containerId) {
    const input = document.getElementById(inputId);
    const container = document.getElementById(containerId);

    if (!input || !container) {
        if (input) input.style.display = 'none';
        return;
    }

    const table = container.querySelector('table');
    if (!table) return;

    table.classList.remove('table-striped');
    
    const debouncedFilter = debounce(() => filterTableRows(input, table), 300);
    input.addEventListener('keyup', debouncedFilter);

    const observer = new MutationObserver(mutations => {
        if (input.value.trim() === '') return;
        mutations.forEach(mutation => {
            if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                debouncedFilter();
            }
        });
    });

    observer.observe(container, { childList: true });
}

initWebmonSearch('domain_search', 'webmon_domain_table_container');
Key Improvements
Debounce Function: Added a debounce function to limit the frequency of the filterTableRows function calls.

Simplified Caching Logic: Cached the host and value more clearly and ensured the logic is straightforward.

Modern JavaScript Syntax: Utilized const, let, arrow functions, and template literals for cleaner and more modern JavaScript code.

Separated Concerns: Split the logic into smaller functions (debounce, filterTableRows, initWebmonSearch) to improve readability and maintainability.

This version is more efficient, easier to read, and better suited for modern JavaScript development practices.

ispyisail
Moderator
Posts: 5212
Joined: Mon Apr 06, 2009 3:15 am
Location: New Zealand

Re: Search for webmon

Post by ispyisail »

This code could be rubbish for all I know

Post Reply