",
7 | "icon":"website",
8 | "tags":"url,query,params,json,convert,decode"
9 | }
10 | **/
11 |
12 | function convertToJson(urlParams) {
13 |
14 | return urlParams
15 | .replace(/\[\d?\]=/gi, '=')
16 | .split('&')
17 | .reduce((result, param) => {
18 | var [key, value] = param.split('=');
19 | value = decodeURIComponent(value || '');
20 |
21 | if (!result.hasOwnProperty(key)) {
22 | result[key] = value;
23 |
24 | return result;
25 | }
26 |
27 | result[key] = [...[].concat(result[key]), value]
28 |
29 | return result
30 | }, {});
31 | }
32 |
33 | function main(input)
34 | {
35 | try {
36 | input.text = JSON.stringify(convertToJson(input.text));
37 | } catch (error) {
38 | input.postError("Unable to parse given string")
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/Boop/Boop/scripts/RemoveDuplicates.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Remove Duplicate Lines",
5 | "description":"Ensures each line of your text is unique.",
6 | "author":"andipaetzold",
7 | "icon":"filtration",
8 | "tags":"unique,duplicate"
9 | }
10 | **/
11 |
12 | function main(input) {
13 | let lines = input.text.split('\n')
14 | let out = unique(lines)
15 |
16 | input.text = out.join('\n')
17 |
18 | input.postInfo(`${lines.length - out.length} lines removed`)
19 |
20 | }
21 |
22 | function unique(array) {
23 | return [...new Set(array)]
24 | }
25 |
--------------------------------------------------------------------------------
/Boop/Boop/scripts/RemoveSlashes.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Remove Slashes",
5 | "description":"Unescapes your text.",
6 | "author":"Ivan",
7 | "icon":"quote",
8 | "tags":"strip,slashes,remove,unescape"
9 | }
10 | **/
11 |
12 | function main(input){
13 |
14 | // discuss at: http://locutus.io/php/stripslashes/
15 | // original by: Kevin van Zonneveld (http://kvz.io)
16 | // improved by: Ates Goral (http://magnetiq.com)
17 | // improved by: marrtins
18 | // improved by: rezna
19 | // fixed by: Mick@el
20 | // bugfixed by: Onno Marsman (https://twitter.com/onnomarsman)
21 | // bugfixed by: Brett Zamir (http://brett-zamir.me)
22 | // input by: Rick Waldron
23 | // input by: Brant Messenger (http://www.brantmessenger.com/)
24 | // reimplemented by: Brett Zamir (http://brett-zamir.me)
25 | // example 1: stripslashes('Kevin\'s code')
26 | // returns 1: "Kevin's code"
27 | // example 2: stripslashes('Kevin\\\'s code')
28 | // returns 2: "Kevin\'s code"
29 |
30 | input.text = (input.text + '')
31 | .replace(/\\(.?)/g, function (s, n1) {
32 | switch (n1) {
33 | case '\\':
34 | return '\\'
35 | case '0':
36 | return '\u0000'
37 | case '':
38 | return ''
39 | default:
40 | return n1
41 | }
42 | });
43 | }
44 |
--------------------------------------------------------------------------------
/Boop/Boop/scripts/ReplaceSmartQuotes.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Replace Smart Quotes",
5 | "description":"Replace Smart Quotes with their simpler values.",
6 | "author":"Thomas Bauer (https://github.com/tbauer428)",
7 | "icon":"broom",
8 | "tags":"smart,quotes,quotations,quotation,smart-quotes,smart-quotations"
9 | }
10 | **/
11 |
12 | function main(input) {
13 | input.text = input.text
14 | .replace(/[\u2018\u2019]/g, "'")
15 | .replace(/[\u201C\u201D]/g, '"')
16 | .replace(/“”/g, '"');
17 | }
18 |
--------------------------------------------------------------------------------
/Boop/Boop/scripts/ReverseLines.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Reverse Lines",
5 | "description":"Flips every line of your text.",
6 | "author":"@Clarko",
7 | "icon":"flip",
8 | "tags":"reverse,order,invert,mirror,flip,upside,down"
9 | }
10 | **/
11 |
12 | function main(input) {
13 | input.text = input.text.split('\n').reverse().join('\n')
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/Boop/Boop/scripts/ReverseString.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Reverse String",
5 | "description":"!seod ti tahw sseuG",
6 | "author":"See Source",
7 | "icon":"flip",
8 | "tags":"flip,mirror,invert"
9 | }
10 | **/
11 |
12 | function main(input) {
13 |
14 | input.text = reverse(input.text)
15 |
16 | }
17 |
18 | /*
19 |
20 | Snippet from https://github.com/mathiasbynens/esrever
21 |
22 | Copyright Mathias Bynens
23 |
24 | Permission is hereby granted, free of charge, to any person obtaining
25 | a copy of this software and associated documentation files (the
26 | "Software"), to deal in the Software without restriction, including
27 | without limitation the rights to use, copy, modify, merge, publish,
28 | distribute, sublicense, and/or sell copies of the Software, and to
29 | permit persons to whom the Software is furnished to do so, subject to
30 | the following conditions:
31 |
32 | The above copyright notice and this permission notice shall be
33 | included in all copies or substantial portions of the Software.
34 |
35 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
36 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
37 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
38 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
39 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
40 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
41 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
42 |
43 | */
44 |
45 |
46 | var regexSymbolWithCombiningMarks = /([\0-\u02FF\u0370-\u1AAF\u1B00-\u1DBF\u1E00-\u20CF\u2100-\uD7FF\uE000-\uFE1F\uFE30-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])([\u0300-\u036F\u1AB0-\u1AFF\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]+)/g;
47 | var regexSurrogatePair = /([\uD800-\uDBFF])([\uDC00-\uDFFF])/g;
48 |
49 | var reverse = function(string) {
50 | // Step 1: deal with combining marks and astral symbols (surrogate pairs)
51 | string = string
52 | // Swap symbols with their combining marks so the combining marks go first
53 | .replace(regexSymbolWithCombiningMarks, function($0, $1, $2) {
54 | // Reverse the combining marks so they will end up in the same order
55 | // later on (after another round of reversing)
56 | return reverse($2) + $1;
57 | })
58 | // Swap high and low surrogates so the low surrogates go first
59 | .replace(regexSurrogatePair, '$2$1');
60 | // Step 2: reverse the code units in the string
61 | var result = [];
62 | var index = string.length;
63 | while (index--) {
64 | result.push(string.charAt(index));
65 | }
66 | return result.join('');
67 | };
68 |
--------------------------------------------------------------------------------
/Boop/Boop/scripts/Rot13.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Rot13",
5 | "description":"Applies the Rot13 cypher to your text.",
6 | "author":"Paul Starr",
7 | "icon":"roman",
8 | "tags":"spoilers,encryption,plaintext"
9 | }
10 | **/
11 |
12 | function main(state) {
13 | let myText = state.text
14 | // adapted from Sophie Alpert's solution: https://stackoverflow.com/questions/617647/where-is-my-implementation-of-rot13-in-javascript-going-wrong
15 | state.text = myText.replace(/[a-z]/gi, function (c) {
16 | return String.fromCharCode(
17 | (c <= "Z" ? 90 : 122) >= (c=c.charCodeAt(0)+13) ? c : c - 26
18 | );
19 | });
20 | return state;
21 | }
22 |
--------------------------------------------------------------------------------
/Boop/Boop/scripts/SHA1.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"SHA1 Hash",
5 | "description":"Computes the SHA1 hash of your text (Hex encoded)",
6 | "icon":"fingerprint",
7 | "tags":"strip,slashes,remove"
8 | }
9 | **/
10 |
11 | const Hashes = require('@boop/hashes')
12 |
13 | function main(state) {
14 | var SHA1 = new Hashes.SHA1;
15 | state.text = SHA1.hex(state.text)
16 | }
17 |
--------------------------------------------------------------------------------
/Boop/Boop/scripts/SHA256.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"SHA256 Hash",
5 | "description":"Computes the SHA256 hash of your text (Hex encoded)",
6 | "icon":"fingerprint",
7 | "tags":"strip,slashes,remove"
8 | }
9 | **/
10 | const Hashes = require('@boop/hashes')
11 |
12 | function main(state) {
13 | var SHA256 = new Hashes.SHA256;
14 | state.text = SHA256.hex(state.text)
15 | }
16 |
--------------------------------------------------------------------------------
/Boop/Boop/scripts/SHA512.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"SHA512 Hash",
5 | "description":"Computes the SHA512 hash of your text (Hex encoded)",
6 | "icon":"fingerprint",
7 | "tags":"strip,slashes,remove"
8 | }
9 | **/
10 |
11 | const Hashes = require('@boop/hashes')
12 |
13 | function main(state) {
14 | var SHA512 = new Hashes.SHA512;
15 | state.text = SHA512.hex(state.text)
16 | }
17 |
--------------------------------------------------------------------------------
/Boop/Boop/scripts/ShuffleLines.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Shuffle Lines",
5 | "description":"Randomize each line of your text.",
6 | "author":"@Clarko",
7 | "icon":"dice",
8 | "tags":"shuffle,random"
9 | }
10 | **/
11 |
12 | function main(input) {
13 | let lines = input.text.split('\n');
14 | let j = lines.length;
15 |
16 | // Fisher-Yates Shuffle
17 | while (j) {
18 | i = Math.floor(Math.random() * j--);
19 | temp = lines[j];
20 | lines[j] = lines[i];
21 | lines[i] = temp;
22 | }
23 |
24 | input.text = lines.join('\n');
25 | }
26 |
--------------------------------------------------------------------------------
/Boop/Boop/scripts/SnakeCase.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Snake Case",
5 | "description":"converts_your_text_to_snake_case.",
6 | "author":"Ivan",
7 | "icon":"snake",
8 | "tags":"snake,case,function,lodash"
9 | }
10 | **/
11 |
12 | const { snakeCase } = require('@boop/lodash.boop')
13 |
14 | function main(input) {
15 |
16 | input.text = snakeCase(input.text)
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/Boop/Boop/scripts/Sort.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Sort lines",
5 | "description":"Sort lines alphabetically.",
6 | "author":"Sebastiaan Besselsen",
7 | "icon":"sort-characters",
8 | "tags":"sort,alphabet"
9 | }
10 | **/
11 |
12 | function main(input) {
13 | let sorted = input.text.replace(/\n$/, '').split('\n')
14 | .sort((a, b) => a.localeCompare(b))
15 | .join('\n');
16 |
17 | if (sorted === input.text) {
18 | sorted = sorted.split('\n').reverse().join('\n');
19 | }
20 | input.text = sorted;
21 | }
22 |
--------------------------------------------------------------------------------
/Boop/Boop/scripts/SortJSON.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Sort JSON",
5 | "description":"Sort JSON",
6 | "author":"MaDnh",
7 | "icon":"sort-characters",
8 | "tags":"json,sort"
9 | }
10 | **/
11 |
12 | function main(state) {
13 | let value = state.text;
14 |
15 | try {
16 | value = JSON.parse(value);
17 | } catch (e) {
18 | state.postError("Invalid JSON");
19 | return;
20 | }
21 |
22 | value = sort(value);
23 |
24 | state.text = JSON.stringify(value, null, 2);
25 | }
26 |
27 |
28 | function sort(obj) {
29 | if (obj instanceof Array) {
30 | let out = obj.map(item => sort(item));
31 | out.sort((a, b) => {
32 | let fa = JSON.stringify(a),
33 | fb = JSON.stringify(b);
34 |
35 | if (fa < fb) {
36 | return -1;
37 | }
38 | if (fa > fb) {
39 | return 1;
40 | }
41 | return 0;
42 | });
43 | return out;
44 | }
45 |
46 | if (!isPlainObject(obj)) {
47 | return obj
48 | }
49 |
50 | const result = {};
51 | const keys = Object.keys(obj);
52 |
53 | keys.sort();
54 | keys.forEach(key => {
55 | result[key] = sort(obj[key])
56 | });
57 |
58 | return result;
59 | }
60 |
61 | function isPlainObject(value) {
62 | return Object.prototype.toString.call(value) === '[object Object]'
63 | }
64 |
--------------------------------------------------------------------------------
/Boop/Boop/scripts/SpongeCase.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api": 1,
4 | "name": "Sponge Case",
5 | "description": "CoNvERtS yoUR Text To A HIghER fOrM Of CoMMUnICAtIOn",
6 | "author": "Paul Seelman",
7 | "icon": "pineapple",
8 | "tags": "bob,sarcasm,no,this,is,patrick"
9 | }
10 | **/
11 | function spongeText(string) {
12 | const chars = string.split("");
13 | for (let i = chars.length - 1; i > 0; i--) {
14 | const j = Math.floor(Math.random() * Math.floor(2));
15 | if (j == 0) {
16 | chars[i] = chars[i].toLowerCase();
17 | } else {
18 | chars[i] = chars[i].toUpperCase();
19 | }
20 | }
21 |
22 | return chars.join("");
23 | }
24 |
25 | function main(input) {
26 | input.text = spongeText(input.text);
27 | }
28 |
--------------------------------------------------------------------------------
/Boop/Boop/scripts/StartCase.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Start Case",
5 | "description":"Converts Your Text To Start Case.",
6 | "author":"Ivan",
7 | "icon":"type",
8 | "tags":"start,case,function,lodash"
9 | }
10 | **/
11 |
12 | const { startCase } = require('@boop/lodash.boop')
13 |
14 | function main(input) {
15 |
16 | input.text = startCase(input.text)
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/Boop/Boop/scripts/SumAll.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Sum All",
5 | "description":"Sums up a list of numbers.",
6 | "author":"Annie Tran",
7 | "icon":"abacus",
8 | "tags":"sum,calculator,addition,add"
9 | }
10 | **/
11 |
12 | function main(input) {
13 | if (!input.text) {
14 | input.postError('')
15 | } else {
16 | input.text = calculate(input.text)
17 | }
18 | }
19 |
20 | function looksLikeFraction(s) {
21 | return /^[\d\.]+\/[\d\.]+$/.test(s)
22 | }
23 |
24 | function getFraction(s) {
25 | const frac = s.split('/')
26 | return frac[0] / frac[1]
27 | }
28 |
29 | function getNumber(s) {
30 | if (looksLikeFraction(s)) {
31 | return getFraction(s)
32 | }
33 | return isNaN(Number(s)) ? '' : Number(s)
34 | }
35 |
36 | function numStringToArray(s) {
37 | return s
38 | .replace(/\/\/.*/g, '')
39 | .split(/[\n\s,;=]/g)
40 | .map((e) => (getNumber(e) ? getNumber(e) : ''))
41 | .filter(Boolean)
42 | }
43 |
44 | function calculate(s) {
45 | const comment = '\t// '
46 | const numbers = numStringToArray(s)
47 |
48 | var sumOutput = numbers.reduce((a, b) => a + b)
49 |
50 | if (numbers.length > 1) {
51 | sumOutput += comment + numbers.join(' + ')
52 | }
53 |
54 | return s
55 | .split(/[\n,;]/g)
56 | .map((e) => {
57 | e = e.trim()
58 | if (e.charAt(0) === '=' || e === '' || e.toString() === Number(e).toString()) {
59 | return e
60 | }
61 | return `${e}${getNumber(e) && comment + getNumber(e)}`
62 | })
63 | .concat('\n= ' + sumOutput)
64 | .join('\n')
65 | }
66 |
--------------------------------------------------------------------------------
/Boop/Boop/scripts/Test.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Test Script",
5 | "description":"Testing script",
6 | "author":"Ivan",
7 | "icon":"flask",
8 | "tags":"test,test,one,two"
9 | }
10 | **/
11 |
12 | function main(input) {
13 |
14 | input.postInfo("Hello this is a test!")
15 |
16 | input.fullText = `Hello, World! Let's try some syntax highlighting shall we?
17 |
18 | var test: String? = "Toast"
19 |
20 | {
21 | "name": "Boop",
22 | "type": "software",
23 | "info": {
24 | "tags": ["software", "editor"]
25 | },
26 | "useful": false,
27 | "version": 1.2345e-10
28 | }
29 |
30 | The MD5 of \`truth\` is 68934a3e9455fa72420237eb05902327
31 |
32 | SELECT "Hello" FROM table LIMIT 2
33 |
34 | /*
35 | haha you can't see me 👻
36 | */
37 |
38 | if(false) return; // this doesn't work
39 |
40 | This line was added on Fri, 19 Jun 2020 01:01:30 GMT
41 |
42 | World
43 |
44 |
45 | "This is quote-unquote \\"escaped\\" if you will."
46 | `
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/Boop/Boop/scripts/Trim.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Trim",
5 | "description":"Trims leading and trailing whitespace.",
6 | "author":"Joshua Nozzi",
7 | "icon":"scissors",
8 | "tags":"trim,whitespace,empty,space",
9 | }
10 | **/
11 |
12 | function main(state) {
13 |
14 | state.text = state.text.trim();
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/Boop/Boop/scripts/URLDecode.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"URL Decode",
5 | "description":"Decodes URL entities in your text.",
6 | "author":"Ivan",
7 | "icon":"link",
8 | "tags":"url,decode,convert"
9 | }
10 | **/
11 |
12 | function main(input) {
13 |
14 | input.text = decodeURIComponent(input.text)
15 |
16 | }
--------------------------------------------------------------------------------
/Boop/Boop/scripts/URLDefang.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Defang",
5 | "description":"Defangs dangerous URLs and other IOCs",
6 | "author":"Ross",
7 | "icon":"link",
8 | "tags":"defang,url,ioc"
9 | }
10 | **/
11 |
12 | function main(input) {
13 | url = input.text;
14 | url = url.replace(/\./g, "[.]");
15 | url = url.replace(/http/gi, "hXXp");
16 | url = url.replace(/:\/\//g, "[://]");
17 | input.text = url;
18 | }
--------------------------------------------------------------------------------
/Boop/Boop/scripts/URLEncode.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"URL Encode",
5 | "description":"Encodes URL entities in your text.",
6 | "author":"Ivan",
7 | "icon":"link",
8 | "tags":"url,encode,convert"
9 | }
10 | **/
11 |
12 | function main(input) {
13 |
14 | input.text = encodeURIComponent(input.text)
15 |
16 | }
--------------------------------------------------------------------------------
/Boop/Boop/scripts/URLEntitiesDecode.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"URL Entities Decode",
5 | "description":"URL Decodes all characters in your text.",
6 | "author":"luisfontes19",
7 | "icon":"percentage",
8 | "tags":"url,decode,full",
9 | "bias": -0.1
10 | }
11 | **/
12 |
13 |
14 | function fullUrlDecode(str) {
15 | var codes = str.split("%");
16 | var decoded = '';
17 |
18 | for (var i = 0; i < codes.length; i++) {
19 | decoded += String.fromCharCode(parseInt(codes[i], 16));
20 | }
21 |
22 | return decoded;
23 | }
24 |
25 | function main(state) {
26 | state.text = fullUrlDecode(state.text);
27 | }
28 |
--------------------------------------------------------------------------------
/Boop/Boop/scripts/URLEntitiesEncode.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"URL Entity Encode",
5 | "description":"URL Encodes all characters in your text.",
6 | "author":"luisfontes19",
7 | "icon":"percentage",
8 | "tags":"url,encode,full",
9 | "bias": -0.1
10 | }
11 | **/
12 |
13 |
14 | function fullUrlEncode(str) {
15 | var encoded = '';
16 |
17 | for (var i = 0; i < str.length; i++) {
18 | var h = parseInt(str.charCodeAt(i)).toString(16);
19 | encoded += '%' + h;
20 | }
21 |
22 | return encoded;
23 | }
24 |
25 | function main(state) {
26 | state.text = fullUrlEncode(state.text);
27 | }
28 |
--------------------------------------------------------------------------------
/Boop/Boop/scripts/URLRefang.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Refang",
5 | "description":"Removes defanging from dangerous URLs and other IOCs",
6 | "author":"Ross",
7 | "icon":"link",
8 | "tags":"refang,url,ioc"
9 | }
10 | **/
11 |
12 | function main(input) {
13 | url = input.text;
14 | url = url.replace(/\[\.\]/g, ".");
15 | url = url.replace(/hXXp/gi, "http");
16 | url = url.replace(/\[:\/\/\]/g, "://");
17 | input.text = url;
18 | }
--------------------------------------------------------------------------------
/Boop/Boop/scripts/Upcase.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Upcase",
5 | "description":"Converts your text to uppercase.",
6 | "author":"Dan2552",
7 | "icon":"type",
8 | "tags":"upcase,uppercase,capital,capitalize,capitalization"
9 | }
10 | **/
11 |
12 | function main(input) {
13 |
14 | input.text = input.text.toUpperCase();
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/Boop/Boop/scripts/YAMLtoJSON.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"YAML to JSON",
5 | "description":"Converts YAML to JSON.",
6 | "author":"Ivan",
7 | "icon":"metamorphose",
8 | "tags":"markup,convert"
9 | }
10 | **/
11 |
12 | const yaml = require('@boop/js-yaml')
13 |
14 | function main(input) {
15 |
16 | try {
17 | input.text = JSON.stringify(yaml.safeLoad(input.text), null, 2)
18 | }
19 | catch(error) {
20 | input.postError("Invalid YAML")
21 | }
22 |
23 | }
--------------------------------------------------------------------------------
/Boop/Boop/scripts/hex2rgb.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Hex to RGB",
5 | "description":"Convert color in hexadecimal to RGB.",
6 | "author":"Venkat",
7 | "icon":"color-wheel",
8 | "tags":"hex,color,rgb,convert"
9 | }
10 | **/
11 |
12 | function main(input) {
13 | R = hexToR(input.text);
14 | G = hexToG(input.text);
15 | B = hexToB(input.text);
16 |
17 | input.text = R.toString().concat(',').
18 | concat(G.toString()).concat(',').
19 | concat(B.toString());
20 | }
21 |
22 | function hexToR(h) { return parseInt((cutHex(h)).substring(0,2),16) }
23 | function hexToG(h) { return parseInt((cutHex(h)).substring(2,4),16) }
24 | function hexToB(h) { return parseInt((cutHex(h)).substring(4,6),16) }
25 | function cutHex(h) { return (h.charAt(0)=="#") ? h.substring(1,7) : h}
--------------------------------------------------------------------------------
/Boop/BoopTests/BoopTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BoopTests.swift
3 | // BoopTests
4 | //
5 | // Created by Ivan on 3/21/20.
6 | // Copyright © 2020 OKatBest. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | class BoopTests: XCTestCase {
12 |
13 | override func setUp() {
14 | // Put setup code here. This method is called before the invocation of each test method in the class.
15 | }
16 |
17 | override func tearDown() {
18 | // Put teardown code here. This method is called after the invocation of each test method in the class.
19 | }
20 |
21 | func testExample() {
22 | // This is an example of a functional test case.
23 | // Use XCTAssert and related functions to verify your tests produce the correct results.
24 | }
25 |
26 | func testPerformanceExample() {
27 | // This is an example of a performance test case.
28 | measure {
29 | // Put the code you want to measure the time of here.
30 | }
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/Boop/BoopTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Boop/BoopTests/ScriptManagerTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ScriptManagerTests.swift
3 | // BoopTests
4 | //
5 | // Created by Ivan on 3/21/20.
6 | // Copyright © 2020 OKatBest. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | class ScriptManagerTests: XCTestCase {
12 |
13 | let manager = ScriptManager()
14 |
15 |
16 | func testManager() {
17 | XCTAssertFalse(manager.scripts.isEmpty)
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/Boop/Documentation/ConvertingNodeModules.md:
--------------------------------------------------------------------------------
1 | # Converting Node Modules
2 |
3 | For more complex scripts it can be nice to leverage packages from NPM.
4 | However, most modules are not compatible with Boop. Luckily any script
5 | that can be in a browser can work with Boop!
6 |
7 | ## Crafting Your Script
8 |
9 | First write your script like any normal Boop Script
10 | except declare your main in the `global` namespace and dont add
11 | the declarative json document yet.
12 |
13 | ```
14 | const curlconverter = require('curlconverter');
15 |
16 | global.main = function(state) {
17 | try {
18 | state.text = curlconverter.toPython(state.text);
19 | } catch (error) {
20 | state.postError("Failed to Convert");
21 | }
22 | }
23 | ```
24 |
25 | Then use [Browserify](http://browserify.org/) to bundle
26 | the script.
27 |
28 | `browserify curlToPython_orig.js -o curlToPythonBundle.js`
29 |
30 | Boop is not a browser. That means you need to add a window var. Just add this line
31 | to the top of the script.
32 | ```
33 | const window = this;
34 | ```
35 |
36 | Finally, add the declarative JSON document
37 | (as described in [Custom Scripts](CustomScripts.md)) to the top, and you are done!
38 |
39 |
--------------------------------------------------------------------------------
/Boop/Documentation/Debugging.md:
--------------------------------------------------------------------------------
1 | # Debugging Scripts
2 |
3 |
4 | ___
5 | **Debugging is only available in dev builds, in Boop version 1.2.0 and above.
**
6 | ___
7 |
8 | ## Getting a dev build
9 |
10 | To use debugging tools, you need a dev build of Boop. This is a restriction of JavascriptCore (the thing that runs scripts) that I have not yet found a way to avoid.
11 |
12 | Since dev builds cannot be signed, they cannot be distributed and you need to make your own. For this, you'll need Xcode (free on the Mac App Store or the [Apple Developer Website](https://developer.apple.com)). Once you have it, follow these steps:
13 |
14 | - Download the Boop source code from Github or clone it locally using git. You can find a compressed version of the source code on the [releases page](https://github.com/IvanMathy/Boop/releases/).
15 |
16 | - In the source code, find and open `Boop.xcodeproj` with Xcode (it's in the `Boop` folder, surprisingly).
17 |
18 | - On the top left corner of the Xcode window, hit the play button. The dev build should start.
19 |
20 | ## Preparing the debugger
21 |
22 | To see your script in the debugger, you'll need to run that script at least once. It does not matter if it doesn't work, Boop just needs to know you want to use it.
23 |
24 | After running your script, open Safari (the web browser that comes bundled on macOS). If you've never used the developer tools, open Safari's preferences, go to the `Advanced` tab and enable `Show Develop menu in menu bar`.
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | ## Connecting the debugger
34 |
35 | Once set up, you should be able to see your script in the `Develop` menu, in the submenu with the same name as your computer (in the screenshot, it's `Ivan MKII` - yours will be different, most likely), under the `Boop Section`. Select the script you'd like to debug.
36 |
37 |
38 |
39 |
40 |
41 | If you see `No Inspectable Applications`, please double check that:
42 |
43 | - Boop is running
44 | - It is a dev build
45 | - You used your script at least once
46 |
47 | ## Using the debugger
48 |
49 | The debugger is the same as the built-in one from Safari, and works just like any in-browser inspector. The central panel shows the source of your script. *Editing it there will not affect the script within Boop, and any change will be lost.*
50 |
51 |
52 |
53 |
54 |
55 |
56 | ## Modules
57 |
58 | Modules are show as separate files in the left panel of the debugger. You'll notice that the module's source will have some added code above and below, surrounded with tags like this:
59 |
60 | ```javascript
61 | /***********************************
62 | * Start of Boop's wrapper *
63 | ***********************************/
64 | ```
65 |
66 | This is how Boop makes sure modules are safe and compatible. The wrapper is added at runtime. It ain't pretty but it gets the job done.
67 |
68 |
69 |
70 |
71 |
72 |
73 | ## Console
74 |
75 | When the debugger is active, the `console` object gets enabled. You can use the bottom panel or the `Console` tab to read the output and use the interactive prompt.
76 |
77 |
78 |
79 |
80 |
81 | Anything posted to the console prior to opening the debugger is recorded and will be displayed when you open it. Therefore, you don't need to rush to open the debugger to see initialization messages for example.
82 |
83 | Please note that `console` is not available in non-debug builds, and using it will cause your script to throw an exception.
84 |
85 | ## Breakpoints
86 |
87 | You can use breakpoints in the debugger, just like you would in any programming environment. The best way to do that is to place it in somewhere called by the `main()` function.
88 |
89 |
90 |
91 |
92 |
93 | When a breakpoint is active, you can see the scope in the right panel, step over/into in the left panel, or interact with the console in the bottom panel.
94 |
95 | While the breakpoint is active, Boop will become unresponsive. To make Boop usable again, continue the script execution by pressing the play button at the top left of the debugger.
96 |
97 | ## Reloading scripts
98 |
99 | When reloading scripts within Boop, the debugger **will not** automatically reload. If you want to check a new version of your script, close the debugger, run the scripts again for it to register, then re-open the debugger.
100 |
101 | Leaving the debugger open will not release the previous version of the script, and therefore duplicates will show in the debugging menu.
102 |
103 | When reopening previously opened scripts (even from a previous development session), the debugger will remember any previously applied breakpoint. Keep that in mind when debugging a module, as it's easy to forget you had a breakpoint in there.
104 |
105 |
106 | ## Final wisdom
107 |
108 | Just like any software development environment, there is inherent danger in using those tools if you don't know what you're doing. Use at your own risk, avoid clicking stuff you don't know, and be nice to people.
109 |
--------------------------------------------------------------------------------
/Boop/Documentation/Images/UI.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IvanMathy/Boop/5cf6914381f6ed6ef004c3a7d470f241a248fcb6/Boop/Documentation/Images/UI.png
--------------------------------------------------------------------------------
/Boop/Documentation/Images/breakpoints.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IvanMathy/Boop/5cf6914381f6ed6ef004c3a7d470f241a248fcb6/Boop/Documentation/Images/breakpoints.png
--------------------------------------------------------------------------------
/Boop/Documentation/Images/console.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IvanMathy/Boop/5cf6914381f6ed6ef004c3a7d470f241a248fcb6/Boop/Documentation/Images/console.png
--------------------------------------------------------------------------------
/Boop/Documentation/Images/debugger.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IvanMathy/Boop/5cf6914381f6ed6ef004c3a7d470f241a248fcb6/Boop/Documentation/Images/debugger.png
--------------------------------------------------------------------------------
/Boop/Documentation/Images/developMenu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IvanMathy/Boop/5cf6914381f6ed6ef004c3a7d470f241a248fcb6/Boop/Documentation/Images/developMenu.png
--------------------------------------------------------------------------------
/Boop/Documentation/Images/modules.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IvanMathy/Boop/5cf6914381f6ed6ef004c3a7d470f241a248fcb6/Boop/Documentation/Images/modules.png
--------------------------------------------------------------------------------
/Boop/Documentation/Images/safari.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IvanMathy/Boop/5cf6914381f6ed6ef004c3a7d470f241a248fcb6/Boop/Documentation/Images/safari.png
--------------------------------------------------------------------------------
/Boop/Documentation/Modules.md:
--------------------------------------------------------------------------------
1 | # Modules
2 |
3 |
4 |
5 | ___
6 | *Modules are available in Boop version 1.2.0 and above.
*
7 | ___
8 |
9 | Just to be very clear before we get started: **Boop does not support commonJS/node/ES6 modules**. It has a custom import system that may or may not be compatible with some existing modules.
10 |
11 | ## Importing modules
12 |
13 | To import a module, use the require function. It'll will return the contents of `module.exports`:
14 |
15 | ```javascript
16 | const test = require('lib/testLib')
17 | const test = require('modules/otherLib.js')
18 | ```
19 |
20 | If you do not include a `.js` extension, the system will add one automatically.
21 |
22 | You can also use destructuring to only get a single function out:
23 |
24 | ```javascript
25 | const { base64decode } = require('lib/base64')
26 | // or
27 |
28 | const { base64encode } = require('lib/base64')
29 | ```
30 |
31 | ## Creating modules
32 |
33 | Modules are run in a pseudo-sandbox, in somewhat similar (but not equal) way to CommonJS. The sandbox contains a `module` object with an `exports` property. Whatever that property contains is what will be returned by the require function.
34 |
35 | ```javascript
36 | // testModule.js
37 |
38 | module.exports = "Hello World"
39 | ```
40 | ```javascript
41 | // script.js
42 | const greeting = require('testModule.js')
43 | ```
44 |
45 | The above example is essentially the same as:
46 |
47 | ```javascript
48 | // script.js
49 | const greeting = "Hello World"
50 | ```
51 |
52 | Module imports can also be nested, though you should probably avoid that.
53 |
54 | ```javascript
55 | // lib/deepModule.js
56 |
57 | module.exports = "Hello World"
58 | ```
59 |
60 | ```javascript
61 | // lib/shallowModule.js
62 | const greeting = require('lib/deepModule')
63 |
64 | module.exports = greeting
65 | ```
66 |
67 | ```javascript
68 | // script.js
69 | const greeting = require('lib/shallowModule')
70 | ```
71 |
72 | When using nested requires, the module name is based on the path of your script, not of the current module.
73 |
74 | ## Built in modules
75 |
76 | Boop ships with a couple of modules, with the prefix `@boop/`, which you can use in your own scripts.
77 |
78 | | Module name | Description |
79 | | ------------------ | ------------- |
80 | | `@boop/base64` | Base 64 encoding and decoding |
81 | | `@boop/lodash.boop`| Subset of lodash string functions (see below) |
82 | | `@boop/he` | HTML entities encoder/decoder |
83 | | `@boop/vkBeautify` | XML, CSS, and SQL formatter and minifier |
84 | | `@boop/js-yaml` | Parses and Stringifies YAML objects |
85 | | `@boop/hashes` | Common crypto hashes |
86 |
87 | If you need a new module that you think could be used by more scripts, feel free to open a PR adding more functionality.
88 |
89 | #### Lodash
90 |
91 | The built in lodash module was created with the following command and only includes a few functions and their direct dependencies:
92 |
93 | ```bash
94 | $ lodash include=camelCase,deburr,escapeRegExp,kebabCase,snakeCase,startCase,size
95 | ```
96 |
97 | If you'd like to add more functions, feel free to rebuild with additional parameters and submit a PR.
98 |
--------------------------------------------------------------------------------
/Boop/Documentation/Readme.md:
--------------------------------------------------------------------------------
1 | # Boop
2 |
3 | Hey there! Thanks for trying out Boop. This documentation should hopefully help you understand how it works, how you can extend it, and how you can contribute back.
4 |
5 | ## Child Pages
6 |
7 | - [Custom Scripts](CustomScripts.md)
8 | - [Modules](Modules.md)
9 | - [Debugging Scripts](Debugging.md)
10 | - [Converting Node Modules](ConvertingNodeModules.md)
11 |
12 | ## Getting Boop
13 |
14 | You can download Boop from the Mac App Store, or from the [Releases page on GitHub](https://github.com/IvanMathy/Boop/releases). If you'd like to roll your own, you can clone the repository and follow the build instructions in the README.
15 |
16 | ## Using Boop
17 |
18 | Boop is pretty easy to use: Open it, paste some text, run some scripts, optionally copy the text out.
19 |
20 | To run scripts, simply open the script picker by pressing `⌘B` or in the top menu under `Scripts > Open Picker`.
21 |
22 | From the script picker, start typing to search for a script. You can then press `Enter ⏎` to pick the first script, or use the arrow keys to select another one.
23 |
24 | You can run the last script again by pressing `⇧⌘B` or from the option in the scripts menu.
25 |
26 | To start over, you can clear the editor by pressing `⌘N`.
27 |
28 | If you are developing scripts, you can reload all the script by pressing `⇧⌘R` or from the script menu as well.
29 |
30 | ## Questions
31 |
32 | ### Can I see a list of all scripts?
33 |
34 | Yes! Simply open the script picker and search for `*`.
35 |
36 | ### Why can't I open/Save a file?
37 |
38 | Because that's not the goal of Boop. It's not really an editor, more of an unstructured limbo for your plain text pasted content.
39 |
40 | ### Where can I find more scripts?
41 |
42 | You can find more functions in the [Boop Script Repository](https://github.com/IvanMathy/Boop/tree/main/Scripts). It contains scripts suggested by the community that are not in the built-in script library. You can go there to find new functionality, or suggest your own!
43 |
44 | ### Can I make my own scripts?
45 |
46 | Yes! Simply follow the instruction in the [Custom Scripts page](CustomScripts.md) to know how to get started.
47 |
48 | ### Does Boop collect data on me?
49 |
50 | No. The only time Boop communicates outside of itself is to check whether a new version is available. This is done by fetching a static .json file, with no additional data passed along. If you downloaded Boop through the Mac App Store, it's possible that standard data and/or crash reports get sent back to Apple and shared with me if you enabled App Analytics sharing, though I have not seen that happen yet.
51 |
52 | ### How can I report a problem?
53 |
54 | The best way to do that is to [file an issue on GitHub](https://github.com/IvanMathy/Boop/issues/new). Otherwise, you can [talk to me on Twitter](https://twitter.com/OKatBest), as long as you're nice.
55 |
56 | ### How is Boop built?
57 |
58 | Boop is mostly built using a custom fork of [SavannaKit](https://github.com/IvanMathy/savannakit), originally created by [Louis D'hauwe](http://twitter.com/LouisDhauwe). The search is powered by a custom fork of [Fuse-swift](https://github.com/IvanMathy/fuse-swift). The rest of Boop is simply built in Swift, besides scripts which are Javascript. Go ahead and open some of them to check their license!
59 |
60 | ### Do I have to say "Boop" out loud when I press ⌘+B?
61 |
62 | Yes.
63 |
--------------------------------------------------------------------------------
/DOING.md:
--------------------------------------------------------------------------------
1 | # DOING.md
2 |
3 | This document, inspired by [this tweet](https://twitter.com/drwave/status/1564063331341590529), contains the currently in progress or planned features for Boop.
4 |
5 | This document was last updated *August, 30th 2022*. Work may be happening in other repositories or branches making this one appear stale, but Boop is still very much an active project.
6 |
7 | ## In progress
8 |
9 | ### Multi Cursor Editing
10 |
11 | The ability to have more than one active cursor to type and edit content.
12 |
13 | ### Split Scripts
14 |
15 | Separating scripts from the main Boop Github repository, into a dedicated one.
16 |
17 | ## Planned
18 |
19 | ### Color Settings
20 |
21 | The ability to customize colors beyond light and dark mode. Maybe themes?
22 |
23 | ### Font Settings
24 |
25 | The ability to change font, and more importantly font-size.
26 |
27 | ## Not Planned
28 |
29 | ### Load / Save
30 |
31 | There is no plan to support files within Boop.
32 |
33 | ### Tabs
34 |
35 | There is no plan to support tabs or multiple windows within Boop.
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Ivan Mathy
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Boop.
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Website • Download from GitHub • Get on the Mac App Store
17 | Documentation • Find more scripts
18 |
19 |
20 | ### How to get Boop
21 |
22 | There are four ways to get Boop. Your best bet is either to
23 |
24 | - Download from GitHub releases or
25 | - Download on the Mac App Store
.
26 |
27 | You can also build it from source, or get it from Homebrew, although that is not officially supported.
28 |
29 | ### How to build from source
30 |
31 | If you're just trying to get Boop, building from source might not be your best bet. Developing new scripts does not require building from source.
32 |
33 | - Clone or download a copy of the repository
34 | - Open `Boop/Boop.xcodeproj`
35 | - Press play
36 |
37 |
38 | ### Documentation
39 |
40 | - [Documentation](Boop/Documentation/Readme.md)
41 | - [Custom scripts](Boop/Documentation/CustomScripts.md)
42 |
--------------------------------------------------------------------------------
/Scripts/CSVtoJSONheaderless.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"CSV to JSON (headerless)",
5 | "description":"Converts comma-separated, headerless tables to JSON.",
6 | "author":"Flare576",
7 | "icon":"table",
8 | "tags":"table,convert",
9 | "bias": -0.2
10 | }
11 | **/
12 | const Papa = require('@boop/papaparse.js');
13 |
14 | function main(state) {
15 | try {
16 | const { data } = Papa.parse(state.text, { header:false });
17 | state.text = JSON.stringify(data, null, 2);
18 | }
19 | catch(error) {
20 | state.text = error;
21 | state.postError("Invalid CSV")
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Scripts/CalculateSize.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api": 1,
4 | "name": "Calculate Size (Bytes)",
5 | "description": "Calculates size of text in Bytes",
6 | "author": "zzz",
7 | "icon": "counter",
8 | "tags": "calc,size,bytes,storage"
9 | }
10 | **/
11 |
12 | //From https://stackoverflow.com/a/12206089
13 | function getUTF8Length(s) {
14 | var len = 0;
15 | for (var i = 0; i < s.length; i++) {
16 | var code = s.charCodeAt(i);
17 | if (code <= 0x7f) {
18 | len += 1;
19 | } else if (code <= 0x7ff) {
20 | len += 2;
21 | } else if (code >= 0xd800 && code <= 0xdfff) {
22 | // Surrogate pair: These take 4 bytes in UTF-8 and 2 chars in UCS-2
23 | // (Assume next char is the other [valid] half and just skip it)
24 | len += 4; i++;
25 | } else if (code < 0xffff) {
26 | len += 3;
27 | } else {
28 | len += 4;
29 | }
30 | }
31 | return len;
32 | }
33 |
34 | function main(input) {
35 | let bytes = getUTF8Length(input.text);
36 | if (bytes > 1000000)
37 | {
38 | bytes /= 1000000;
39 | input.postInfo(`${bytes.toFixed(2)} Mb`)
40 | }
41 | if (bytes > 100000) {
42 | bytes /= 1000;
43 | input.postInfo(`${bytes.toFixed(2)} Kb`)
44 | }
45 | else {
46 | input.postInfo(`${bytes} bytes`)
47 | }
48 | }
--------------------------------------------------------------------------------
/Scripts/CreateProjectGlossaryMarkdown.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Create Project Glossary Markdown File",
5 | "description":"Type 'help' and run this script for instructions.",
6 | "author":"Terry L. Lewis",
7 | "icon":"colosseum",
8 | "tags":"markdown,glossary",
9 | "bias":0.0
10 | }
11 | **/
12 |
13 |
14 | ///
15 | /// Generates a Markdown Glossary file for the project specified in the input parameters.
16 | /// Run the script with no input text to create the input JSON required by the glossary generator.
17 | /// Fill in the required values, and re-run the Boop Script to generate the glossary.
18 | ///
19 | function main(state) {
20 |
21 | try {
22 | var options = {};
23 | if (state.text.trim().toLowerCase() === "help"){
24 | state.fullText = getHelpText();
25 | } else if (state.fullText.trim() === ""){
26 | state.fullText = JSON.stringify(getDefaultParameters());
27 | } else {
28 | options = JSON.parse(state.text);
29 |
30 | var indexEntries = "0ABCDEFGHIJKLMNOPQRSTUVWXYZ";
31 | var header = getHeader();
32 | var sectionTemplate = getSectionTemplate();
33 | var sampleEntries = getSampleEntries();
34 |
35 | var index = "\r\n";
36 | var body = "";
37 | for (var x = 0; x < indexEntries.length; x++)
38 | {
39 | var c = indexEntries.charAt(x);
40 |
41 | // Build Index
42 | index = index + "[" + c + "](#" + c.toLowerCase() + ") "
43 | if (c === "H" || c === "Q" || c === "Z") index = index + "\r\n";
44 |
45 | // Build Sections
46 | var section = sectionTemplate.replace("{idx}", c)
47 | if (c === "E" && options.includeSamples){
48 | section = section.replace("{samples}", sampleEntries);
49 | } else {
50 | section = section.replace("{samples}", "");
51 | }
52 | body = body + section
53 | }
54 |
55 | // Put it all together
56 | var glossary = header + index + body;
57 |
58 | state.fullText = glossary.replace("{projectName}", options.projectName);
59 | }
60 | }
61 | catch(error) {
62 | options = getDefaultParameters();
63 | options.error = error.toString();
64 | state.fullText = JSON.stringify(options);
65 | //state.text = error.toString()
66 | state.postError(error.toString())
67 | }
68 |
69 | }
70 |
71 | function getDefaultParameters(){
72 | return {
73 | projectName: "Project Name",
74 | includeSamples: false
75 | }
76 | }
77 |
78 | function getHeader(){
79 | return `
80 | # {projectName}
81 | ## Glossary Of Terms
82 | `.trim()
83 | }
84 |
85 | function getSectionTemplate(){
86 | return `
87 | ## {idx}
88 | {samples}
89 | [Back to Top](#glossary-of-terms)
90 |
91 | ---
92 | `;
93 | }
94 |
95 | function getSampleEntries() {
96 | return `
97 |
98 | ### Example Entry
99 | This example provides a template for how glossary entries should be formatted.
100 |
101 | ### Example Entry 2
102 | Sample definition of Example Entry 2. See also [Example Entry](#example-entry).
103 |
104 | `;
105 | }
106 |
107 | function getHelpText(){
108 | return `
109 | Create Project Glossary Markdown File
110 | =====================================
111 |
112 | This script takes a JSON input string and reads its
113 | properties to produce an empty Glossary file. Use this
114 | when starting a new project to ensure that everyone on
115 | the team knows the proper definitions of the jargon used.
116 |
117 | Running the script without an input string generates the
118 | required JSON structure.
119 |
120 | You can delete all but the JSON below and run the script
121 | to see example results.
122 |
123 | Input structure:
124 |
125 | {
126 | "projectName" : "--Project Name Here--",
127 | "includeSamples" : false
128 | }
129 |
130 | The "includeSamples" parameter is optional and defaults to [false].
131 | `;
132 | }
--------------------------------------------------------------------------------
/Scripts/DIGI2ASCII.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api": 1,
4 | "name": "Digi to ASCII",
5 | "description": "Digi to ASCII",
6 | "author": "Joseph Ng Rong En",
7 | "icon": "dice",
8 | "tags": "ascii,digi"
9 | }
10 | **/
11 |
12 | function digi2a(str) {
13 | var split = str.split(/[ ,]+/);
14 | var arr = [];
15 | for (var i = 0, l = split.length; i < l; i ++) {
16 | var ascii = String.fromCharCode(split[i]);
17 | arr.push(ascii);
18 | }
19 | return arr.join('');
20 | }
21 |
22 | function main(input) {
23 | input.text = digi2a(input.text);
24 | }
--------------------------------------------------------------------------------
/Scripts/FromUnicode.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | {
4 | "api":1,
5 | "name":"From string from unicode scaped",
6 | "description":"Returns a readable string from the unicode scaped string (js format)",
7 | "author":"luisfontes19",
8 | "icon":"broom",
9 | "tags":"string,normalize,convert,readable,unicode"
10 | }
11 | **/
12 |
13 | function main(state) {
14 | state.text = fromUnicode(state.text);
15 | }
16 |
17 | function fromUnicode(str) {
18 | return str.split("\\u").map(u => {
19 | return String.fromCharCode(parseInt(u, 16));
20 | }).join("");
21 | }
--------------------------------------------------------------------------------
/Scripts/JoinLines.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Join Lines",
5 | "description":"Joins all lines without any delimiter.",
6 | "author":"riesentoaster",
7 | "icon":"collapse",
8 | "tags":"join"
9 | }
10 | **/
11 |
12 | function main(input) {
13 | input.text = input.text.replace(/\n/g, '');
14 | }
15 |
--------------------------------------------------------------------------------
/Scripts/JoinLinesWithComma.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Join Lines With Comma",
5 | "description":"Joins all lines with a comma.",
6 | "author":"riesentoaster",
7 | "icon":"collapse",
8 | "tags":"join, comma",
9 | "bias": -0.1
10 | }
11 | **/
12 |
13 | function main(input) {
14 | input.text = input.text.replace(/\n/g, ',');
15 | }
16 |
--------------------------------------------------------------------------------
/Scripts/JoinLinesWithSpace.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Join Lines With Space",
5 | "description":"Joins all lines with a space",
6 | "author":"riesentoaster",
7 | "icon":"collapse",
8 | "tags":"join, space",
9 | "bias": -0.1
10 | }
11 | **/
12 |
13 | function main(input) {
14 | input.text = input.text.replace(/\n/g, ' ');
15 | }
16 |
--------------------------------------------------------------------------------
/Scripts/JsObjectToJSON.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"JS Object to JSON",
5 | "description":"Converts a javascript object to JSON format",
6 | "author":"luisfontes19",
7 | "icon":"HTML",
8 | "tags":"json,js,object,convert",
9 | "bias": -0.1
10 | }
11 | **/
12 |
13 | function main(state) {
14 |
15 | try {
16 | const data = state.text;
17 | eval("parsed = " + data);
18 | state.text = JSON.stringify(parsed);
19 | }
20 | catch (ex) {
21 | state.postError(ex.message);
22 | }
23 | }
--------------------------------------------------------------------------------
/Scripts/LineComparer.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Line compare",
5 | "description":"Check if existing lines have all the same content",
6 | "author":"Luis Fontes",
7 | "icon":"type",
8 | "tags":"string,match,text,compare,line",
9 | "bias": -0.1
10 | }
11 | **/
12 |
13 | function main(state) {
14 |
15 | const lines = state.text.split(/\n/);
16 | const first = lines[0];
17 | const differentLines = [];
18 |
19 | for (let i = 1; i < lines.length; i++) {
20 | const line = lines[i];
21 |
22 | if (first !== line) differentLines.push(i + 1);
23 | }
24 |
25 | if (differentLines.length === 0)
26 | state.postInfo('Lines are equal')
27 | else if (differentLines.length === 1)
28 | state.postError(`Line ${differentLines[0]} is not equal to the line 1`);
29 | else
30 | state.postError(`Lines [${differentLines.join(", ")}] are not equal to line 1`);
31 | }
--------------------------------------------------------------------------------
/Scripts/NewBoopScript.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"New Boop Script",
5 | "description":"Returns a basic Boop script.",
6 | "author":"tlewis",
7 | "icon":"quote",
8 | "tags":"boop,state,script,debug,new,create"
9 | }
10 | **/
11 |
12 | function main(state) {
13 | try {
14 | state.text = script
15 | }
16 | catch(error) {
17 | state.postError("Something strange happened here...")
18 | }
19 | }
20 |
21 | var script = `
22 | /**
23 | {
24 | "api":1,
25 | "name":"New Boop Script",
26 | "description":"What does your script do?",
27 | "author":"Whooooooo are you?",
28 | "icon":"broom",
29 | "tags":"place,tags,here",
30 | "bias":0.0
31 | }
32 | **/
33 |
34 | function main(state) {
35 | try {
36 |
37 | /*
38 | The 'state' object has three properties to deal with text: text, fullText, and selection.
39 |
40 | state.fullText will contain or set the entire string from the Boop editor, regardless of whether a selection is made or not.
41 | state.selection will contain or set the currently selected text, one at a time if more that one selection exists.
42 | state.text will behave like selection if there is one or more selected piece of text, otherwise it will behave like fullText.
43 | */
44 |
45 | state.fullText = state.selection; // Remove all but selected text
46 | }
47 | catch(error) {
48 | state.postError("Explain what went wrong here...")
49 | }
50 |
51 | }
52 | `
53 |
--------------------------------------------------------------------------------
/Scripts/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Additional scripts
3 |
4 | This folder contains useful scripts not included in the default Boop library you might want to download.
5 |
6 | ## Installing new scripts
7 |
8 | To install a new script, simply download the .js file and place it into the same folder as your custom scripts. If Boop is already open, reload scripts from the `Scripts` menu.
9 |
10 | ## Contributing
11 |
12 | Made something useful? Think of a way to improve an existing script? Feel free to open a pull request or a new issue on GitHub!
--------------------------------------------------------------------------------
/Scripts/ShuffleCharacters.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api": 1,
4 | "name": "Shuffle characters",
5 | "description": "Shuffles characters randomly",
6 | "author": "Christian Petersen",
7 | "icon": "dice",
8 | "tags": "shuffle,random,character,char"
9 | }
10 | **/
11 | function shuffleString(string) {
12 | const chars = string.split("");
13 |
14 | for (let i = chars.length - 1; i > 0; i--) {
15 | const j = Math.floor(Math.random() * (i + 1));
16 | [chars[i], chars[j]] = [chars[j], chars[i]];
17 | }
18 |
19 | return chars.join("");
20 | }
21 |
22 | function main(input) {
23 | input.text = shuffleString(input.text);
24 | }
25 |
--------------------------------------------------------------------------------
/Scripts/TimeToSecond.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Time to seconds",
5 | "description":"Convert hh:mm:ss to seconds",
6 | "author":"PeteChu",
7 | "icon":"watch",
8 | "tags":"transform,convert"
9 | }
10 | **/
11 |
12 | function timeToSeconds(durationText) {
13 | const [hours = 0, minutes = 0, seconds = 0] = String(durationText).split(':');
14 | return Number(hours) * 3600 + Number(minutes) * 60 + Number(seconds);
15 | }
16 |
17 | function main(input) {
18 | input.insert('\n\n' + timeToSeconds(input.text));
19 | }
--------------------------------------------------------------------------------
/Scripts/TrimEnd.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Trim End",
5 | "description":"Trims trailing whitespace.",
6 | "author":"Joshua Nozzi",
7 | "icon":"scissors",
8 | "tags":"trim,end,right,trailing,whitespace,empty,space",
9 | }
10 | **/
11 |
12 | function main(state) {
13 |
14 | state.text = state.text.trimEnd();
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/Scripts/TrimStart.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Trim Start",
5 | "description":"Trims leading whitespace.",
6 | "author":"Joshua Nozzi",
7 | "icon":"scissors",
8 | "tags":"trim,start,left,leading,beginning,whitespace,empty,space",
9 | }
10 | **/
11 |
12 | function main(state) {
13 |
14 | state.text = state.text.trimStart();
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/Scripts/Wadsworth.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Wadsworth Constant",
5 | "description":"first 30% of your text.",
6 | "author":"Ivan",
7 | "icon":"scissors",
8 | "tags":"snap"
9 | }
10 | **/
11 |
12 |
13 | function main(state) {
14 | var all = state.text.split(" ")
15 | all.splice(0, Math.ceil(all.length * 0.3))
16 | state.text = all.join(" ")
17 | }
18 |
--------------------------------------------------------------------------------
/Scripts/WkbToWkt.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Well-Known Binary to Text",
5 | "description":"Converts your hex encoded WKB (any endian) to WKB, wkb2wkt",
6 | "author":"Mikael Brassman (Twitter: @spoike)",
7 | "icon":"globe",
8 | "tags":"wkb,convert,wkt,binary,hex,wkb2wkt"
9 | }
10 | **/
11 |
12 | function main(input) {
13 | try {
14 | input.text = input.text.replace(/0[01]0[1-6][0-9a-f]+/g, convertHex);
15 | } catch (err) {
16 | input.postError(err);
17 | return;
18 | }
19 | }
20 |
21 | function convertHex(hexStr) {
22 | let pos = 0;
23 | let output = "";
24 | while (pos < hexStr.length) {
25 | const littleEndian = getIsLittleEndian(hexStr, pos);
26 | pos += 2;
27 | const geoType = getUint32(hexStr, pos, littleEndian);
28 | pos += 8;
29 | switch (geoType) {
30 | case 1: {
31 | // POINT
32 | const point = getPoint(hexStr, pos, littleEndian);
33 | pos += 32;
34 | output += `POINT (${point})`;
35 | break;
36 | }
37 | case 2: {
38 | // LINESTRING
39 | const length = getUint32(hexStr, pos, littleEndian);
40 | pos += 8;
41 | const points = [];
42 | for (let i = 0; i < length; i++) {
43 | points.push(getPoint(hexStr, pos, littleEndian));
44 | pos += 32;
45 | }
46 | output += `LINESTRING (${points.join(", ")})`;
47 | break;
48 | }
49 | case 3: {
50 | // POLYGON
51 | const count = getUint32(hexStr, pos, littleEndian);
52 | pos += 8;
53 | const rings = [];
54 | for (let i = 0; i < count; i++) {
55 | const length = getUint32(hexStr, pos, littleEndian);
56 | pos += 8;
57 | const points = [];
58 | for (let j = 0; j < length; j++) {
59 | points.push(getPoint(hexStr, pos, littleEndian));
60 | pos += 32;
61 | }
62 | rings.push(points.join(", "));
63 | }
64 | output += `POLYGON (${rings.map(wrapParens).join(", ")})`;
65 | break;
66 | }
67 | case 4: {
68 | // MULTIPOINT
69 | const points = [];
70 | const count = getUint32(hexStr, pos, littleEndian);
71 | pos += 8;
72 | for (let i = 0; i < count; i++) {
73 | const innerLE = getIsLittleEndian(hexStr, pos);
74 | pos += 2 + 8;
75 | points.push(getPoint(hexStr, pos, innerLE));
76 | pos += 32;
77 | }
78 | output += `MULTIPOINT (${points.join(", ")})`;
79 | break;
80 | }
81 | case 5: {
82 | // MULTILINESTRING
83 | const lineStrings = [];
84 | const count = getUint32(hexStr, pos, littleEndian);
85 | pos += 8;
86 | for (let i = 0; i < count; i++) {
87 | const innerLE = getIsLittleEndian(hexStr, pos);
88 | pos += 2 + 8;
89 | const points = [];
90 | const length = getUint32(hexStr, pos, littleEndian);
91 | pos += 8;
92 | for (let j = 0; j < length; j++) {
93 | points.push(getPoint(hexStr, pos, innerLE));
94 | pos += 32;
95 | }
96 | lineStrings.push(points.join(", "));
97 | }
98 | output += `MULTILINESTRING (${lineStrings.map(wrapParens).join(", ")})`;
99 | break;
100 | }
101 | case 6: {
102 | // MULTIPOLYGON
103 | const polys = [];
104 | const polyCount = getUint32(hexStr, pos, littleEndian);
105 | pos += 8;
106 | for (let i = 0; i < polyCount; i++) {
107 | const innerLE = getIsLittleEndian(hexStr, pos);
108 | pos += 2 + 8;
109 | const rings = [];
110 | const ringCount = getUint32(hexStr, pos, innerLE);
111 | pos += 8;
112 | for (let j = 0; j < ringCount; j++) {
113 | const points = [];
114 | const pointCount = getUint32(hexStr, pos, innerLE);
115 | pos += 8;
116 | for (let k = 0; k < pointCount; k++) {
117 | points.push(getPoint(hexStr, pos, innerLE));
118 | pos += 32;
119 | }
120 | rings.push(points.join(", "));
121 | }
122 | polys.push(rings.map(wrapParens).join(", "));
123 | }
124 | output += `MULTIPOLYGON (${polys.map(wrapParens).join(", ")})`;
125 | break;
126 | }
127 | default:
128 | throw geoType + " is not supported";
129 | }
130 | }
131 | return output;
132 | }
133 |
134 | function wrapParens(el) {
135 | return `(${el})`;
136 | }
137 |
138 | function getIsLittleEndian(str, pos) {
139 | const byteString = str.substr(pos, 2);
140 | if (byteString === "00") {
141 | return false;
142 | } else if (byteString === "01") {
143 | return true;
144 | }
145 | throw byteString + " is unknown byte order";
146 | }
147 |
148 | function getPoint(str, pos, littleEndian) {
149 | const numbers = [];
150 | numbers.push(getDouble(str, pos, littleEndian));
151 | numbers.push(getDouble(str, pos + 16, littleEndian));
152 | return numbers.join(" ");
153 | }
154 |
155 | function getUint32(str, pos, littleEndian) {
156 | const view = new DataView(new ArrayBuffer(4));
157 | let data = str.substr(pos, 8).match(/../g);
158 | for (let i = 0; i < data.length; i++) {
159 | view.setUint8(i, parseInt(data[i], 16));
160 | }
161 | return view.getUint32(0, littleEndian);
162 | }
163 |
164 | function getDouble(str, pos, littleEndian) {
165 | const view = new DataView(new ArrayBuffer(8));
166 | let data = str.substr(pos, 16).match(/../g);
167 | for (let i = 0; i < data.length; i++) {
168 | view.setUint8(i, parseInt(data[i], 16));
169 | }
170 | return view.getFloat64(0, littleEndian);
171 | }
172 |
--------------------------------------------------------------------------------
/Scripts/WktToWkb.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Well-Known Text to Binary",
5 | "description":"Converts your WKT to little endian WKB (hex encoded), wkt2wkb",
6 | "author":"Mikael Brassman (Twitter: @spoike)",
7 | "icon":"globe",
8 | "tags":"wkb,convert,wkt,binary,little endian,hex,wkt2wkb"
9 | }
10 | **/
11 |
12 | const re = /(?:(?:MULTI)?POINT|(?:MULTI)?LINESTRING|(?:MULTI)?POLYGON)\s*\([()0-9\s,\.\s]+\)/g;
13 |
14 | function main(input) {
15 | try {
16 | input.text = input.text.replace(re, convert);
17 | } catch (err) {
18 | input.postError(err);
19 | return;
20 | }
21 | }
22 |
23 | function convert(text) {
24 | const littleEndian = true;
25 | let tokens = [];
26 | for (let i = 0; i < text.length; i++) {
27 | tokenize(tokens, text[i]);
28 | }
29 | tokens = tokens.filter(Boolean);
30 | let output = "";
31 | while (tokens.length > 0) {
32 | const token = tokens.shift();
33 | if (tokens.shift() !== "(") {
34 | throw token + "is missing (";
35 | }
36 | switch (token) {
37 | case "POINT":
38 | output += handlePoint(tokens, littleEndian);
39 | break;
40 | case "LINESTRING":
41 | output += handleLineString(tokens, littleEndian);
42 | break;
43 | case "POLYGON":
44 | output += handlePolygon(
45 | getParensSlice(tokens, "POLYGON", false),
46 | littleEndian
47 | );
48 | break;
49 | case "MULTIPOINT":
50 | output += handleMultipoint(tokens, littleEndian);
51 | break;
52 | case "MULTILINESTRING":
53 | output += handleMultilinestring(tokens, littleEndian);
54 | break;
55 | case "MULTIPOLYGON":
56 | output += handleMultipolygon(tokens, littleEndian);
57 | break;
58 | default:
59 | throw "Unrecognized token " + token;
60 | }
61 | if (tokens.shift() !== ")") {
62 | throw token + " is missing )";
63 | }
64 | }
65 | return output;
66 | }
67 |
68 | function tokenize(memo, char) {
69 | if (memo.length === 0) {
70 | memo.push(char);
71 | return;
72 | }
73 | if (/[A-Z0-9\.\-]/.test(char)) {
74 | memo[memo.length - 1] = memo[memo.length - 1] + char;
75 | } else if (/[()]/.test(char)) {
76 | memo.push(char);
77 | memo.push("");
78 | } else {
79 | memo.push("");
80 | }
81 | }
82 |
83 | function handlePoint(arr, littleEndian) {
84 | let out = toByteOrder(littleEndian) + toUint32(1, littleEndian);
85 | out += handleDouble(arr.shift(), littleEndian);
86 | out += handleDouble(arr.shift(), littleEndian);
87 | return out;
88 | }
89 |
90 | function handleLineString(arr, littleEndian) {
91 | let out = toByteOrder(littleEndian) + toUint32(2, littleEndian);
92 | const slice = getParensSlice(arr, "LINESTRING", true);
93 | const pairs = Math.floor(slice.length / 2);
94 | out += toUint32(pairs, littleEndian);
95 | for (const token of slice) {
96 | out += handleDouble(token, littleEndian);
97 | }
98 | return out;
99 | }
100 |
101 | function handlePolygon(rings, littleEndian) {
102 | let out = toByteOrder(littleEndian) + toUint32(3, littleEndian);
103 | out += toUint32(rings.length, littleEndian);
104 | for (let ring of rings) {
105 | out += handleRing(ring, littleEndian);
106 | }
107 | return out;
108 | }
109 |
110 | function handleMultipoint(arr, littleEndian) {
111 | let out = toByteOrder(littleEndian) + toUint32(4, littleEndian);
112 | const slice = getParensSlice(arr, "MULTIPOINT", true);
113 | const pairs = slice.length / 2;
114 | out += toUint32(pairs, littleEndian);
115 | for (let i = 0; i < slice.length; i = i + 2) {
116 | out += toByteOrder(littleEndian);
117 | out += toUint32(1, littleEndian);
118 | out += toDouble(slice[i], littleEndian);
119 | out += toDouble(slice[i + 1], littleEndian);
120 | }
121 | return out;
122 | }
123 |
124 | function handleMultilinestring(arr, littleEndian) {
125 | let out = toByteOrder(littleEndian) + toUint32(5, littleEndian);
126 | const slices = getParensSlice(arr, "MULTILINESTRING", false);
127 | out += toUint32(slices.length, littleEndian);
128 | for (let slice of slices) {
129 | const pairs = Math.floor(slice.length / 2);
130 | out +=
131 | toByteOrder(littleEndian) +
132 | toUint32(2, littleEndian) +
133 | toUint32(pairs, littleEndian);
134 | for (let token of slice) {
135 | out += toDouble(token, littleEndian);
136 | }
137 | }
138 | return out;
139 | }
140 |
141 | function handleMultipolygon(arr, littleEndian) {
142 | let out = toByteOrder(littleEndian) + toUint32(6, littleEndian);
143 | const polygons = getParensSlice(arr, "MULTIPOLYGON", false);
144 | out += toUint32(polygons.length, littleEndian);
145 | for (let polygon of polygons) {
146 | out += handlePolygon(polygon, littleEndian);
147 | }
148 | return out;
149 | }
150 |
151 | function handleRing(tokens, littleEndian) {
152 | let out = "";
153 | const pairs = Math.floor(tokens.length / 2);
154 | out += toUint32(pairs, littleEndian);
155 | for (let token of tokens) {
156 | out += handleDouble(token, littleEndian);
157 | }
158 | return out;
159 | }
160 |
161 | function getParensSlice(arr, type, flatten) {
162 | let slices = [];
163 | while (arr[0] === "(") {
164 | arr.shift(); // remove (
165 | const innerSlice = getParensSlice(arr, type, flatten);
166 | slices.push(flatten ? innerSlice.flat() : innerSlice);
167 | arr.shift(); // remove )
168 | }
169 | let seek = arr.findIndex((token) => /^\)$/.test(token));
170 | if (seek === -1) {
171 | throw type + " missing matching )";
172 | }
173 | if (seek > 0) {
174 | slices = slices.concat(arr.splice(0, seek));
175 | }
176 | return flatten ? slices.flat() : slices;
177 | }
178 |
179 | function handleDouble(token, littleEndian) {
180 | const number = parseFloat(token);
181 | if (isNaN(number)) {
182 | throw token + " is NaN";
183 | }
184 | return toDouble(number, littleEndian);
185 | }
186 |
187 | function toByteOrder(littleEndian) {
188 | return littleEndian ? "01" : "00";
189 | }
190 |
191 | function toUint32(number, littleEndian) {
192 | const view = new DataView(new ArrayBuffer(4));
193 | view.setUint32(0, number, littleEndian);
194 | return asHex(view, 4);
195 | }
196 |
197 | function toDouble(number, littleEndian) {
198 | const view = new DataView(new ArrayBuffer(8));
199 | view.setFloat64(0, number, littleEndian);
200 | return asHex(view, 8);
201 | }
202 |
203 | function getHex(i) {
204 | return ("00" + i.toString(16)).slice(-2);
205 | }
206 |
207 | function asHex(view, length) {
208 | return Array.apply(null, { length })
209 | .map((_, i) => getHex(view.getUint8(i)))
210 | .join("");
211 | }
212 |
--------------------------------------------------------------------------------
/Scripts/contrastingColor.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api": 1,
4 | "name": "Contrasting Color",
5 | "description": "Determine whether black or white contrasts better with the given color(s) (one per line).",
6 | "author": "Sunny Walker",
7 | "icon": "color-wheel",
8 | "tags": "contrast,color,wcag"
9 | }
10 | **/
11 |
12 | function main(input) {
13 | let lines = input.fullText.split("\n");
14 | let o = lines.map(c => betterColor(c));
15 | input.fullText = o.join("\n");
16 | }
17 |
18 | // convert #rrggbb into its integer r, g, b components
19 | const hex2Rgb = hex => {
20 | const rgb = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
21 | return rgb ? {
22 | r: parseInt(rgb[1], 16),
23 | g: parseInt(rgb[2], 16),
24 | b: parseInt(rgb[3], 16)
25 | } : null;
26 | };
27 |
28 | // calculate the luminance of a color
29 | const luminance = hex => {
30 | var rgb = hex2Rgb(hex);
31 | var a = [rgb.r, rgb.g, rgb.b].map(v => {
32 | v /= 255;
33 | return (v <= 0.03928) ? v / 12.92 : Math.pow(((v + 0.055) / 1.055), 2.4);
34 | });
35 | return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
36 | };
37 |
38 | // calculate the contrast ratio between two colors
39 | const contrast = (c1, c2) => {
40 | const l1 = luminance(c1) + .05;
41 | const l2 = luminance(c2) + .05;
42 | let ratio = l1 / l2;
43 | if (l2 > l1) {
44 | ratio = 1 / ratio;
45 | }
46 | return Math.floor(ratio * 100) / 100;
47 | };
48 |
49 | // convert #rbg to #rrggbb
50 | const normalizeHex = hex => hex.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i, '#$1$1$2$2$3$3');
51 |
52 | // determine the WCAG 2.0 contrast ratio level
53 | const wcagLevel = ratio => ratio >= 7 ? 'AAA' : ratio >= 4.5 ? 'AA' : 'fail';
54 |
55 | // determine the better contrasting color for a color
56 | const betterColor = hex => {
57 | const h = normalizeHex(hex);
58 | if (!h.match(/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i)) {
59 | return hex;
60 | }
61 | var w = contrast(h, '#ffffff');
62 | var b = contrast(h, '#000000');
63 | var r = Math.max(w, b);
64 | return hex + ' // contrasts best with ' + (w > b ? '#fff' : '#000') + ' with a ratio of ' + r + ' to 1; WCAG 2.0: ' + wcagLevel(r);
65 | };
66 |
--------------------------------------------------------------------------------
/Scripts/convertToMarkdownTable.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"Convert to pretty markdown table",
5 | "description":"Converts csv, tsv or markdown table into pretty markdown table format.",
6 | "author":"xshoji",
7 | "icon":"term",
8 | "tags":"csv,tsv,md,markdown"
9 | }
10 | **/
11 | function main(input) {
12 | input.text = convertToPrettyMarkdownTableFormat(input.text);
13 | }
14 |
15 | function convertToPrettyMarkdownTableFormat(input) {
16 | const list = input.trim().replace(/^(\r?\n)+$/g, "\n").split("\n").map(v => v.replace(/^\||\|$/g, ""));
17 | const delimiter = [`|`, `\t`, `","`, `,`].find(v => list[0].split(v).length > 1);
18 | if (delimiter === `|`) {
19 | // If input text is markdown table format, removes header separator.
20 | list.splice(1, 1);
21 | }
22 | const tableElements = list.map(record => record.split(delimiter).map(v => v.trim()));
23 | const calcBytes = (character) => {
24 | let length = 0;
25 | for (let i = 0; i < character.length; i++) {
26 | const c = character.charCodeAt(i);
27 | // Multibyte handling
28 | (c >= 0x0 && c < 0x81) || (c === 0xf8f0) || (c >= 0xff61 && c < 0xffa0) || (c >= 0xf8f1 && c < 0xf8f4) ? length += 1 : length += 2;
29 | }
30 | return length;
31 | };
32 | const columnMaxLengthList = tableElements[0].map((v, i) => i).reduce((map, columnIndex) => {
33 | let maxLength = 0;
34 | tableElements.forEach(record => maxLength < calcBytes(record[columnIndex]) ? maxLength = calcBytes(record[columnIndex]) : null);
35 | if (maxLength === 1) {
36 | // Avoids markdown header line becomes only ":" ( ":-" is correct. ).
37 | maxLength = 2;
38 | }
39 | map[columnIndex] = maxLength;
40 | return map;
41 | }, {})
42 | const formattedTableElements = tableElements.map(record => record.map((value, columnIndex) => value + "".padEnd(columnMaxLengthList[columnIndex] - calcBytes(value), " ")));
43 | const headerValues = formattedTableElements.shift();
44 | const tableLine = headerValues.map(v => "".padStart(calcBytes(v), "-").replace(/^./, ":"));
45 | formattedTableElements.unshift(tableLine);
46 | formattedTableElements.unshift(headerValues);
47 | return formattedTableElements.map(record => "| " + record.join(" | ") + " |").join("\n");
48 | }
49 |
--------------------------------------------------------------------------------
/Scripts/generateHashtag.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api": 1,
4 | "name": "Generate hashtag",
5 | "description": "Generate hashtag from a word or sentence",
6 | "author": "Armand Salle",
7 | "icon": "metamorphose",
8 | "tags": "hashtag,word"
9 | }
10 | **/
11 |
12 | function capitalize(str) {
13 | return str.charAt(0).toUpperCase() + str.slice(1);
14 | }
15 |
16 | function createHashtag(str) {
17 | if (str === "") {
18 | throw new Error("Invalid text :(");
19 | } else {
20 | const result = str.replace(/\n+/gm, " ");
21 | const text = result.replace(/[^A-Za-zÀ-ÖØ-öø-ÿ0-9\s]+/gm, " ");
22 |
23 | return "#" + text.toLowerCase().split(" ").map(capitalize).join("");
24 | }
25 | }
26 |
27 | function main(input) {
28 | try {
29 | const generatedHashatag = createHashtag(input.text);
30 | input.text = generatedHashatag;
31 | input.postInfo("Nice!");
32 | } catch (e) {
33 | input.postError(e.message);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Scripts/jsToPhp.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"JS To PHP",
5 | "description":"Convert JS Object or Array to PHP.",
6 | "author":"jtolj",
7 | "icon":"elephant",
8 | "tags":"js,php,convert"
9 | }
10 | **/
11 |
12 | function main(state) {
13 | const js = state.text.replace(/\n\n\/\/ Result:[\s\S]*$/, '');
14 | let output = '';
15 | try {
16 | const result = new Function(`return ${js}`)();
17 | output = convert(result) + ';';
18 | } catch (error) {
19 | state.postError(error.message);
20 | }
21 | state.text = js + "\n\n// Result:\n\n" + output;
22 | }
23 |
24 | const toPHP = function (value, indentation) {
25 | switch (typeof value) {
26 | case 'undefined':
27 | value = null;
28 | break;
29 | case 'object':
30 | if(value !== null) {
31 | value = convert(value, indentation + 1);
32 | }
33 | break;
34 | case 'string':
35 | value = value.replace(/"/g, '\\"');
36 | value = `"${value}"`;
37 | break;
38 | }
39 |
40 | return value;
41 | };
42 |
43 | const convert = function (result, indentation = 1) {
44 | const isArray = Array.isArray(result);
45 | let str = Object.keys(result).reduce((acc, key) => {
46 | const value = toPHP(result[key], indentation);
47 | acc += ' '.repeat(indentation * 4);
48 | acc += isArray ? value : `'${key}' => ${value}`;
49 | acc += ',\n';
50 | return acc;
51 | }, '');
52 | const endingIndentation = ' '.repeat((indentation - 1) * 4);
53 | return `[\n${str}${endingIndentation}]`;
54 | };
55 |
--------------------------------------------------------------------------------
/Scripts/listToHTMLList.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api": 1,
4 | "name": "List to HTML list",
5 | "description": "Turns comma separated list to HTML Lists",
6 | "author": "Christian Heilmann",
7 | "icon": "table",
8 | "tags": "HTML,Lists"
9 | }
10 | **/
11 |
12 | const listToHTML = (str) => {
13 | if (str.indexOf('') === -1) {
14 | let chunks = str.split(',');
15 | let out = `
16 | - ${chunks.join("
\n - ")}`;
17 | return out + "
\n
";
18 | } else {
19 | let chunks = str.split('- ');
20 | let out = [];
21 | chunks.forEach(c => {
22 | out.push(c.match(/[^<]*/));
23 | });
24 | out.shift();
25 | return out.join(',');
26 | }
27 | }
28 | function main(input) {
29 | input.text = listToHTML(input.text);
30 | }
31 |
--------------------------------------------------------------------------------
/Scripts/rgb2hex.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"RGB to Hex",
5 | "description":"Convert color in RGB to hexadecimal",
6 | "author":"luisfontes19",
7 | "icon":"color-wheel",
8 | "tags":"rgb,hex,convert,color"
9 | }
10 | **/
11 |
12 | function main(state) {
13 | const rgb = state.text;
14 | const rgbArray = rgb.includes(",") ? rgb.split(",") : rgb.split(" ");
15 |
16 | if (rgbArray.length !== 3) return state.postError("Invalid RGB format");
17 |
18 | let hex = "#";
19 |
20 | try {
21 | rgbArray.forEach(c => {
22 | hex += parseInt(c).toString(16);
23 | });
24 | }
25 | catch (error) {
26 | return state.postError("Invalid RGB value");;
27 | }
28 |
29 | state.text = hex.toUpperCase();
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/Scripts/toUnicode.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | {
4 | "api":1,
5 | "name":"To Unicode Escaped String",
6 | "description":"Converts a UTF8 string to unicode escape chars(js format)",
7 | "author":"luisfontes19",
8 | "icon":"broom",
9 | "tags":"string,unicode,convert,escape"
10 | }
11 | **/
12 |
13 | function main(state) {
14 | state.text = toUnicode(state.text);
15 | }
16 |
17 | function toUnicode(str) {
18 | return [...str].map(c => {
19 | let hex = c.charCodeAt(0).toString(16);
20 | if (hex.length == 2) hex = "00" + hex;
21 | return ("\\u" + hex).slice(-7);
22 | }).join("");
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/Scripts/toggleCamelHyphen.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api": 1,
4 | "name": "Toggle Camel and Hyphen",
5 | "description": "Turns camelCase to camel-case and vice versa",
6 | "author": "Christian Heilmann",
7 | "icon": "table",
8 | "tags": "camelcase,hyphencase,syntax,codestandards"
9 | }
10 | **/
11 |
12 | const toggleCamelHyphen = (str) => {
13 | let chunks = str.split(/\n/);
14 | chunks.forEach((c,k) => {
15 | if (c.indexOf('-') !== -1) {
16 | chunks[k] = c.replace(/\W+(.)/g, (x, chr) => {
17 | return chr.toUpperCase();
18 | });
19 | } else {
20 | chunks[k] = c
21 | .replace(/[^a-zA-Z0-9]+/g, '-')
22 | .replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')
23 | .replace(/([a-z])([A-Z])/g, '$1-$2')
24 | .replace(/([0-9])([^0-9])/g, '$1-$2')
25 | .replace(/([^0-9])([0-9])/g, '$1-$2')
26 | .replace(/-+/g, '-')
27 | .toLowerCase();
28 | }
29 | });
30 | return chunks.join("\n");
31 | }
32 |
33 | function main(input) {
34 | input.text = toggleCamelHyphen(input.text);
35 | }
36 |
--------------------------------------------------------------------------------
/Scripts/tsvToJson.js:
--------------------------------------------------------------------------------
1 | /**
2 | {
3 | "api":1,
4 | "name":"TSV to JSON",
5 | "description":"Converts TSV to JSON",
6 | "author":"Quddus George",
7 | "icon":"table",
8 | "tags":"tab, tsv, json, table"
9 | }
10 | **/
11 |
12 | //credit for tsv function: https://gist.github.com/iwek/7154706#gistcomment-3369283
13 |
14 | function main(state) {
15 | function tsvJSON(tsv) {
16 | return tsv
17 | .split("\n")
18 | .map((line) => line.split("\t"))
19 | .reduce((a, c, i, d) => {
20 | if (i) {
21 | const item = Object.fromEntries(c.map((val, j) => [d[0][j], val]));
22 | return a ? [...a, item] : [item];
23 | }
24 | }, []);
25 | }
26 | let json = JSON.stringify(tsvJSON(state.fullText));
27 | state.fullText = json;
28 | }
29 |
--------------------------------------------------------------------------------