// ==UserScript== // @name App Inventer 2 block helper // @version 0.6.1 // @namespace App Inventer 2 block helper // @description An easy way to operate blocks at MIT App Inventor 2. // @author wangsk789@163.com // @match *://ai2.appinventor.mit.edu/* // @match *://code.appinventor.mit.edu/* // @license MIT // @downloadURL none // ==/UserScript== (function() { //'use strict'; setTimeout(() => { var lastIndex = -1; var blocks; let container = document.querySelector(".ya-ProjectName").parentElement.nextElementSibling.firstChild.firstChild.firstChild; 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 && lastIndex < blocks.length) { blocks[lastIndex].setHighlighted(false); lastIndex = -1; } span.innerHTML = ""; }); 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().filter(block=>simpleString(block).toLowerCase().includes(keyword.toLowerCase())); if(lastIndex > -1){ blocks[lastIndex].setHighlighted(false); } lastIndex++; if(lastIndex{ return types.indexOf(block.type)>=0 }); var i=0; var timer=setTimeout(function(){ if(i"; 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(/ /g, ' '); 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); })();