IoTTalk Delete Device

Delete multiple devices in IoTTalk using regular expressions

当前为 2020-06-30 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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;
    })();
})();