asciibird/src/ascii.js

997 lines
34 KiB
JavaScript
Raw Normal View History

2021-08-04 03:21:06 +00:00
import LZString from 'lz-string';
import store from './store';
// 0 => 'white',
// 1 => 'black',
// 2 => 'navy',
// 3 => 'green',
// 4 => 'red',
// 5 => 'brown',
// 6 => 'purple',
// 7 => 'olive',
// 8 => 'yellow', # dark yellow
// 9 => 'lime', # ltgreen
// 10 => 'teal',
// 11 => 'cyan',
// 12 => 'blue', # ltblue,
// 13 => 'fuchsia', # pink
// 14 => 'grey',
// 15 => 'lightgrey',
export const mircColours99 = [
'rgb(255,255,255)',
'rgb(0,0,0)',
'rgb(0,0,127)',
'rgb(0,147,0)',
'rgb(255,0,0)',
'rgb(127,0,0)',
'rgb(156,0,156)',
'rgb(252,127,0)',
'rgb(255,255,0)',
'rgb(0,252,0)',
'rgb(0,147,147)',
'rgb(0,255,255)',
'rgb(0,0,252)',
'rgb(255,0,255)',
'rgb(127,127,127)',
'rgb(210,210,210)',
"#470000",
"#472100",
"#474700",
"#324700",
"#004700",
"#00472c",
"#004747",
"#002747",
"#000047",
"#2e0047",
"#470047",
"#47002a",
"#740000",
"#743a00",
"#747400",
"#517400",
"#007400",
"#007449",
"#007474",
"#004074",
"#000074",
"#4b0074",
"#740074",
"#740045",
"#b50000",
"#b56300",
"#b5b500",
"#7db500",
"#00b500",
"#00b571",
"#00b5b5",
"#0063b5",
"#0000b5",
"#7500b5",
"#b500b5",
"#b5006b",
"#ff0000",
"#ff8c00",
"#ffff00",
"#b2ff00",
"#00ff00",
"#00ffa0",
"#00ffff",
"#008cff",
"#0000ff",
"#a500ff",
"#ff00ff",
"#ff0098",
"#ff5959",
"#ffb459",
"#ffff71",
"#cfff60",
"#6fff6f",
"#65ffc9",
"#6dffff",
"#59b4ff",
"#5959ff",
"#c459ff",
"#ff66ff",
"#ff59bc",
"#ff9c9c",
"#ffd39c",
"#ffff9c",
"#e2ff9c",
"#9cff9c",
"#9cffdb",
"#9cffff",
"#9cd3ff",
"#9c9cff",
"#dc9cff",
"#ff9cff",
"#ff94d3",
"#000000",
"#131313",
"#282828",
"#363636",
"#4d4d4d",
"#656565",
"#818181",
"#9f9f9f",
"#bcbcbc",
"#e2e2e2",
"#ffffff",
2021-08-04 03:21:06 +00:00
];
2021-08-12 01:52:40 +00:00
// How big the brush size can get
// Although you can type in the input a bigger number than this anyway
export const maxBrushSize = 50;
2021-08-12 01:52:40 +00:00
// Chars that end up in the toolbar
export const charCodes = [' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-',
'.', '/',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A',
'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e',
'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
'x', 'y', 'z', '{', '|', '}', '~', 'Ç', 'ü', 'é', 'â', 'ä', 'à', 'å', 'ç', 'ê', 'ë', 'è',
'ï', 'î', 'ì', 'Ä', 'Å', 'É', 'æ', 'Æ', 'ô', 'ö', 'ò', 'û', 'ù', 'ÿ', 'Ö', 'Ü', 'ø', '£',
'Ø', '×', 'ƒ', 'á', 'í', 'ó', 'ú', 'ñ', 'Ñ', 'ª', 'º', '¿', '®', '¬', '½', '¼', '¡', '«',
'»', 'Á', 'Â', 'À', '©', '¢', '¥', 'ã', 'Ã', '¤', 'ð', 'Ð', 'Ê', 'Ë', 'È', 'ı', 'Í', 'Î',
'Ï', '¦', 'Ì', 'Ó', 'ß', 'Ô', 'Ò', 'õ', 'Õ', 'µ', 'þ', 'Þ', 'Ú', 'Û', 'Ù', 'ý', 'Ý', '¸',
'°', '¨', '·', '¯', '´', '≡', '±', '‗', '¶', '§', '÷',
'⁰', '¹', '³', '²', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹', '⁺', '⁻', '⁼', '⁽', '⁾',
'₀', '₁', '₂', '₃', '₄', '₅', '₆', '₇', '₈', '₉', '₊', '₋', '₌', '₍', '₎',
2021-12-04 03:47:31 +00:00
'¼', '½', '¾', '⅓', '⅔', '⅕', '⅖', '⅗', '⅘', '⅙', '⅚', '⅛', '⅜', '⅝', '⅞', '⅟', '⅒', '⅑', '',
'└', '┐', '┘', '┌', '│', '┤', '├', '┴', '┬', '─', '┼',
'╚', '╗', '╝', '╔', '║', '╣', '╠', '╩', '╦', '═', '╬',
'╰', '╮', '╯', '╭', '', '╲', '',
'▘', '▖', '▝', '▗', '▚',
'▏', '▎', '▍', '▌', '▋', '▊', '▉',
'▁', '▂', '▃', '▄', '▅', '▆', '▇', '▀', '▔', '░', '▒', '▓', '█',
];
// Toolbar icons
export const toolbarIcons = [{
name: 'default',
icon: 'edit_off',
},
{
name: 'select',
icon: 'photo_size_select_small',
},
{
name: 'text',
icon: 'text_rotation_none',
},
{
name: 'fill',
icon: 'format_color_fill',
},
{
name: 'brush',
icon: 'brush',
},
{
name: 'dropper',
icon: 'colorize',
},
{
name: 'eraser',
icon: 'remove_circle_outline',
},
{
name: 'fill-eraser',
icon: 'auto_fix_off',
},
];
2021-08-04 03:21:06 +00:00
export const emptyBlock = {
// bg: null,
// fg: null,
// char: null,
2021-08-04 03:21:06 +00:00
};
export const create2DArray = (rows) => {
const arr = [];
for (let i = 0; i < rows; i++) {
arr[i] = [];
}
return arr;
};
// Width and height of the ASCII blocks
// they seem to be 8x15 in asciiblaster
export const blockWidth = 8;
export const blockHeight = 15;
// Limits for undo and brush histories
2021-11-06 00:59:17 +00:00
export const maxBrushHistory = 200;
2021-12-17 11:03:10 +00:00
export const maxUndoHistory = 500;
2021-11-06 00:59:17 +00:00
export const tabLimit = 20;
2021-12-18 09:33:47 +00:00
export const parseMircAscii = async (contents, filename) => {
// The current state of the Colours
let curBlock = {
...emptyBlock,
};
contents = contents
.split('\u0003\u0003')
.join('\u0003')
.split('\u000F').join('')
.split('\u0003\n').join('\n')
.split('\u0002\u0003').join('\u0003')
.split('\u0002').join('') // bold
.split('\u001D').join(''); // bg highlight
let asciiLines = contents.split("\n");
const finalAscii = {
title: filename,
layers: [{
label: filename,
visible: true,
data: create2DArray(contents.split('\n').length),
width: 0, // calculated down bellow
height: contents.split('\n').length,
}],
history: [],
historyIndex: 0,
imageOverlay: {
url: null,
opacity: 95,
asciiOpacity: 100,
left: 0,
top: 0,
position: 'centered',
size: 100,
repeatx: true,
repeaty: true,
visible: false,
stretched: false,
},
x: blockWidth * 35, // the dragable ascii canvas x
y: blockHeight * 2, // the dragable ascii canvas y
selectedLayer: 0,
};
// let isPlainText = !colourCodeRegex.test(contents);
// https://modern.ircdocs.horse/formatting.html#color
// In the following list, <CODE> represents the color formatting character (0x03), <COLOR> represents one or two ASCII digits (either 0-9 or 00-99).
// The use of this code can take on the following forms:
// <CODE> - Reset foreground and background colors.
// <CODE>, - Reset foreground and background colors and display the , character as text.
// <CODE><COLOR> - Set the foreground color.
// <CODE><COLOR>, - Set the foreground color and display the , character as text.
// <CODE><COLOR>,<COLOR> - Set the foreground and background color.
// const mIrcSingleColourRegex = new RegExp(/(\u0003\d{0,2}[,])/, 'gu');
const asciiblasterRegex = /(^[\d]{1,2})?(?:,([\d]{1,2}))?/;
const mIrcColourRegex = new RegExp(/\u0003(\d{0,2})?[,]?(\d{0,2})?/, 'gu');
// let isPlainText = !colourCodeRegex.test(contents);
// Get the max line width, as some lines can be trimmed by spaces
// we cannot always rely on the first line for the width
for (let i = 0; i < asciiLines.length; i++) {
let cleanedWidth = asciiLines[i].replace(mIrcColourRegex, '').length;
if (cleanedWidth > finalAscii.layers[0].width) {
finalAscii.layers[0].width = cleanedWidth;
}
// Save some time on large asciis?
// if (i > 50) break;
}
// The foreground color is the first <COLOR>, and the background color is the second <COLOR> (if sent).
for (let y in asciiLines) {
let line = asciiLines[y];
let len = line.length - 1;
let char;
let block = {};
let pos = -1;
let actualPos = 0;
while (pos < len) {
pos++;
char = line[pos];
// next char is a color styling char, with possible color nums after
if (char === '\x03') {
var matches = line.substr(pos + 1, 5).match(asciiblasterRegex);
// \x03 without color code is a soft block reset
if (matches[1] === undefined && matches[2] === undefined) {
block.fg = 1;
block.bg = 0;
continue;
}
if (matches[1] !== undefined)
block.fg = Number(matches[1]);
if (matches[2] !== undefined)
block.bg = Number(matches[2]);
pos += matches[0].length;
continue;
}
block.char = char;
finalAscii.layers[0].data[y][actualPos] = {
...block
};
actualPos++;
}
pos = -1;
actualPos = 0;
block = {};
}
// First layer data generation
finalAscii.layers = [...fillNullBlocks(finalAscii.layers[0].height, finalAscii.layers[0]
.width, finalAscii.layers)];
// Store the ASCII and ensure we have no null blocks
finalAscii.layers = LZString.compressToUTF16(
JSON.stringify(finalAscii.layers),
);
// Save ASCII to storage
store.commit('newAsciibirdMeta', finalAscii);
return true;
}
export const parseMircAsciiOld = async (contents, filename) => {
2021-12-18 09:33:47 +00:00
// The current state of the Colours
let curBlock = {
...emptyBlock,
};
contents = contents
.split('\u0003\u0003')
.join('\u0003')
.split('\u000F').join('')
2021-12-30 04:25:07 +00:00
.split('\u0003\n').join('\n')
.split('\u0002\u0003').join('\u0003')
.split('\u0002').join('') // bold
2022-01-06 05:53:55 +00:00
.split('\u001D').join(''); // bg highlight
2021-12-18 09:33:47 +00:00
let asciiLines = contents.split("\n");
const finalAscii = {
title: filename,
layers: [{
label: filename,
visible: true,
data: create2DArray(contents.split('\n').length),
width: 0, // calculated down bellow
2021-12-18 09:33:47 +00:00
height: contents.split('\n').length,
}],
history: [],
historyIndex: 0,
imageOverlay: {
url: null,
opacity: 95,
asciiOpacity: 100,
left: 0,
top: 0,
2021-12-18 09:33:47 +00:00
position: 'centered',
size: 100,
repeatx: true,
repeaty: true,
visible: false,
stretched: false,
},
x: blockWidth * 35, // the dragable ascii canvas x
y: blockHeight * 2, // the dragable ascii canvas y
selectedLayer: 0,
};
// Determine if we have a plain text ascii
const colourCodeRegex = new RegExp(/\u0003/, 'g');
let isPlainText = !colourCodeRegex.test(contents);
// https://modern.ircdocs.horse/formatting.html#color
// In the following list, <CODE> represents the color formatting character (0x03), <COLOR> represents one or two ASCII digits (either 0-9 or 00-99).
// The use of this code can take on the following forms:
// <CODE> - Reset foreground and background colors.
// <CODE>, - Reset foreground and background colors and display the , character as text.
// <CODE><COLOR> - Set the foreground color.
// <CODE><COLOR>, - Set the foreground color and display the , character as text.
// <CODE><COLOR>,<COLOR> - Set the foreground and background color.
// const mIrcSingleColourRegex = new RegExp(/(\u0003\d{0,2}[,])/, 'gu');
const mIrcColourRegex = new RegExp(/\u0003(\d{0,2})?[,]?(\d{0,2})?/, 'gu');
const asciiblasterRegex = /(^[\d]{1,2})?(?:,([\d]{1,2}))?/;
// Get the max line width, as some lines can be trimmed by spaces
// we cannot always rely on the first line for the width
for (let i = 0; i < asciiLines.length; i++) {
let cleanedWidth = asciiLines[i].replace(mIrcColourRegex, '').length;
if (cleanedWidth > finalAscii.layers[0].width) {
finalAscii.layers[0].width = cleanedWidth;
}
// Save some time on large asciis?
// if (i > 50) break;
}
2021-12-18 09:33:47 +00:00
// The foreground color is the first <COLOR>, and the background color is the second <COLOR> (if sent).
for (let y in asciiLines) {
let line = asciiLines[y];
// Check C5, or C, first then
// let cleanLinesSingle = line.replace(mIrcSingleColourRegex, '');
// Do this
// let cleanLinesSingle = line.replace(mIrcSingleColourRegex, '');
let cleanLines = line.replace(mIrcColourRegex, '');
// cleanLines = cleanLines.replace(mIrcColourRegex, '');
// Somehow merge the arrays
// let parsedLineSingle = [...line.matchAll(mIrcSingleColourRegex)];
2021-12-18 11:18:39 +00:00
let parsedLine = [...line.matchAll(mIrcColourRegex)];
2021-12-18 09:33:47 +00:00
let colourData = [];
// parsedLine = [...parsedLine, ...parsedLineSingle];
2021-12-18 09:33:47 +00:00
curBlock = {
...emptyBlock,
};
2021-12-18 11:18:39 +00:00
let newData = [];
2021-12-18 09:33:47 +00:00
if (!isPlainText) {
for (let x in parsedLine) {
let codeData = parsedLine[x];
let colourArray = codeData[0].split("\u0003").join("").split(",");
// If we have C3,
let endsWithComma = /,$/;
if (endsWithComma.test(colourArray[0])) {
cleanLines[codeData.index] = ',';
}
2021-12-18 11:18:39 +00:00
if (colourArray.length === 2) {
if (colourArray[0] > -1) {
2021-12-18 09:33:47 +00:00
curBlock.fg = Number.parseInt(colourArray[0]);
}
if (colourArray[1] !== "" && colourArray[1] > -1) {
2021-12-18 09:33:47 +00:00
curBlock.bg = Number.parseInt(colourArray[1]);
}
2021-12-18 11:18:39 +00:00
} else if (colourArray.length === 1) {
if (colourArray[0] == "") {
2021-12-18 11:18:39 +00:00
delete curBlock['bg'];
delete curBlock['fg'];
delete curBlock['char'];
2021-12-18 11:18:39 +00:00
}
if (colourArray[0] > 0) {
curBlock.fg = Number.parseInt(colourArray[0]);
delete curBlock['bg'];
}
2022-01-05 06:59:33 +00:00
}
2021-12-18 11:18:39 +00:00
colourData.push({
code: codeData,
b: {
...curBlock
}
});
2021-12-18 09:33:47 +00:00
}
2021-12-18 11:18:39 +00:00
// Readjust the indexes
let indexAdjustment = 0;
2021-12-18 09:33:47 +00:00
for (let index in colourData) {
if (index === 0) {
continue;
}
2021-12-18 11:18:39 +00:00
colourData[index].code.index = colourData[index].code.index - indexAdjustment;
indexAdjustment = indexAdjustment + colourData[index].code[0].length;
newData[colourData[index].code.index] = colourData[index].b;
2021-12-18 09:33:47 +00:00
}
2021-12-18 11:18:39 +00:00
2021-12-18 09:33:47 +00:00
}
2021-12-18 11:18:39 +00:00
// Construct the ascii blocks
for (let i in cleanLines) {
let char = cleanLines[i];
2021-12-18 09:33:47 +00:00
2021-12-18 11:18:39 +00:00
// If there is a colour change present at this index
// we will keep track of it
if (!isPlainText && newData[i]) {
if (newData[i].bg !== undefined) {
2021-12-18 09:33:47 +00:00
curBlock.bg = newData[i].bg;
}
if (newData[i].fg !== undefined) {
2021-12-18 09:33:47 +00:00
curBlock.fg = newData[i].fg;
}
2021-12-18 11:18:39 +00:00
if (newData[i].fg === undefined && newData[i].bg === undefined) {
curBlock = {
...emptyBlock
};
2021-12-18 09:33:47 +00:00
}
2021-12-18 11:18:39 +00:00
}
2021-12-18 09:33:47 +00:00
curBlock.char = char;
2021-12-18 11:18:39 +00:00
2022-01-06 05:53:55 +00:00
if (curBlock.bg === null) {
delete curBlock['bg']
}
if (curBlock.fg === null) {
delete curBlock['fg']
}
if (curBlock.char === null) {
delete curBlock['char']
}
2021-12-18 09:33:47 +00:00
finalAscii.layers[0].data[y][i] = {
...curBlock
};
}
}
// First layer data generation
finalAscii.layers = [...fillNullBlocks(finalAscii.layers[0].height, finalAscii.layers[0]
.width, finalAscii.layers)];
2021-12-18 11:18:39 +00:00
2021-12-18 09:33:47 +00:00
// Store the ASCII and ensure we have no null blocks
finalAscii.layers = LZString.compressToUTF16(
JSON.stringify(finalAscii.layers),
);
2021-08-07 06:10:52 +00:00
// Save ASCII to storage
2021-08-04 03:21:06 +00:00
store.commit('newAsciibirdMeta', finalAscii);
2021-08-04 02:47:17 +00:00
return true;
};
// Creates new blank ASCII
2021-08-04 02:47:17 +00:00
export const createNewAscii = (forms) => {
2021-08-04 03:21:06 +00:00
const newAscii = {
2021-08-04 02:47:17 +00:00
title: forms.createAscii.title,
history: [],
historyIndex: 0,
2021-08-04 02:47:17 +00:00
x: 247, // the dragable ascii canvas x
y: 24, // the dragable ascii canvas y
layers: [{
2021-08-14 06:41:42 +00:00
label: forms.createAscii.title,
visible: true,
data: create2DArray(forms.createAscii.height),
width: Number.parseInt(forms.createAscii.width),
height: Number.parseInt(forms.createAscii.height),
2021-08-14 06:41:42 +00:00
}],
2021-10-16 05:16:21 +00:00
imageOverlay: {
url: null,
opacity: 95,
asciiOpacity: 100,
left: 0,
top: 0,
2021-10-16 05:16:21 +00:00
position: 'centered',
size: 100,
repeatx: true,
repeaty: true,
visible: false,
2021-10-23 00:23:02 +00:00
stretched: false,
2021-10-16 05:16:21 +00:00
},
2021-08-14 06:41:42 +00:00
selectedLayer: 0,
2021-08-04 02:47:17 +00:00
};
newAscii.layers = [...fillNullBlocks(newAscii.layers[0].height, newAscii.layers[0]
.width, newAscii.layers)];
newAscii.layers = LZString.compressToUTF16(JSON.stringify(newAscii.layers));
2021-08-04 03:21:06 +00:00
store.commit('newAsciibirdMeta', newAscii);
2021-08-06 01:51:58 +00:00
store.commit('closeModal', 'new-ascii');
2021-08-04 02:47:17 +00:00
return true;
2021-08-04 03:21:06 +00:00
};
// Converts ASCIIBIRD blocks to mIRC colours
export const exportMirc = (blocks = null) => {
2021-12-26 01:41:24 +00:00
if (blocks === null) {
// Export the entire main ascii
2021-12-26 01:41:24 +00:00
var {
currentAscii
} = store.getters;
2021-08-28 03:20:59 +00:00
2021-12-26 01:41:24 +00:00
var {
currentAsciiLayersWidthHeight
} = store.getters;
2021-08-28 03:20:59 +00:00
blocks = mergeLayers();
2021-12-26 01:41:24 +00:00
} else {
// We are exporting a brush
2021-12-26 01:41:24 +00:00
var currentAscii = {};
currentAscii.title = `brush-${cyrb53(JSON.stringify(blocks))}.txt`
2021-12-26 01:41:24 +00:00
var currentAsciiLayersWidthHeight = {
height: blocks.length,
width: blocks[0].length
}
}
const output = [];
2021-08-14 06:41:42 +00:00
let curBlock = false;
2021-09-04 00:21:53 +00:00
let pushString = '';
2021-08-28 03:20:59 +00:00
let prevBlock = {
bg: -1,
fg: -1
};
2022-01-06 05:39:54 +00:00
const zeroPad = (num, places = 2) => String(num).padStart(places, '0');
2022-01-05 06:59:33 +00:00
2021-09-04 05:16:27 +00:00
for (let y = 0; y <= currentAsciiLayersWidthHeight.height - 1; y++) {
2021-08-14 22:51:45 +00:00
2021-09-04 05:16:27 +00:00
for (let x = 0; x <= currentAsciiLayersWidthHeight.width - 1; x++) {
2021-08-28 03:20:59 +00:00
curBlock = {
...blocks[y][x]
};
2021-08-14 22:51:45 +00:00
if (curBlock.bg === null) {
delete curBlock['bg']
}
if (curBlock.fg === null) {
delete curBlock['fg']
}
if (curBlock.char === null) {
delete curBlock['char']
}
let isPadded = ((blocks[y][x + 1] !== undefined) && (blocks[y][x + 1].bg === undefined ||
blocks[
y][x + 1].fg ===
undefined) && blocks[y][x + 1]
.char !== undefined && (Number.parseInt(
blocks[y][x + 1]
2022-01-08 03:56:54 +00:00
.char) >= 0 && Number.parseInt(blocks[y][x + 1].char) <= 9) ||
(blocks[y][x].char !==
undefined && (Number.parseInt(blocks[y][x]
.char) >= 0 && Number.parseInt(blocks[y][x].char) <= 9)))
2022-01-06 05:39:54 +00:00
// If we have a difference between our previous block
// we'll put a colour codes and continue as normal
2022-01-05 06:59:33 +00:00
if ((curBlock.bg !== prevBlock.bg || curBlock.fg !== prevBlock.fg)) {
2021-08-14 22:51:45 +00:00
curBlock = {
2021-08-28 03:20:59 +00:00
...blocks[y][x]
2021-08-14 22:51:45 +00:00
};
2021-08-28 03:20:59 +00:00
if (curBlock.fg === undefined && curBlock.bg === undefined) {
2021-09-04 00:21:53 +00:00
output.push('\u0003');
2021-08-28 03:20:59 +00:00
} else {
2021-09-04 00:21:53 +00:00
if (curBlock.bg === undefined && curBlock.fg !== undefined) {
2022-01-05 06:59:33 +00:00
pushString =
2022-01-06 05:39:54 +00:00
`\u0003${(isPadded) ? zeroPad(curBlock.fg) : curBlock.fg}`;
2021-09-04 00:21:53 +00:00
}
if (curBlock.bg !== undefined && curBlock.fg !== undefined) {
2021-12-29 06:52:32 +00:00
// export will check if the next char is a number and add 0 padding to prevent clients eating characters
2022-01-05 06:59:33 +00:00
pushString =
2022-01-06 05:39:54 +00:00
`\u0003${curBlock.fg},${(isPadded) ? zeroPad(curBlock.bg) : curBlock.bg}`;
2021-09-04 00:21:53 +00:00
}
if (curBlock.bg !== undefined && curBlock.fg === undefined) {
2022-01-05 06:59:33 +00:00
pushString =
2022-01-06 05:39:54 +00:00
`\u0003,${(isPadded) ? zeroPad(curBlock.bg) : curBlock.bg}`;
2021-10-23 00:37:25 +00:00
}
2021-09-04 00:21:53 +00:00
output.push(pushString);
2021-08-28 03:20:59 +00:00
}
}
// null .chars will end up as space
output.push(curBlock.char ?? ' ');
2021-09-04 00:21:53 +00:00
prevBlock = {
...blocks[y][x]
};
}
// We can never have a -1 colour code so we'll always
// write one at the start of each line
prevBlock = {
bg: -1,
fg: -1
};
// New line except for the very last line
2021-12-29 06:52:32 +00:00
// if (blocks[y] && y < blocks[y].length - 1) {
2022-01-05 06:59:33 +00:00
output.push('\n');
2021-12-29 06:52:32 +00:00
// }
}
// Download to a txt file
// Check if txt already exists and append it
var filename = currentAscii.title.slice(currentAscii.title.length - 3) === 'txt' ?
currentAscii.title :
`${currentAscii.title}.txt`;
2021-12-26 01:41:24 +00:00
return {
filename,
output
}
}
// Download a string to a file with a filename
export const downloadFile = (content, filename, contentType) => {
const a = document.createElement('a');
const file = new Blob([content], {
type: contentType
});
a.href = URL.createObjectURL(file);
a.download = filename;
a.click();
URL.revokeObjectURL(a.href);
};
2021-12-26 01:41:24 +00:00
export function canvasToPng(canvas, filename) {
let downloadLink = document.createElement('a');
downloadLink.setAttribute('download', filename);
canvas.toBlob(function (blob) {
let url = URL.createObjectURL(blob);
downloadLink.setAttribute('href', url);
downloadLink.click();
});
}
export const checkForGetRequest = async () => {
const asciiUrlCdn = new URL(location.href).searchParams.get('ascii');
if (asciiUrlCdn) {
const res = await fetch(`https://ascii.jewbird.live/${asciiUrlCdn}`, {
method: 'GET',
headers: {
Accept: 'text/plain',
},
});
const asciiData = await res.text();
parseMircAscii(asciiData, asciiUrlCdn);
return;
}
const asciiUrl = new URL(location.href).searchParams.get('ircwatch');
if (asciiUrl) {
const res = await fetch(`https://irc.watch/ascii/txt/${asciiUrl}`, {
method: 'GET',
headers: {
Accept: 'text/plain',
},
});
const asciiData = await res.text();
parseMircAscii(asciiData, asciiUrl);
return;
}
const haxAscii = new URL(location.href).searchParams.get('haxAscii');
if (haxAscii) {
const res = await fetch(`https://art.h4x.life/${haxAscii}`, {
method: 'GET',
headers: {
Accept: 'text/plain',
},
});
// Considers paths
const asciiName = haxAscii.split('/').pop();
const asciiData = await res.text();
parseMircAscii(asciiData, asciiName);
}
}
2021-08-04 02:47:17 +00:00
// Hashing algo to detect duplicate brushes
2021-08-06 11:00:35 +00:00
// from https://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript
export const cyrb53 = function (str, seed = 1337) {
let h1 = 0xdeadbeef ^ seed,
h2 = 0x41c6ce57 ^ seed;
for (let i = 0, ch; i < str.length; i++) {
ch = str.charCodeAt(i);
h1 = Math.imul(h1 ^ ch, 2654435761);
h2 = Math.imul(h2 ^ ch, 1597334677);
}
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
};
2021-08-07 06:10:52 +00:00
// Mostly plain text asciis wont have all their blocks
// so this will fix that
export const fillNullBlocks = function (height, width, layerData = null) {
2021-08-12 01:52:40 +00:00
// Probably used on irc import to make the blocks proper,
// especially with plain text ascii
2021-08-15 00:15:36 +00:00
if (layerData === null) {
var layers = [...store.getters.currentAsciiLayers]
} else {
var layers = [...layerData]
2021-08-12 01:52:40 +00:00
}
2021-08-15 00:15:36 +00:00
for (let i = 0; i <= layers.length - 1; i++) {
let blocks = layers[i].data;
for (let y = 0; y < height; y++) {
// New row
if (!blocks[y]) {
blocks[y] = [];
for (let x = 0; x < width; x++) {
2021-08-12 01:52:40 +00:00
blocks[y][x] = {
...emptyBlock
};
2021-08-07 02:41:47 +00:00
}
2021-08-15 00:15:36 +00:00
} else {
// no new rows but new cols
for (let x = 0; x < width; x++) {
if (blocks[y] && !blocks[y][x]) {
blocks[y][x] = {
...emptyBlock
};
}
}
2021-08-07 02:41:47 +00:00
}
}
2021-08-15 00:15:36 +00:00
// Update layer with new blocks
layers[i].data = [...blocks]
layers[i].width = width
layers[i].height = height
2021-08-07 02:41:47 +00:00
}
2022-01-05 06:59:33 +00:00
return [...layers]
2021-08-07 02:41:47 +00:00
}
2021-08-07 06:10:52 +00:00
// Sometimes if we copy blocks the initial Y values will be null
// and cause an error when trying to calculate width
2021-08-12 01:52:40 +00:00
// So we get the longest x length
2021-08-07 02:41:47 +00:00
export const getBlocksWidth = function (blocks) {
2021-08-12 01:52:40 +00:00
let maxWidth = 0;
2021-08-07 02:41:47 +00:00
2021-08-12 01:52:40 +00:00
for (let y = 0; y < blocks.length; y++) {
2021-08-07 02:41:47 +00:00
if (!blocks[y]) {
continue
}
2021-08-12 01:52:40 +00:00
if (blocks[y] && blocks[y].length > maxWidth) {
maxWidth = blocks[y].length
2021-08-07 02:41:47 +00:00
}
}
2021-08-12 01:52:40 +00:00
return maxWidth
2021-08-07 02:41:47 +00:00
}
2021-08-07 06:10:52 +00:00
// This removes the null blocks from our copy and paste
// to make sure it's centered better
2021-08-07 02:41:47 +00:00
export const filterNullBlocks = function (blocks) {
let newBlocks = [];
let y;
blocks = blocks.filter(function (item) {
return item !== null
});
for (y = 0; y < blocks.length; y++) {
newBlocks[y] = (blocks[y].filter(function (item) {
return item !== null
}))
2021-08-07 02:41:47 +00:00
}
return newBlocks
}
2021-08-14 06:41:42 +00:00
// Function to check if the left and top values are visible on the screen
export const checkVisible = function (bottom, top) {
var viewHeight = Math.max(
document.documentElement.clientHeight,
window.innerHeight
);
return !(bottom < 0 || top - viewHeight >= 0);
}
export const mergeLayers = function (blocks = null) {
let mergedLayers = [];
// Position of the meta array
let x = 0;
let y = 0;
let z = 0;
// Draws the actual rectangle
let canvasX = 0;
let canvasY = 0;
let curBlock = null;
for (y = 0; y < store.getters.currentAsciiLayers[0].height + 1; y++) {
canvasY = blockHeight * y;
if (!mergedLayers[y]) {
mergedLayers[y] = [];
}
for (x = 0; x < store.getters.currentAsciiLayers[0].width + 1; x++) {
canvasX = blockWidth * x;
curBlock = {
...emptyBlock
};
// Loop layers
for (z = store.getters.currentAsciiLayers.length - 1; z >= 0; z--) {
if (store.getters.currentAsciiLayers[z].visible === false) {
continue;
}
if (
store.getters.currentAsciiLayers[z] &&
store.getters.currentAsciiLayers[z].data &&
store.getters.currentAsciiLayers[z].data[y] &&
store.getters.currentAsciiLayers[z].data[y][x]
) {
2021-12-26 10:04:20 +00:00
if (curBlock.bg === undefined) {
curBlock.bg = store.getters.currentAsciiLayers[z].data[y][x].bg;
2021-08-28 02:41:31 +00:00
}
if (curBlock.fg === undefined) {
2021-08-28 02:41:31 +00:00
curBlock.fg = store.getters.currentAsciiLayers[z].data[y][x].fg;
2022-01-05 06:59:33 +00:00
}
2021-12-26 10:04:20 +00:00
if (curBlock.char === undefined) {
curBlock.char = store.getters.currentAsciiLayers[z].data[y][x].char;
2021-08-28 02:41:31 +00:00
}
2021-08-28 02:41:31 +00:00
continue;
}
2021-12-26 10:04:20 +00:00
// break;
}
2021-08-28 02:41:31 +00:00
mergedLayers[y][x] = {
...curBlock
}
}
}
2021-10-09 00:47:16 +00:00
return mergedLayers;
}
// Splash screen ascii encoded
export const splashAscii = JSON.parse(LZString.decompressFromEncodedURIComponent(
"NrDeCICMHNwLgIwFYA04BmtFoMYAsBDAJ3nAAJwBfFCGeZNTe3QkucqmqLBjHl4qQrVaPVH2bh8g9sK51E4ptimshnUfSX9VMjiO5bGO6W33yxxyafUGFvZQgFm5mxVZU3ZGw+4me1bztLfyddFx97bWtA8zcHE1jXXwSYvWSojzCvOJTogPTIkOUABmdSQCTSIvgy0OzYwGTSarhax3q9JuCarPazTotuuvL2frdWnl7SUd9x+kmR5tnEefBphSWEFbWsDa3FnuHV-aHwqeO2w+3Bi9OFrpaD26P73cvzibeXx5yrh5Of95zT4DP43AFff6NQHLYFjb5QiFghEg15PX6o8Eo+EdaGbWEzbF9XF7REfNHE-HrQlnUlA8m0mH0rGQnEMvFMuEsolsknMpGsvlkzGc-ncwV04UErk08WMyVU6V3WXs+U7alKkVC5GaiXaqWimU6uV6hUGjX6rUCo0qk1qxXPZW861Oi26q2u43u02WsXO2GUAC6KDAeSyhwyxUShXuqQKERj+RWEaMoXDzQAHB5ajlk3BM-5s0kM1m0-d86VSyDy3ac81Y0m64nK-Em09c-Xm6HU23G2Gewm+7WBwXDlVh45O5kR08x47KXazQ6-RyPTavQufYbVy7vW7fdv59dN+bd5796e1+eN3utxed9ez7eH5en0ebyfn-e34+P9+X7-QWPJcDxXO9D0A99gLA0DP3AjFbT-L8IJ-KDYJgxC4PVVCMPQ5D-2wvCkPg9ccNVUiEMIzD7XRLCaOoilcOIq9yJIyjGNohiyLYrimNfbiKN4gDBII4S6MXMSgIkyCpJQmT8LkoiOJEQNgxbQci3HKN4xBDt+yrEs9LcatBiHfTp1MoyDIs3xjL+ayFFswtozMitDJsqyNJcmtPLU7t7MjNJtN8ic3KnEL-JTcKfK7KLnOCrTbB01sIr8VyclneKTNiDKQJ4pS53Y+ieSo8TOIE-LlzyoqCqq0risKuqavK6rKuaxrWtY0Sys6ircrayTuuY-iepavqRvasahq6+raoGmb+ukwa+Omprxrm1apt66DZsW+a1t2jblq2tCdtkpahOOljNtG7aFrOvbrom26rRUkMwoSoIkvUuL3PM6KHI8n6Ab+oGdEXXNHMnLBIdC6HAaClIsIh+HEsskGEeBtL-oCuNUZij7cnewK8aJ3HPsysnCZxhtNOJ8n8bpqnIoJ9t8icswcoUAB2b6OeaHm-Oy-necqYXBb0TmsAF2K+fuaWCcl+B5cZxW4GVynVfVlZNZF9gdfF2WQS10cxZl0W5d18B9bNvXTYVu2VYdjWne1l2TYtg3zaNy3rftj2batt2ZyD9KQ6F-2-e9z3bYjx3Y+d+PXcT92o4D3249TyO3GN4Pk9zzOM+zn2w4lkvDaL6PA7z0Pq-DguE-rpPG5Tiu07Lr3W6z3wc5r5v887wvu+L2vS5H8vXopmmvsr5H0ZJuG5-pzHvNB+AYZS9fsbXlGl4XgOAD5i0Xpm8x3k-N9X0-j9Zy2b5n3t79pym74Dl+WeaZKt7gT-L5-jGsD-vPbe-gBYbw8KAr+YRlDiBSlAgBUNJDQIQWrW+7dd5K1QWPIBKDH59xSj3L+BDL5EP-hg3BA9n5oJPiQ7BND0E4NflQ3MdDqGYLwYQthFCp5cOQSw5hnCh7kMEYwrB9C+FMIkaI1hQjuYCNkTIqWcjFEKLISI9hxClGqPflI-hKiGHaPUaQ-RjNdFqJ4bDLRJjJGGNoZo4xlDlJBjetTZBukwHXyPljS+F8jE+OwX4+hATz5n1nl43xITPEr3CR4p+3CGYOOnmY3wgD6EpJPmk3MGSP52KyfcXJXkyGwI8DAyBxTXFlIsfYpu5je41LrnU0eNiY5NKri09ODcGnl2EV3eRbcpHtOqd0wevSenKL6W0qhAyW5DI6TMwZIzhljNGZY2ZCzVlLMWSs+ZGz1lbOmWs7Zez+5zP2Tsw5VTTlHNqSc45BzLkXNuWc+5LCpmPKufUm51y7lvIeV8p5PyXmTKBf04FEyQWONUvEuJpNoV7yif4iJZZEXKkOIAbwJcRooxU8dFbJMW4uxVinIOKCl2UgfkyeyDyXJJyTSvRVKFD0pcZUuB8ASmXxZd-cp-g2VGI5Ty7BK0OqPXWkKo6N0Tp3XkudESl1hrCoOqKi64qrpiqehK-a91DpKrVSq7VIrJqqv1c9eVmrFUyuVXKw1CqDV6utcaq1pqbXmp1Za21jr7VuqlQ9B1XqtXOqNeqk1vqzUKRKgG3V-q7WBp9YpC1gqnWjAnlC1xjLWV2KCaE+FgTkXWjxSigl+KiWEtiMStGYTsGps5XS2lSSGU1oMRS5lla3GlO5VypBzKKlFLbZU+NpN2Yd3iQO5pIbpWhoauG11kb3XRs9bGl1fbZ3TuDQmsda7vVzrDVGiN47Toro9cu+dk7F07vXX63dkqj3bqnRejV+6l23qDVemdp6N2Hq3S+m9Z7R1vsfTGj997X3nu-auiFzjmZWLyfWyDIJK0ZsiVlbxObfrluzTEklw70nQYSW4SteG7EtvZc2rtraO3drI6R+Bnae2wObWze5J7UpZtaRTTDqtGMbH5SO1cXGWOgeA7+kDB6-2bondejjsqJNxskzJ6TcmF2yYUxahAAAWZD9g1PofiJp1DJ9VPqZ4Dp5j7YjOId5aZ0l7KLOYZMwZ+g1nXEOeZU52BLnIFuas3ZxAHnzNef01plIPnsH+d07ZgLGm-NBfoSF4zdYot6fi2F0LcWvNSfakmut1asu1qozltN2WG3UoKzB3D2HYX5by1WyrdGCM1b0RsQ4gAnUmLXoZrhbYhtZ0iR9l3XeW9eC-1sRw8wUjc6YO75fy-y8deZN35HyJvzf+bNwF4LRufMW+8xpY3uMLa2+tvbu2umHfG0tjbc2DunYu5to7l2bvXZO-dnbhnUt2Ia7217H36ufcq29lKv2v7-cvoDoxwOBXfcKxeZBoP6HQ5PrD3M8OWsg-ByV1cUOUc4bR85rzK3RsZdyxDgnqPMvVeG6V4rmOSeE4q9TqrtP8MU-K3T4nRPKfPia0j0gnXc1PG51jnIfPIe885+wQXYMqPkYl5R-LkuZccL0eInR1jtvSJ+1kabyv9tGMVy00xtOdcq71yzx7qv9d2IN1r2xCvzc2+t3byrMWzNg6+5XAAyiL8AABbD3BAPcABt-ce4R5bAAQh7gAlh7gAXkHj3AAZD3AB7H3HuAB2dYlg2Yz152yTPc-IPz5Uwv7jKvF8gZn1xFf3t6Md5Z7XtuzH45p8b5nbPm9t9b0ztJqsGek8Z5SsrA-+-V4DmLv8HP2utY92PvCE-80C+n4vmMg29Mr-bGvusG+lfb915r47Q29Fl6Q4f9Np+T-n8qxb-fpuW-q3zDkAAzk7e-sRk-xxf3oNA7-eEeA-2YX33+Tw3uO+FCf+pAAAknvs9uFuziPgTIAACkHuiBk+ZgyB8+sQaBPOOQmB-OGBSBseKBzuauLuxBpBtOCAVeJetOR+0SpeZ+dBF+1BBGlBAOBGiWUBJurgTeneQ+fefB9Og+Py3e2SlcPeghKUlasOM+UhS+6BU+hBoushWBHWShiMNG0uVasumhGhvG6+6hGi9uZuhht+DeTBjBLeNBCK5hHelhaGDB9hRhl+phwCOQAB9cYB7AAADs-ocN4YATkCQP4a-j4U8AAG4hE5AACmERsQAAJjEXoAAPwpYwHj5wGMwAByKeChXuHu0ROR7AORwelcOAHueA0eFRRRBBchyO1hee9B5BSMCGNhDRLeHK+EmaFhGOTOteWeIBlu5M3BhGRiveAhw+Eh4hX8oxmyV8ygECx+cx6Olc0hlsKxyxqhQuC+ORaxo+m++hfW+xA2hx0WW+u+-R1+RuHeV+t2VuThdRBerRHe7R9R9xReBGTRSKrx+CzhVxPxTO1xnBHB529exhTx7BVRORWROR+RNRpAAALh7gAO5J4e6BGwnsAADWHuhR6JcOlsMJyheg6AaeeRHuCJnxDhXRXxX8thwSlch8FJZhlJLRJB5B4JZxHJoAQxdWDu0xHefJXekxv8jx8xtByif2qxGxsBWxuJOxBMM+fK7aUu
2021-08-14 06:41:42 +00:00
));
2021-08-04 03:21:06 +00:00
export default createNewAscii;