您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Delete multiple devices in IoTTalk using regular expressions
// ==UserScript== // @name IoTTalk Delete Device // @namespace Fractalism // @version 1.1.2 // @description Delete multiple devices in IoTTalk using regular expressions // @author Fractalism // @match http*://*.iottalk.tw/list_all // @grant none // ==/UserScript== (function () { 'use strict'; // make find-and-delete UI (function CreateUI() { var MainUI = document.createElement('div'); document.body.appendChild(MainUI); MainUI.id = 'main-UI'; MainUI.innerHTML = ` <form action='javascript:void(0)'> <span>Find and delete</span><br> <input type='text' id='d_name-input'><br> <div id='search-div'> <button id='search-btn'>Search</button> <button id='hint-message' disabled><b>?</b></button> </div> <div id='delete-div' style='display:none'> <button id='delete-btn'>Delete</button> <button id='cancel-btn'>Cancel</button> </div> <div id='param-div'> <label><input type='checkbox' id='case_sensitive'><small>Case-sensitive search</small></label><br> <label><input type='checkbox' id='no_project' checked><small>Match unused devices only</small></label> </div> </form> `; MainUI.style.position = 'fixed'; MainUI.style.top = '20px'; // pixels MainUI.style.right = '20px'; // pixels MainUI.style.backgroundColor = 'white'; MainUI.style.border = '1px black solid'; MainUI.style.padding = '5px 15px 5px 15px'; MainUI.style.borderRadius = '10px'; var d_name_Input = document.getElementById('d_name-input'); d_name_Input.onkeydown = function () { if (!(event.which == 13 || event.keyCode == 13)) { switch_display(1); } } var SearchDiv = document.getElementById('search-div'); SearchDiv.style.position = 'relative'; // to position HintButton correctly SearchDiv.style.width = d_name_Input.clientWidth; var DeleteDiv = document.getElementById('delete-div'); DeleteDiv.style.position = 'relative'; var ParamsDiv = document.getElementById('param-div'); var SearchButton = document.getElementById('search-btn'); SearchButton.onclick = function () { var params = { case_sensitive: document.getElementById('case_sensitive').checked, no_project: document.getElementById('no_project').checked, } find_devices(params); switch_display(2); d_name_Input.focus(); }; var DeleteButton = document.getElementById('delete-btn'); DeleteButton.onclick = function () { var confirm = delete_devices(); if (confirm) { switch_display(1); } }; var CancelButton = document.getElementById('cancel-btn'); CancelButton.onclick = function () { window.device_matches = []; switch_display(1); } var HintButton = document.getElementById('hint-message'); HintButton.style.position = 'absolute'; //HintButton.style.bottom = '10px'; HintButton.style.right = '0px'; HintButton.style.width = '1.5em'; HintButton.style.height = '1.5em'; HintButton.style.border = '1.5px black solid'; HintButton.style.borderRadius = '50%'; HintButton.style.color = 'black'; HintButton.style.backgroundColor = 'white'; HintButton.style.padding = '0px'; HintButton.title = `Hint:\n> Type part of the d_name or use a regex, check the console for results.\n> Search empty string to get all devices on the server.\n> Right click on an <a> element in a match and select "Scroll into view" to see the device on the webpage.`; var my_style = document.createElement('style'); my_style.innerHTML = ` div#main-UI * {text-align: center; margin: 5px 0px 5px 0px;} div#main-UI * {font-family: Arial, Helvetica, sans-serif;} div#main-UI div {margin: 0px auto 0px auto; padding: 0px 0px 0px 0px;} div#main-UI input[type=checkbox] {margin: 5px 5px 5px 5px;} div#main-UI #param-div {text-align: left;} `; // apply to everything inside div document.head.appendChild(my_style); // switch to display certain buttons // mode: 1=search mode, 2=delete mode function switch_display(mode) { switch (mode) { case 1: SearchDiv.style.display = 'block'; DeleteDiv.style.display = 'none'; ParamsDiv.style.display = 'block'; break; case 2: SearchDiv.style.display = 'none'; DeleteDiv.style.display = 'block'; ParamsDiv.style.display = 'none'; break; default: } } })(); // modify send_delete function for one-by-one deletions without refreshing page window.send_delete = send_delete_single; function send_delete_single(url) { $.ajax({ url: url, type: 'DELETE', success: (result) => alert(`Device with MAC address ${url.substr(1)} deleted successfully.`), error: (jqXHR, textStatus, errorThrown) => alert(`Error deleting ${url.substr(1)}\ntextStatus: ${textStatus}\nerrorThrown: ${errorThrown}`) }); event.preventDefault(); // don't scroll to top after deleting } // for batch deletions (send output to console instead) function send_delete_batch(url) { $.ajax({ url: url, type: 'DELETE', success: (result) => console.log(`Device with MAC address ${url.substr(1)} deleted successfully.`), error: (jqXHR, textStatus, errorThrown) => console.log(`Error deleting ${url.substr(1)}\ntextStatus: ${textStatus}\nerrorThrown: ${errorThrown}`) }); event.preventDefault(); } // find devices with d_name matching input regex function find_devices(params) { console.clear(); var d_name = document.getElementById('d_name-input').value; console.log('Search d_name: ' + d_name); var index, node, matches = [], skipped = []; for ([index, node] of window.relevantNodes.entries()) { if (node.nodeType == node.TEXT_NODE && node.nodeValue.search(RegExp(`d_name: .*${d_name}`, params.case_sensitive ? '' : 'i')) != -1) { if (params.no_project && node.nodeValue.trim().search(/project:$/) != -1 && (node.nextSibling.getAttribute('onclick') && node.nextSibling.getAttribute('onclick').indexOf('open_project(') != -1)) { skipped.push([node, window.relevantNodes[index - 1]]); } else { matches.push([node, window.relevantNodes[index - 1]]); // text node and link to delete device } } } console.log(`Found ${matches.length+skipped.length} devices (${matches.length} matched, ${skipped.length} skipped)`); console.groupCollapsed(`Matched devices (${matches.length})`); for (let i = 0; i < matches.length; ++i) { let d_name, dm_name; let lines = matches[i][0].nodeValue.split('\n'); for (let line of lines) { if (line.search('d_name:') != -1) d_name = line.trim(); if (line.search('dm_name:') != -1) dm_name = line.trim(); } console.log(`Match ${i+1}:\n ${d_name}\n ${dm_name}\n`, matches[i][1]); }; console.groupEnd(); console.groupCollapsed(`Skipped devices (${skipped.length})`); for (let i = 0; i < skipped.length; ++i) { let d_name, dm_name; let lines = skipped[i][0].nodeValue.split('\n'); for (let line of lines) { if (line.search('d_name:') != -1) d_name = line.trim(); if (line.search('dm_name:') != -1) dm_name = line.trim(); } console.log(`Skip ${i+1}:\n ${d_name}\n ${dm_name}\n`, skipped[i][1]); } console.groupEnd(); window.device_matches = matches; } function delete_devices() { var matches = window.device_matches; if (!confirm(`Delete all ${matches.length} matched devices?`)) return false; window.send_delete = send_delete_batch; var index, match; for ([index, match] of matches.entries()) { console.log(`Deleting match ${index+1}`); match[1].onclick(); } window.send_delete = send_delete_single; return true; } // get all relevant nodes only once at start (function collect_nodes() { var relevantNodes = []; var walker = document.createTreeWalker(document.body.firstElementChild, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, function my_filter(node) { if (node.nodeType == node.TEXT_NODE && node.nodeValue.indexOf('d_name:') != -1 || node.nodeType == node.ELEMENT_NODE && node.nodeName == "A" && node.innerText == "Delete") { return NodeFilter.FILTER_ACCEPT; } else { return NodeFilter.FILTER_SKIP; } }); let node; while (node = walker.nextNode()) { if (node.nodeName == "HR") break; // scan device part, skip DF-module part relevantNodes.push(node); } window.relevantNodes = relevantNodes; })(); })();