Greasy Fork

Greasy Fork is available in English.

App Inventer 2 block helper

An easy way to operate blocks at MIT App Inventor 2.

当前为 2023-04-03 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         App Inventer 2 block helper
// @version      0.5
// @namespace    App Inventer 2 block helper
// @description  An easy way to operate blocks at MIT App Inventor 2.
// @author       [email protected]
// @match        *://ai2.appinventor.mit.edu/*
// @match        *://code.appinventor.mit.edu/*
// @license      MIT

// ==/UserScript==

(function() {
    //'use strict';
    setTimeout(() => {
        var lastIndex = -1;
        var blocks;
        let container = document.querySelectorAll("tr")[50];
        let td = document.createElement("td");
        td.style = "vertical-align: top;";
        container.appendChild(td);

        let input = document.createElement("input");
        input.type = "text";
        input.size = "15";
        input.id = "myInput";
        input.placeholder = "key word here...";
        td.appendChild(input);

        let td2 = document.createElement("td");
        td2.style = "vertical-align: top;";
        container.appendChild(td2);
        let button = document.createElement("button");
        button.id = "mySearch";
        button.className = "ode-TextButton ode-TextButton-up";
        button.innerHTML = "Search";
        td2.appendChild(button);
        button.addEventListener("click", () => {
            if (input.value) {
                findBlock(input.value);
            }
        });



        let td3 = document.createElement("td");
        td3.style = "vertical-align: top;";
        container.appendChild(td3);
        let clear = document.createElement("button");
        clear.id = "myClear";
        clear.className = "ode-TextButton ode-TextButton-up";
        clear.innerHTML = "Clear";
        td3.appendChild(clear);
        clear.addEventListener("click", () => {
            input.value = "";
            if (blocks && lastIndex > -1) {
                blocks[lastIndex].setHighlighted(false);
                lastIndex = -1;
            }
        });

        let td4 = document.createElement("td");
        td4.style = "vertical-align: top;";
        container.appendChild(td4);
        let removeComment = document.createElement("button");
        removeComment.id = "myRemoveComment";
        removeComment.className = "ode-TextButton ode-TextButton-up";
        removeComment.innerHTML = "Remove Comments";
        td4.appendChild(removeComment);
        removeComment.addEventListener("click", () => {
            Blockly.getMainWorkspace().getAllBlocks().forEach(b=>{b.setCommentText(null)});
        });

        let td5 = document.createElement("td");
        td5.style = "vertical-align: top;";
        container.appendChild(td5);
        let downloadAll = document.createElement("button");
        downloadAll.id = "myDownloadAll";
        downloadAll.className = "ode-TextButton ode-TextButton-up";
        downloadAll.innerHTML = "Download All";
        td5.appendChild(downloadAll);
        downloadAll.addEventListener("click", () => {
            downloadPNGIgnoreOrphan();
        });

        let td6 = document.createElement("td");
        td6.style = "vertical-align: top;";
        container.appendChild(td6);
        let span = document.createElement("span");
        span.id = "progressIndicator";
        span.innerHTML = "";
        td6.appendChild(span);
 function findBlock(keyword) {
        blocks = Blockly.getMainWorkspace().getAllBlocks();
        for (var i = lastIndex + 1; i < blocks.length; i++) {
            blocks[i].setCollapsed(false);
            if (simpleString(blocks[i]).toLowerCase().includes(keyword.toLowerCase())) {

                Blockly.getMainWorkspace().centerOnBlock(blocks[i].id);
                blocks[i].select();
                blocks[i].setHighlighted(true)
                if (lastIndex > -1) {
                    blocks[lastIndex].setHighlighted(false);
                }
                lastIndex = i;
                return;
            }
        }
        if (lastIndex > -1) {
            blocks[lastIndex].setHighlighted(false);
            lastIndex = -1;
        }
    }

    function simpleString(block) {
        var text = '';
        for (var i = 0, input;
             (input = block.inputList[i]); i++) {
            if (input.name == Blockly.BlockSvg.COLLAPSED_INPUT_NAME) {
                continue;
            }
            for (var j = 0, field;
                 (field = input.fieldRow[j]); j++) {
                text += field.getText() + ' ';
            }
        }
        text = goog.string.trim(text) || '???';
        return text;
    }

    function downloadPNGIgnoreOrphan(){
        var types=["component_event","global_declaration","procedures_defreturn","procedures_defnoreturn"];
        var topblocks=Blockly.getMainWorkspace().getTopBlocks();
        var blocks=topblocks.filter((block)=>{
            return types.indexOf(block.type)>=0
        });
        var i=0;
        var timer=setTimeout(function(){
            if(i<blocks.length){
                document.querySelector("#progressIndicator").innerHTML = (i+1)+"/"+blocks.length;
                exportBlockAsPng(blocks[i]);
                i++;
                timer=setTimeout(arguments.callee,1000)
            }else{
                //window.postMessage({message:"total "+i+" image(s) downloaded"},"*");
            }
        },1000);
    }



    var doctype = '<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">';

    function isExternal(url) {
        return url && url.lastIndexOf('http', 0) == 0 && url.lastIndexOf(window.location.host) == -1
    }

    function styles(el, selectorRemap) {
        var css = "";
        var sheets = document.styleSheets;
        for (var i = 0; i < sheets.length; i++) {
            if (isExternal(sheets[i].href)) {
                console.warn("Cannot include styles from other hosts: " + sheets[i].href);
                continue
            }
            var rules = null;
            try {
                rules = sheets[i].cssRules
            } catch (e) {
                console.warn('Skipping a potentially injected stylesheet', e);
                continue
            }
            if (rules != null) {
                for (var j = 0; j < rules.length; j++) {
                    var rule = rules[j];
                    if (typeof(rule.style) != "undefined") {
                        var match = null;
                        try {
                            match = el.querySelector(rule.selectorText)
                        } catch (err) {
                            console.warn('Invalid CSS selector "' + rule.selectorText + '"', err)
                        }
                        if (match && rule.selectorText.indexOf("blocklySelected") == -1) {
                            var selector = selectorRemap ? selectorRemap(rule.selectorText) : rule.selectorText;
                            css += selector + " { " + rule.style.cssText + " }"
                        } else if (rule.cssText.match(/^@font-face/)) {
                            css += rule.cssText + ''
                        }
                    }
                }
            }
        }
        return css
    }
    function svgAsDataUri(el, optmetrics, options, cb) {
        options = options || {};
        options.scale = options.scale || 1;
        var xmlns = "http://www.w3.org/2000/xmlns/";
        var outer = document.createElement("div");
        var textAreas = document.getElementsByTagName("textarea");
        for (var i = 0; i < textAreas.length; i++) {
            textAreas[i].innerHTML = textAreas[i].value
        }
        var clone = el.cloneNode(true);
        var width, height;
        if (el.tagName == 'svg') {
            var box = el.getBoundingClientRect();
            width = box.width || parseInt(clone.getAttribute('width') || clone.style.width || window.getComputedStyle(el).getPropertyValue('width'));
            height = box.height || parseInt(clone.getAttribute('height') || clone.style.height || window.getComputedStyle(el).getPropertyValue('height'));
            var left = (parseFloat(optmetrics.contentLeft) - parseFloat(optmetrics.viewLeft)).toString();
            var top = (parseFloat(optmetrics.contentTop) - parseFloat(optmetrics.viewTop)).toString();
            var right = (parseFloat(optmetrics.contentWidth)).toString();
            var bottom = (parseFloat(optmetrics.contentHeight)).toString();
            clone.setAttribute("viewBox", left + " " + top + " " + right + " " + bottom)
        } else {
            var matrix = el.getScreenCTM();
            //clone.setAttribute('transform', clone.getAttribute('transform').replace(/translate\(.*?\)/, '').replace(/scale\(.*?\)/, '').trim());
            clone.setAttribute('transform', "");
            var box = el.getBBox();
            width = box.width;
            height = box.height;
            var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
            svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
            svg.appendChild(clone);
            clone = svg;
            clone.setAttribute('viewBox', box.x + " " + box.y + " " + width + " " + height)
        }
        clone.setAttribute("version", "1.1");
        clone.setAttribute("width", width);
        clone.setAttribute("height", height);
        clone.setAttribute("style", 'background-color: rgba(255, 255, 255, 0);');
        outer.appendChild(clone);
        var css = styles(el, options.selectorRemap);
        var s = document.createElement('style');
        s.setAttribute('type', 'text/css');
        s.innerHTML = "<![CDATA[" + css + "]]>";
        var defs = document.createElement('defs');
        defs.appendChild(s);
        clone.insertBefore(defs, clone.firstChild);
        var toHide = clone.getElementsByClassName("blocklyScrollbarHandle");
        for (var i = 0; i < toHide.length; i++) {
            toHide[i].setAttribute("visibility", "hidden")
        }
        toHide = clone.getElementsByClassName("blocklyScrollbarBackground");
        for (var i = 0; i < toHide.length; i++) {
            toHide[i].setAttribute("visibility", "hidden")
        }
        toHide = clone.querySelectorAll('image');
        for (var i = 0; i < toHide.length; i++) {
            toHide[i].setAttribute("visibility", "hidden")
        }
        toHide = clone.querySelectorAll('.blocklyMainBackground');
        for (var i = 0; i < toHide.length; i++) {
            toHide[i].parentElement.removeChild(toHide[i])
        }
        var zelement = clone.getElementById("rectCorner");
        if (zelement) {
            zelement.setAttribute("visibility", "hidden")
        }
        zelement = clone.getElementById("indicatorWarning");
        if (zelement) {
            zelement.setAttribute("visibility", "hidden")
        }
        var svg = doctype + outer.innerHTML;
        svg = svg.replace(/&nbsp/g, '&#160');
        svg = svg.replace(/sans-serif/g, 'Arial, Verdana, "Nimbus Sans L", Helvetica');
        var uri = 'data:image/svg+xml;base64,' + window.btoa(unescape(encodeURIComponent(svg)));
        if (cb) {
            cb(uri)
        }
    }

    function makeCRCTable() {
        var c;
        var crcTable = [];
        for (var n = 0; n < 256; n++) {
            c = n;
            for (var k = 0; k < 8; k++) {
                c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1))
            }
            crcTable[n] = c
        }
        return crcTable
    }

    function crc32(data) {
        var crcTable = window.crcTable || (window.crcTable = makeCRCTable());
        var crc = 0 ^ (-1);
        for (var i = 0; i < data.length; i++) {
            crc = (crc >>> 8) ^ crcTable[(crc ^ data[i]) & 0xFF]
        }
        return (crc ^ (-1)) >>> 0
    }
    var CODE_PNG_CHUNK = 'coDe';

    function PNG() {
        this.chunks = null
    }
    PNG.HEADER = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
    var pHY_data = [0x00, 0x00, 0x16, 0x25, 0x00, 0x00, 0x16, 0x25, 0x01];
    PNG.Chunk = function(length, type, data, crc) {
        this.length = length;
        this.type = type;
        this.data = data;
        this.crc = crc
    };
    PNG.prototype.readFromBlob = function(blob, callback) {
        var reader = new FileReader();
        var png = this;
        reader.addEventListener('loadend', function() {
            png.processData_(new Uint8Array(reader.result));
            if (callback instanceof Function) callback(png)
        });
        reader.readAsArrayBuffer(blob)
    };
    PNG.prototype.getCodeChunk = function() {
        if (!this.chunks) return null;
        for (var i = 0; i < this.chunks.length; i++) {
            if (this.chunks[i].type === CODE_PNG_CHUNK) {
                return this.chunks[i]
            }
        }
        return null
    };
    PNG.prototype.processData_ = function(data) {
        var chunkStart = PNG.HEADER.length;

        function decode4() {
            var num;
            num = data[chunkStart++];
            num = num * 256 + data[chunkStart++];
            num = num * 256 + data[chunkStart++];
            num = num * 256 + data[chunkStart++];
            return num
        }

        function read4() {
            var str = '';
            for (var i = 0; i < 4; i++, chunkStart++) {
                str += String.fromCharCode(data[chunkStart])
            }
            return str
        }

        function readData(length) {
            return data.slice(chunkStart, chunkStart + length)
        }
        this.chunks = [];
        while (chunkStart < data.length) {
            var length = decode4();
            var type = read4();
            var chunkData = readData(length);
            chunkStart += length;
            var crc = decode4();
            this.chunks.push(new PNG.Chunk(length, type, chunkData, crc))
        }
    };
    PNG.prototype.setCodeChunk = function(code) {
        var text = new TextEncoder().encode(CODE_PNG_CHUNK + code);
        var length = text.length - 4;
        var crc = crc32(text);
        text = text.slice(4);
        for (var i = 0, chunk;
             (chunk = this.chunks[i]); i++) {
            if (chunk.type === CODE_PNG_CHUNK) {
                chunk.length = length;
                chunk.data = text;
                chunk.crc = crc;
                return
            }
        }
        chunk = new PNG.Chunk(length, CODE_PNG_CHUNK, text, crc);
        this.chunks.splice(this.chunks.length - 1, 0, chunk)
    };
    PNG.prototype.toBlob = function() {
        var length = PNG.HEADER.length;
        this.chunks.forEach(function(chunk) {
            length += chunk.length + 12
        });
        var buffer = new Uint8Array(length);
        var index = 0;

        function write4(value) {
            if (typeof value === 'string') {
                var text = new TextEncoder().encode(value);
                buffer.set(text, index);
                index += text.length
            } else {
                buffer[index + 3] = value & 0xFF;
                value >>= 8;
                buffer[index + 2] = value & 0xFF;
                value >>= 8;
                buffer[index + 1] = value & 0xFF;
                value >>= 8;
                buffer[index] = value & 0xFF;
                index += 4
            }
        }

        function writeData(data) {
            buffer.set(data, index);
            index += data.length
        }
        writeData(PNG.HEADER);
        this.chunks.forEach(function(chunk) {
            write4(chunk.length);
            write4(chunk.type);
            writeData(chunk.data);
            write4(chunk.crc)
        });
        return new Blob([buffer], {
            'type': 'image/png'
        })
    };
    function exportBlockAsPng(block) {
        var xml = document.createElement('xml');
        xml.appendChild(Blockly.Xml.blockToDom(block, true));
        var code = Blockly.Xml.domToText(xml);
        svgAsDataUri(block.svgGroup_, block.workspace.getMetrics(), null, function(uri) {
            var img = new Image();
            img.src = uri;
            img.onload = function() {
                var canvas = document.createElement('canvas');
                canvas.width = 2 * img.width;
                canvas.height = 2 * img.height;
                var context = canvas.getContext('2d');
                context.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height);

                function download(png) {
                    png.setCodeChunk(code);
                    for (var i = 0; i < png.chunks.length; i++) {
                        var phy = [112, 72, 89, 115];
                        if (png.chunks[i].type == 'pHYs') {
                            png.chunks.splice(i, 1, new PNG.Chunk(9, 'pHYs', pHY_data, crc32(phy.concat(pHY_data))));
                            break
                        } else if (png.chunks[i].type == 'IDAT') {
                            png.chunks.splice(i, 0, new PNG.Chunk(9, 'pHYs', pHY_data, crc32(phy.concat(pHY_data))));
                            break
                        }
                    }
                    var blob = png.toBlob();
                    var a = document.createElement('a');
                    a.download = simpleString(block) + '.png';
                    a.target = '_self';
                    a.href = URL.createObjectURL(blob);
                    document.body.appendChild(a);
                    a.addEventListener("click", function(e) {
                        a.parentNode.removeChild(a)
                    });
                    a.click()
                }
                if (canvas.toBlob === undefined) {
                    var src = canvas.toDataURL('image/png');
                    var base64img = src.split(',')[1];
                    var decoded = window.atob(base64img);
                    var rawLength = decoded.length;
                    var buffer = new Uint8Array(new ArrayBuffer(rawLength));
                    for (var i = 0; i < rawLength; i++) {
                        buffer[i] = decoded.charCodeAt(i)
                    }
                    var blob = new Blob([buffer], {
                        'type': 'image/png'
                    });
                    new PNG().readFromBlob(blob, download)
                } else {
                    canvas.toBlob(function(blob) {
                        new PNG().readFromBlob(blob, download)
                    })
                }
            }
        })
    };


        console.log("block finder loaded~~~");




    }, 8000);

   

})();