add import/export
This commit is contained in:
parent
7d7e11bdb1
commit
3a92f0fc1d
22 changed files with 1185 additions and 983 deletions
|
@ -1,16 +1,16 @@
|
|||
{
|
||||
"files": {
|
||||
"main.css": "/static/css/main.7948f1f9.css",
|
||||
"main.js": "/static/js/main.22757a4a.js",
|
||||
"main.js": "/static/js/main.6564ce4b.js",
|
||||
"static/js/787.ada1a5f8.chunk.js": "/static/js/787.ada1a5f8.chunk.js",
|
||||
"static/media/Calamity-Regular.otf": "/static/media/Calamity-Regular.cbeefc650e6ac39335b6.otf",
|
||||
"index.html": "/index.html",
|
||||
"main.7948f1f9.css.map": "/static/css/main.7948f1f9.css.map",
|
||||
"main.22757a4a.js.map": "/static/js/main.22757a4a.js.map",
|
||||
"main.6564ce4b.js.map": "/static/js/main.6564ce4b.js.map",
|
||||
"787.ada1a5f8.chunk.js.map": "/static/js/787.ada1a5f8.chunk.js.map"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/css/main.7948f1f9.css",
|
||||
"static/js/main.22757a4a.js"
|
||||
"static/js/main.6564ce4b.js"
|
||||
]
|
||||
}
|
|
@ -1 +1 @@
|
|||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="for Breath of the Wild"/><meta property="og:site_name" content="itntpiston.app"/><meta property="og:title" content="Hundo Duplication Simulator"><meta property="og:type" content="website"><meta property="og:url" content="https://dupl.itntpiston.app/#/"><meta property="og:description" content="for Breath of the Wild"><link rel="manifest" href="/manifest.json"/><title>Hundo Duplication Simulator</title><script defer="defer" src="/static/js/main.22757a4a.js"></script><link href="/static/css/main.7948f1f9.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="for Breath of the Wild"/><meta property="og:site_name" content="itntpiston.app"/><meta property="og:title" content="Hundo Duplication Simulator"><meta property="og:type" content="website"><meta property="og:url" content="https://dupl.itntpiston.app/#/"><meta property="og:description" content="for Breath of the Wild"><link rel="manifest" href="/manifest.json"/><title>Hundo Duplication Simulator</title><script defer="defer" src="/static/js/main.6564ce4b.js"></script><link href="/static/css/main.7948f1f9.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
1
docs/static/js/main.22757a4a.js.map
vendored
1
docs/static/js/main.22757a4a.js.map
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
docs/static/js/main.6564ce4b.js.map
vendored
Normal file
1
docs/static/js/main.6564ce4b.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
67
src/App.tsx
67
src/App.tsx
|
@ -1,17 +1,15 @@
|
|||
import { Command, CommandBreakSlots, CommandInitialize, CommandNothing, CommandReload, CommandSave, CommandSortKey } from 'core/Command';
|
||||
import { Inventory } from 'core/Inventory';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { Command, CommandBreakSlots, CommandInitialize, CommandNothing, CommandReload, CommandSave, CommandSortKey } from "core/Command";
|
||||
import { Inventory } from "core/Inventory";
|
||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||
|
||||
import './App.css';
|
||||
import { CommandItem } from './components/CommandItem';
|
||||
import { ItemStack as ISC } from './components/ItemStack';
|
||||
import "./App.css";
|
||||
import { CommandItem } from "./components/CommandItem";
|
||||
|
||||
import { ItemStack, ItemType } from 'core/ItemStack';
|
||||
import { DisplayPane } from 'surfaces/DisplayPane';
|
||||
import { Item } from 'core/Item';
|
||||
import { deserialzeCommands, serializeCommands } from 'core/serialize';
|
||||
|
||||
const Buffer = require("buffer/").Buffer;
|
||||
import { DisplayPane } from "surfaces/DisplayPane";
|
||||
import { Item } from "core/Item";
|
||||
import { deserialzeCommands, serializeCommands } from "core/serialize";
|
||||
import { saveAs } from "data/FileSaver";
|
||||
import { parseCommand } from "core/Parser";
|
||||
|
||||
const getDefaultCommands = (): Command[]=>{
|
||||
const encoded = localStorage.getItem("HDS.CurrentCommands");
|
||||
|
@ -43,7 +41,7 @@ const getDefaultCommands = (): Command[]=>{
|
|||
new CommandSave(),
|
||||
new CommandReload()
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
export const App: React.FC = () => {
|
||||
const [commands, setCommands] = useState<Command[]>(getDefaultCommands());
|
||||
|
@ -70,7 +68,7 @@ export const App: React.FC = () => {
|
|||
}else if(e.code==="ArrowUp"){
|
||||
setDisplayIndex(Math.max(0, displayIndex-1));
|
||||
}
|
||||
}
|
||||
};
|
||||
}, [commands, displayIndex]);
|
||||
|
||||
useEffect(()=>{
|
||||
|
@ -78,12 +76,25 @@ export const App: React.FC = () => {
|
|||
localStorage.setItem("HDS.CurrentCommands", encoded);
|
||||
}, [commands]);
|
||||
|
||||
|
||||
const uploadRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
return (
|
||||
<div className='Calamity'
|
||||
>
|
||||
|
||||
<input ref={uploadRef} id="Upload" type="File" hidden onChange={(e)=>{
|
||||
const files = e.target.files;
|
||||
if(files?.length && files[0]){
|
||||
const file = files[0];
|
||||
file.text().then(text=>{
|
||||
const lines = text.split("\n");
|
||||
const parsedCommands: Command[] = lines.map(l=>parseCommand(l)).filter(c=>c) as Command[];
|
||||
setDisplayIndex(0);
|
||||
setContextIndex(-1);
|
||||
setContextMenuShowing(false);
|
||||
setCommands(parsedCommands);
|
||||
});
|
||||
}
|
||||
}}/>
|
||||
<div id="CommandList" style={{
|
||||
width: "300px",
|
||||
height: "calc( 100vh - 5px )",
|
||||
|
@ -98,11 +109,11 @@ export const App: React.FC = () => {
|
|||
paddingInlineStart: 0
|
||||
}}>
|
||||
{
|
||||
commands.map((c,i)=>(
|
||||
commands.map((c,i)=>
|
||||
<CommandItem
|
||||
onClick={()=>setDisplayIndex(i)}
|
||||
onContextMenu={(x,y)=>{
|
||||
setContextIndex(i)
|
||||
setContextIndex(i);
|
||||
setContextMenuX(x);
|
||||
setContextMenuY(y);
|
||||
setContextMenuShowing(true);
|
||||
|
@ -114,14 +125,23 @@ export const App: React.FC = () => {
|
|||
>
|
||||
{c.getDisplayString()}
|
||||
</CommandItem>
|
||||
))
|
||||
)
|
||||
}
|
||||
<CommandItem onClick={()=>{
|
||||
const arrCopy = [...commands];
|
||||
arrCopy.push(new CommandNothing());
|
||||
setCommands(arrCopy);
|
||||
}}>(new)</CommandItem>
|
||||
|
||||
<CommandItem onClick={()=>{
|
||||
if(uploadRef.current){
|
||||
uploadRef.current.click();
|
||||
}
|
||||
}}>(import)</CommandItem>
|
||||
<CommandItem onClick={()=>{
|
||||
const lines = commands.map(c=>c.getDisplayString());
|
||||
const text = lines.join("\n");
|
||||
saveAs(text, "dupe.txt");
|
||||
}}>(export)</CommandItem>
|
||||
</ul>
|
||||
</div>
|
||||
{displayIndex >= 0 && displayIndex < commands.length &&
|
||||
|
@ -134,9 +154,8 @@ export const App: React.FC = () => {
|
|||
const arrCopy = [...commands];
|
||||
arrCopy[displayIndex] = c;
|
||||
setCommands(arrCopy);
|
||||
}}/>
|
||||
|
||||
|
||||
}}
|
||||
/>
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -190,4 +209,4 @@ export const App: React.FC = () => {
|
|||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -15,12 +15,11 @@ export const CommandItem: React.FC<CommandItemProps> = ({isSelected, isContextSe
|
|||
onClick={onClick}
|
||||
onContextMenu={(e)=>{
|
||||
if(onContextMenu){
|
||||
console.log(e);
|
||||
onContextMenu(e.clientX,e.clientY);
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
}}
|
||||
>{children} </li>
|
||||
}
|
||||
>{children} </li>;
|
||||
};
|
||||
|
||||
|
|
|
@ -10,9 +10,9 @@ export const ItemList: React.FC<ItemListProps> = ({items, numBroken}) => {
|
|||
{
|
||||
items.map(({name, count}, i)=>{
|
||||
const broken = i+numBroken >= items.length;
|
||||
return <ItemStack key={i} name={name} count={count} isBroken={broken}/>
|
||||
return <ItemStack key={i} name={name} count={count} isBroken={broken}/>;
|
||||
})
|
||||
}
|
||||
</>;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -5,5 +5,5 @@ type ItemStackProps = {
|
|||
};
|
||||
|
||||
export const ItemStack: React.FC<ItemStackProps> = ({name, count, isBroken})=>{
|
||||
return <span>[{name} x{count}]{isBroken && " (broken)"} <br/></span>
|
||||
}
|
||||
return <span>[{name} x{count}]{isBroken && " (broken)"} <br/></span>;
|
||||
};
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { Inventory } from "./Inventory";
|
||||
import { Item, ItemIds } from "./Item";
|
||||
import { ItemStack, ItemType } from "./ItemStack";
|
||||
import { ItemStack } from "./ItemStack";
|
||||
|
||||
const Buffer = require("buffer/").Buffer;
|
||||
const Buffer = require("buffer/").Buffer; /* eslint-disable-line @typescript-eslint/no-var-requires*/
|
||||
|
||||
export interface Command {
|
||||
execute(inv: Inventory): void,
|
||||
|
@ -13,11 +13,8 @@ export interface Command {
|
|||
|
||||
export class CommandNothing implements Command {
|
||||
static Op = 0x0;
|
||||
constructor(){
|
||||
|
||||
}
|
||||
|
||||
fromBuffer(buf: Buffer): number {
|
||||
fromBuffer(_buf: Buffer): number {
|
||||
return 0;
|
||||
}
|
||||
toBuffer(): Buffer {
|
||||
|
@ -26,8 +23,8 @@ export class CommandNothing implements Command {
|
|||
return buf;
|
||||
}
|
||||
|
||||
execute(inv: Inventory): void {
|
||||
|
||||
execute(_inv: Inventory): void {
|
||||
// nothing
|
||||
}
|
||||
getDisplayString(): string {
|
||||
return "";
|
||||
|
@ -37,7 +34,7 @@ export class CommandNothing implements Command {
|
|||
|
||||
export class CommandInitialize implements Command {
|
||||
static Op = 0x1;
|
||||
private stacks: ItemStack[]
|
||||
private stacks: ItemStack[];
|
||||
constructor(stacks: ItemStack[]){
|
||||
this.stacks = stacks;
|
||||
}
|
||||
|
@ -68,7 +65,6 @@ export class CommandInitialize implements Command {
|
|||
buf.writeInt16LE(this.stacks.length, write);
|
||||
write+=2;
|
||||
this.stacks.forEach(({item,count})=>{
|
||||
console.log(count);
|
||||
buf.writeInt16LE(count&0xffff, write);
|
||||
write+=2;
|
||||
buf.writeInt8(ItemIds[item], write);
|
||||
|
@ -85,7 +81,7 @@ export class CommandInitialize implements Command {
|
|||
this.stacks.forEach(({item, count})=>{
|
||||
parts.push(""+count);
|
||||
parts.push(item);
|
||||
})
|
||||
});
|
||||
return parts.join(" ");
|
||||
}
|
||||
|
||||
|
@ -117,7 +113,7 @@ export class CommandBreakSlots implements Command {
|
|||
|
||||
export class CommandSave implements Command {
|
||||
static Op = 0x3;
|
||||
public fromBuffer(buf: Buffer): number {
|
||||
public fromBuffer(_buf: Buffer): number {
|
||||
return 0;
|
||||
}
|
||||
public toBuffer(): Buffer {
|
||||
|
@ -135,7 +131,7 @@ export class CommandSave implements Command {
|
|||
|
||||
export class CommandReload implements Command {
|
||||
static Op = 0x4;
|
||||
public fromBuffer(buf: Buffer): number {
|
||||
public fromBuffer(_buf: Buffer): number {
|
||||
return 0;
|
||||
}
|
||||
public toBuffer(): Buffer {
|
||||
|
@ -153,7 +149,7 @@ export class CommandReload implements Command {
|
|||
|
||||
export class CommandSortKey implements Command {
|
||||
static Op = 0x5;
|
||||
public fromBuffer(buf: Buffer): number {
|
||||
public fromBuffer(_buf: Buffer): number {
|
||||
return 0;
|
||||
}
|
||||
public toBuffer(): Buffer {
|
||||
|
@ -171,7 +167,7 @@ export class CommandSortKey implements Command {
|
|||
|
||||
export class CommandSortMaterial implements Command {
|
||||
static Op = 0x6;
|
||||
public fromBuffer(buf: Buffer): number {
|
||||
public fromBuffer(_buf: Buffer): number {
|
||||
return 0;
|
||||
}
|
||||
public toBuffer(): Buffer {
|
||||
|
@ -197,7 +193,7 @@ const VerbToId = {
|
|||
"Get": 6,
|
||||
"Add": 7,
|
||||
"Pickup": 8
|
||||
}
|
||||
};
|
||||
|
||||
export class CommandRemoveMaterial implements Command {
|
||||
static Op = 0x7;
|
||||
|
@ -256,7 +252,7 @@ export class CommandRemoveUnstackableMaterial implements Command {
|
|||
private item: Item;
|
||||
private slot: number;
|
||||
constructor(verb: string,item: Item, slot: number){
|
||||
this.verb = VerbToId[verb as keyof typeof VerbToId] || 0;;
|
||||
this.verb = VerbToId[verb as keyof typeof VerbToId] || 0;
|
||||
this.item = item;
|
||||
this.slot = slot;
|
||||
}
|
||||
|
|
|
@ -4,11 +4,11 @@ import { ItemStack, ItemType } from "./ItemStack";
|
|||
export class Inventory {
|
||||
private slots: ItemStack[] = [];
|
||||
private savedSlots: ItemStack[] = [];
|
||||
private numBroken: number = 0;
|
||||
private isInitialSort: boolean = false;
|
||||
private isAltered: boolean = true;
|
||||
private isSaveAltered: boolean = true;
|
||||
private inaccurate: boolean = false;
|
||||
private numBroken = 0;
|
||||
private isInitialSort = false;
|
||||
private isAltered = true;
|
||||
private isSaveAltered = true;
|
||||
private inaccurate = false;
|
||||
public clone(): Inventory {
|
||||
const other = new Inventory();
|
||||
other.slots = [...this.slots.map(stack=>({...stack}))];
|
||||
|
@ -36,6 +36,11 @@ export class Inventory {
|
|||
public init(stacks: ItemStack[]) {
|
||||
this.savedSlots = [...stacks.map((stack)=>({...stack}))];
|
||||
this.slots = [...stacks.map((stack)=>({...stack}))];
|
||||
this.numBroken = 0;
|
||||
this.isInitialSort = false;
|
||||
this.isAltered = true;
|
||||
this.isSaveAltered = true;
|
||||
this.inaccurate = false;
|
||||
}
|
||||
|
||||
public addBrokenSlots(num: number) {
|
||||
|
@ -56,7 +61,7 @@ export class Inventory {
|
|||
[ItemType.Material]: [],
|
||||
[ItemType.Meal]: [],
|
||||
[ItemType.Key]: []
|
||||
}
|
||||
};
|
||||
for(let i=Math.max(0, this.slots.length-this.numBroken);i<this.slots.length;i++){
|
||||
const stack = this.slots[i];
|
||||
if(!shouldIgnoreOnReload(stack.item)){
|
||||
|
|
|
@ -5,7 +5,6 @@ export enum Item {
|
|||
Glider = "Glider",
|
||||
SpiritOrb = "SpiritOrb",
|
||||
|
||||
|
||||
Lotus = "Lotus",
|
||||
SilentPrincess = "SilentPrincess",
|
||||
Honey = "Honey",
|
||||
|
@ -50,7 +49,7 @@ export const ItemIds = {
|
|||
[Item.Wood]: 0x1f,
|
||||
|
||||
[Item.SpeedFood]: 0x40,
|
||||
}
|
||||
};
|
||||
|
||||
export const itemToType = (item: Item): ItemType => {
|
||||
if (item === Item.Slate || item === Item.Glider || item === Item.SpiritOrb){
|
||||
|
@ -60,15 +59,15 @@ export const itemToType = (item: Item): ItemType => {
|
|||
return ItemType.Meal;
|
||||
}
|
||||
return ItemType.Material;
|
||||
}
|
||||
};
|
||||
|
||||
export const shouldIgnoreOnReload = (item: Item): boolean => {
|
||||
return item === Item.Slate || item === Item.Glider;
|
||||
}
|
||||
};
|
||||
|
||||
export const isStackable = (item: Item): boolean => {
|
||||
return item !==Item.Slate && item !== Item.Glider && item !== Item.SpeedFood;
|
||||
}
|
||||
};
|
||||
|
||||
const KeyItemSortOrderMap = (()=>{
|
||||
const map: {[k in Item]?: number} = {};
|
||||
|
@ -82,7 +81,7 @@ const KeyItemSortOrderMap = (()=>{
|
|||
|
||||
export const getKeyItemSortOrder = (item: Item): number => {
|
||||
return KeyItemSortOrderMap[item] || -1;
|
||||
}
|
||||
};
|
||||
|
||||
const MaterialSortOrderMap = (()=>{
|
||||
const map: {[k in Item]?: number} = {};
|
||||
|
@ -109,4 +108,4 @@ const MaterialSortOrderMap = (()=>{
|
|||
|
||||
export const getMaterialSortOrder = (item: Item): number => {
|
||||
return MaterialSortOrderMap[item] || -1;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -78,4 +78,4 @@ export const parseCommand = (cmdString: string): Command | undefined => {
|
|||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Command, CommandAddMaterial, CommandBreakSlots, CommandInitialize, CommandNothing, CommandReload, CommandRemoveMaterial, CommandRemoveUnstackableMaterial, CommandSave, CommandSortKey, CommandSortMaterial } from "./Command";
|
||||
import { Item } from "./Item";
|
||||
|
||||
const Buffer = require("buffer/").Buffer;
|
||||
const Buffer = require("buffer/").Buffer; /* eslint-disable-line @typescript-eslint/no-var-requires*/
|
||||
|
||||
export const serializeCommands = (commands: Command[]): string => {
|
||||
const sizeBuf: Buffer = Buffer.alloc(4);
|
||||
|
@ -9,9 +9,9 @@ export const serializeCommands = (commands: Command[]): string => {
|
|||
|
||||
const commandBuffers = commands.map(c=>c.toBuffer());
|
||||
const allBufs = Buffer.concat([sizeBuf, ...commandBuffers]);
|
||||
console.log(allBufs);
|
||||
|
||||
return allBufs.toString("base64");
|
||||
}
|
||||
};
|
||||
|
||||
export const deserialzeCommands = (base64str: string): Command[] => {
|
||||
const buf: Buffer = Buffer.from(base64str, "base64");
|
||||
|
@ -62,4 +62,4 @@ export const deserialzeCommands = (base64str: string): Command[] => {
|
|||
}
|
||||
}
|
||||
return commands;
|
||||
}
|
||||
};
|
||||
|
|
168
src/data/FileSaver/FileSaver.js
Normal file
168
src/data/FileSaver/FileSaver.js
Normal file
|
@ -0,0 +1,168 @@
|
|||
/* eslint no-empty:0 */
|
||||
/*
|
||||
* FileSaver.js
|
||||
* A saveAs() FileSaver implementation.
|
||||
*
|
||||
* By Eli Grey, http://eligrey.com
|
||||
*
|
||||
* License : https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md (MIT)
|
||||
* source : http://purl.eligrey.com/github/FileSaver.js
|
||||
*/
|
||||
|
||||
// The one and only way of getting global scope in all environments
|
||||
// https://stackoverflow.com/q/3277182/1008999
|
||||
var _global = typeof window === "object" && window.window === window
|
||||
? window : typeof window.self === "object" && window.self.self === window.self
|
||||
? window.self : typeof window.global === "object" && window.global.global === window.global
|
||||
? window.global
|
||||
: this;
|
||||
|
||||
function bom (blob, opts) {
|
||||
if (typeof opts === "undefined") {opts = { autoBom: false };}
|
||||
else if (typeof opts !== "object") {
|
||||
console.error("Deprecated: Expected third argument to be a object");
|
||||
opts = { autoBom: !opts };
|
||||
}
|
||||
|
||||
// prepend BOM for UTF-8 XML and text/* types (including HTML)
|
||||
// note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
|
||||
if (opts.autoBom && /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
|
||||
return new Blob([String.fromCharCode(0xFEFF), blob], { type: blob.type });
|
||||
}
|
||||
return blob;
|
||||
}
|
||||
|
||||
function download (url, name, opts) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", url);
|
||||
xhr.responseType = "blob";
|
||||
xhr.onload = function () {
|
||||
saveAs(xhr.response, name, opts);
|
||||
};
|
||||
xhr.onerror = function () {
|
||||
console.error("could not download file");
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
function corsEnabled (url) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
// use sync to avoid popup blocker
|
||||
xhr.open("HEAD", url, false);
|
||||
try {
|
||||
xhr.send();
|
||||
} catch (e) {}
|
||||
return xhr.status >= 200 && xhr.status <= 299;
|
||||
}
|
||||
|
||||
// `a.click()` doesn't work for all browsers (#465)
|
||||
function click (node) {
|
||||
try {
|
||||
node.dispatchEvent(new MouseEvent("click"));
|
||||
} catch (e) {
|
||||
var evt = document.createEvent("MouseEvents");
|
||||
evt.initMouseEvent("click", true, true, window, 0, 0, 0, 80,
|
||||
20, false, false, false, false, 0, null);
|
||||
node.dispatchEvent(evt);
|
||||
}
|
||||
}
|
||||
|
||||
// Detect WebView inside a native macOS app by ruling out all browsers
|
||||
// We just need to check for 'Safari' because all other browsers (besides Firefox) include that too
|
||||
// https://www.whatismybrowser.com/guides/the-latest-user-agent/macos
|
||||
var isMacOSWebView = _global.navigator && /Macintosh/.test(navigator.userAgent) && /AppleWebKit/.test(navigator.userAgent) && !/Safari/.test(navigator.userAgent);
|
||||
|
||||
var saveAs = _global.saveAs || (
|
||||
// probably in some web worker
|
||||
typeof window !== "object" || window !== _global
|
||||
? function saveAs () { /* noop */ }
|
||||
|
||||
// Use download attribute first if possible (#193 Lumia mobile) unless this is a macOS WebView
|
||||
: "download" in HTMLAnchorElement.prototype && !isMacOSWebView
|
||||
? function saveAs (blob, name, opts) {
|
||||
var URL = _global.URL || _global.webkitURL;
|
||||
var a = document.createElement("a");
|
||||
name = name || blob.name || "download";
|
||||
|
||||
a.download = name;
|
||||
a.rel = "noopener"; // tabnabbing
|
||||
|
||||
// TODO: detect chrome extensions & packaged apps
|
||||
// a.target = '_blank'
|
||||
|
||||
if (typeof blob === "string") {
|
||||
// Support regular links
|
||||
a.href = blob;
|
||||
if (a.origin !== window.location.origin) {
|
||||
corsEnabled(a.href)
|
||||
? download(blob, name, opts)
|
||||
: click(a, a.target = "_blank");
|
||||
} else {
|
||||
click(a);
|
||||
}
|
||||
} else {
|
||||
// Support blobs
|
||||
a.href = URL.createObjectURL(blob);
|
||||
setTimeout(function () { URL.revokeObjectURL(a.href); }, 4E4); // 40s
|
||||
setTimeout(function () { click(a); }, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Use msSaveOrOpenBlob as a second approach
|
||||
: "msSaveOrOpenBlob" in navigator
|
||||
? function saveAs (blob, name, opts) {
|
||||
name = name || blob.name || "download";
|
||||
|
||||
if (typeof blob === "string") {
|
||||
if (corsEnabled(blob)) {
|
||||
download(blob, name, opts);
|
||||
} else {
|
||||
var a = document.createElement("a");
|
||||
a.href = blob;
|
||||
a.target = "_blank";
|
||||
setTimeout(function () { click(a); });
|
||||
}
|
||||
} else {
|
||||
navigator.msSaveOrOpenBlob(bom(blob, opts), name);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to using FileReader and a popup
|
||||
: function saveAs (blob, name, opts, popup) {
|
||||
// Open a popup immediately do go around popup blocker
|
||||
// Mostly only available on user interaction and the fileReader is async so...
|
||||
popup = popup || window.open("", "_blank");
|
||||
if (popup) {
|
||||
popup.document.title =
|
||||
popup.document.body.innerText = "downloading...";
|
||||
}
|
||||
|
||||
if (typeof blob === "string") {return download(blob, name, opts);}
|
||||
|
||||
var force = blob.type === "application/octet-stream";
|
||||
var isSafari = /constructor/i.test(_global.HTMLElement) || _global.safari;
|
||||
var isChromeIOS = /CriOS\/[\d]+/.test(navigator.userAgent);
|
||||
|
||||
if ((isChromeIOS || force && isSafari || isMacOSWebView) && typeof FileReader !== "undefined") {
|
||||
// Safari doesn't allow downloading of blob URLs
|
||||
var reader = new FileReader();
|
||||
reader.onloadend = function () {
|
||||
var url = reader.result;
|
||||
url = isChromeIOS ? url : url.replace(/^data:[^;]*;/, "data:attachment/file;");
|
||||
if (popup) {popup.location.href = url;}
|
||||
else {window.location = url;}
|
||||
popup = null; // reverse-tabnabbing #460
|
||||
};
|
||||
reader.readAsDataURL(blob);
|
||||
} else {
|
||||
var URL = _global.URL || _global.webkitURL;
|
||||
var url = URL.createObjectURL(blob);
|
||||
if (popup) {popup.location = url;}
|
||||
else {window.location.href = url;}
|
||||
popup = null; // reverse-tabnabbing #460
|
||||
setTimeout(function () { URL.revokeObjectURL(url); }, 4E4); // 40s
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export default saveAs;
|
9
src/data/FileSaver/LICENSE.md
Normal file
9
src/data/FileSaver/LICENSE.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
The MIT License
|
||||
|
||||
Copyright © 2016 Eli Grey.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
8
src/data/FileSaver/index.ts
Normal file
8
src/data/FileSaver/index.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import FileSaverFunction from "./FileSaver";
|
||||
|
||||
export const saveAs = (content: string, filename: string): void =>{
|
||||
const blob = new Blob([content], {
|
||||
type: "text/plain;charset=utf-8"
|
||||
});
|
||||
FileSaverFunction(blob, filename);
|
||||
};
|
|
@ -1,11 +1,11 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import './index.css';
|
||||
import {App} from './App';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import "./index.css";
|
||||
import {App} from "./App";
|
||||
import reportWebVitals from "./reportWebVitals";
|
||||
|
||||
const root = ReactDOM.createRoot(
|
||||
document.getElementById('root') as HTMLElement
|
||||
document.getElementById("root") as HTMLElement
|
||||
);
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { ReportHandler } from 'web-vitals';
|
||||
import { ReportHandler } from "web-vitals";
|
||||
|
||||
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
|
||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||
import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||
getCLS(onPerfEntry);
|
||||
getFID(onPerfEntry);
|
||||
getFCP(onPerfEntry);
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom';
|
||||
import "@testing-library/jest-dom";
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import clsx from "clsx"
|
||||
import { ItemList, ItemListProps } from "components/ItemList"
|
||||
import { Command } from "core/Command"
|
||||
import { itemToType } from "core/Item"
|
||||
import { ItemStack, ItemType } from "core/ItemStack"
|
||||
import { parseCommand } from "core/Parser"
|
||||
import clsx from "clsx";
|
||||
import { ItemList, ItemListProps } from "components/ItemList";
|
||||
import { Command } from "core/Command";
|
||||
import { itemToType } from "core/Item";
|
||||
import { ItemStack, ItemType } from "core/ItemStack";
|
||||
import { parseCommand } from "core/Parser";
|
||||
|
||||
import React, { useEffect, useState } from "react"
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
type DisplayPaneProps = {
|
||||
command: string,
|
||||
|
@ -31,12 +31,12 @@ const stacksToItemListProps = (stacks: ItemStack[], numBroken: number): [ItemLis
|
|||
items: stacksToNameAndCount(keyItems),
|
||||
numBroken
|
||||
},
|
||||
]
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
const stacksToNameAndCount = (stacks: ItemStack[]): ItemListProps["items"] => {
|
||||
return stacks.map(({item, count})=>({name: item, count}));
|
||||
}
|
||||
};
|
||||
|
||||
export const DisplayPane: React.FC<DisplayPaneProps> = ({command,editCommand,displayIndex, stacks, numBroken})=>{
|
||||
const [commandString, setCommandString] = useState<string>("");
|
||||
|
@ -126,6 +126,5 @@ When you reload without altering inventory, things become weird. It won't be han
|
|||
<ItemList {...keyItemListProps}/>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
}
|
||||
</div>;
|
||||
};
|
||||
|
|
Reference in a new issue