Compare commits

...

27 commits

Author SHA1 Message Date
networkmastered
1456c959ba performance improvements 2025-03-27 05:21:13 +00:00
networkmastered
b3828e0935 add huffman encoding, update version, add more tests, and verify compressions 2025-03-27 03:28:11 +00:00
networkmastered
9914f9ca5b fix readme 2025-03-27 03:26:04 +00:00
networkmastered
be25102905 fix up readme 2025-03-25 04:37:32 +00:00
networkmastered
316100ba87 readd 1.0.0? 2025-03-25 04:33:29 +00:00
networkmastered
f11f5458ff remove fund url 2025-03-25 04:21:48 +00:00
networkmastered
0380742c68 remove fund url 2025-03-25 04:06:28 +00:00
networkmastered
4903ca7e9b validation completion 2025-03-25 04:01:00 +00:00
networkmastered
768afb5cb6 validation completion 2025-03-25 03:55:52 +00:00
networkmastered
4d2bac86c0 ver bump V1.1.0 2025-03-11 00:27:05 +00:00
networkmastered
56657f92f7 ver bump V1.1.0 2025-03-11 00:26:31 +00:00
networkmastered
83147c41cf fix reading sizes 2025-03-11 00:23:32 +00:00
networkmastered
d5d3220e73 Add larger dictionary 2025-03-11 00:00:56 +00:00
networkmastered
532be92fa5 test 2025-03-10 19:12:51 +00:00
networkmastered
405f8889f3 Readd checks 2025-03-10 15:20:38 +00:00
networkmastered
4c203ad816 Remove compression checks, already done 2025-03-10 15:18:03 +00:00
networkmastered
bafa5da70a Rewrite compression before release 2025-03-10 15:02:54 +00:00
networkmastered
51c5aa7b02 remove var and fix critical error, and add debug 2025-03-10 12:33:18 +00:00
networkmastered
93fc3b9ba0 Rewrite README 2025-03-09 17:30:49 +00:00
networkmastered
0cb57c0c79 redo first release. Fix versions 2025-03-09 17:14:53 +00:00
networkmastered
304c28dc04 fix lengths 2025-03-09 17:12:13 +00:00
networkmastered
6d301abe44 FIX PAKO COMPRESSION. 2025-03-09 17:05:51 +00:00
netwworkmastered
b7e378255d Update README to contain a warning and remove debugging logs from main. 2025-03-09 15:26:24 +00:00
netwworkmastered
00e0eb3c2b change descritions to match obsidian entry 2025-03-09 15:16:21 +00:00
netwworkmastered
b45f82a214 ReBumpq 2025-03-09 11:17:16 +00:00
netwworkmastered
30af9d7f1c Bump 2025-03-09 11:06:58 +00:00
netwworkmastered
d05ff3aa31 Register Events 2025-03-09 11:05:43 +00:00
22 changed files with 20598 additions and 180 deletions

4
.gitignore vendored
View file

@ -20,3 +20,7 @@ data.json
# Exclude macOS Finder (System Explorer) View States
.DS_Store
util/dictionaryBIGUnusedWiki.json
util/temp.js
archive/versions

View file

@ -1,4 +1,4 @@
Copyright (C) 2020-2025 by Dynalist Inc.
Copyright (C) 2020-2025 by NetworkMastered.
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.

View file

@ -1,36 +1,67 @@
# Obsidian File Compressor
# File Compressor
<i>This is a plugin made for Obsidian.</i>
This plugin is designed to take large files and then compress them. Can typically do most file types. If you are compressing something other than a MD(MarkDown) please copy it so you have a backup. When a file is in a compressed state if its not human-readable or a plugin manages it then it may have issues.
File compressor creates a new file type which is a .ctxt(compressed txt). Although it says txt it does support markdown. The plugin is designed to be as automated as possible. Which means that you do not need to compress and decompress the files yourself. All you need to do is click the "Create new ctxt" button. Which will then create the new ctxt in the root directory of your vault.
Once you have a new ctxt file you just need to edit it like a normal file. The plugin will do the rest. If you ever see the compressed data then you should close the tab and re-open it.
The editor works by taking Obsidian's default markdown editor and then telling it to not refresh it to the saved data(which doesnt always work), then once Obsidian saves the plugin will then copy all of the contents of the file and then compress it.
Only .MD files have been tested. But we plan on making it work with multiple plugins by only compressing the data once youre done! This will come later though. But as of now it should still be able to compress any file. As long as it is just plain text. Once again though if you do convert a file then the plugin wont read it. If the plugin also uses a specific file type then it will not be saved. Instead itll be converted to a MD. This is planned to change.
## Installation
There are two ways to install this plugin which are as follows:
1. Install it from the obsidian community plugins tab.
2. Get the latest main.js and manifest.json from the github releases and drag it into your vault's ".obsidian/plugins/compressor" folder. The "compressor" folder may not be there. If not then you should be able to create one. If you are on linux the .obsidian folder may be hidden, depending on your operating system it will be different. You should be able to look up how to view hidden folders. Or optionally you can cd into it.
You can install/update the plugin in one of two ways:
## How to use
This plugin is made to need as little interaction as possible. All you need to do it create a new ctxt file(compressed text) and then open it up. Then it will do the compression for you.
1. You can install and update the plugin from the Obsidian community plugin marketplace.
If you wish to turn a file to a ctxt you can press the button with 2 arrows which will then convert the file. If you do use this then you will need to minimise and re open obsidian. Or click on the taskbar and back. This is an unknown bug.
2. You may also go to this repositorys github releases and then download the latest main and manifest then go into your vaults directory, open up your .Obsidian folder, if you are on linux you may need to find out how to view hidden files. Then you just need to open up the plugins folder, create a new folder. You can name the folder whatever you like, or just "FileCompressor" or "compressor" then just drag the main and manifest files you downloaded into the folder!
This plugin does support markdown and uses the same editor as markdown files.
## What are the differences like?
The differences cannot be defined. But the bigger the file is the more it should be able to do. At the start it will not compress as it will not be any smaller.
## Buttons
This plugin comes with two buttons.
> **Create CTXT** - creates a new compressed file.
> **Convert to CTXT** - converts the currently open file to a ctxt file.
## Settings
> **Print Results** - Each time a compressed file is saved it will show the compression ratio(percentage of file size)
> **Show File Size** - This setting shows the currently opened/editing file's size in the status bar.
*simplified/stepped:*
- Go to releases and download the main.js and manifest.json files
- Goto your Obsidian vault
- Open vault/.Obsidian/Plugins/compressor (or create compressor folder)
- Place the main.js and manifest.json files you downloaded into the compressor plugin folder.
- Open obsidian or save your files and reload Obsidian(Ctrl+p Reload app without saving)
## NOTICE:
1. Changing a files contents to a more obscure one will come with the risk of data loss.
2. Links do work but will not show up in the Graph.
3. Plugins may fail to read the data.
4. This uses a CLOSED source build script along with esbuild. You will need to remove the " && node archive/versions/execute.js" in the package in order to build.
5. The larger your file is the more intensive the compression system will be. Each time a file is saved it runs multiple compression algorithems. If you are using large files then you may need to
## Uninstalling
>[!WARNING]
>BEFORE UNINSTALLING/DISABLING THIS PLUGIN. CONVERT YOUR FILES BACK TO A MARKDOWN!<br>
If not, you will NOT be able to access compressed files. You will still be able to reinstall the plugin and convert the files.
## Future
We plan on adding better compression and a better system to allow files to be in plaintext while editing.
## What comes packaged:
The compression plugin is packaged with pako, a compression library and a custom one. The plugin also comes with the following
If there are any issues or requests, make sure to create a request!
**features**:
Buttons:
> "**Create new ctxt**" - will create a new .ctxt file.<br>
> "**Convert open file**" - This will convert the currently opened/most recently edited file from whatever it is to a ctxt. If it is a ctxt it gets converted to .md
<br>Settings:
> "**Show compression results**" - This setting shows a toast of the compression ratio of the most recently saved file(if it does get compressed)<br>
> "**Show file storage in statusbar**" - This does what it says and will show the most recently opened/edited file's storage space in bytes at the bottom on the statusbar.
## Compression Ratios:
These ratios will be based off of the bee movie script as it provides a realistic use case and has variations in the words.
*(can be found at https://gist.githubusercontent.com/MattIPv4/045239bc27b16b2bcf7a3a9a4648c08a/raw)*
> What is a compression ratio?<br>a compression ratio is how much the compressed version takes up of the raw version. (25kB compressed of 100kB = 25%, 25kB compressed of 50kB = 50%)
1.0.0-1.0.2-1.1.0 PRE-RELEASE: Error<br>
1.0.0: 55.4% ( Initial Release )<br>
1.1.0: 55.4% ( Ready for publishing )<br>
1.1.2: 55.4% ( Follow Obsidian's publishing )<br>
1.2.2: 55.4% ( Huffman encoding added. )<br>
1.2.5: 55.4% ( Performance Improvements 10s-4s )

12
TODO Normal file
View file

@ -0,0 +1,12 @@
[ ] Finish Compression on plugins
[ ] If compression is only on open file check for ![[]] links for rendering
[ ] Search for more custom data plugins
[ ] Compressed Vault?
[ ] Custom ViewType/Leaf
[ ] Block the data
[ ] checksum the data
[ ] implament some kind of repairer
[ ] Rewrite README to make it more polished and add links for easier reading
[ ] Create a version control system so i dont accidently publish broken or same versions
[ ] Create a setting for "Uncompress entire Vault" and "Uncompress open file and requisite"
[x] Seperate each plugin compression type to a new script

43
archive/README.md Normal file
View file

@ -0,0 +1,43 @@
# Obsidian File Compressor
This plugin is designed to take large files and then compress them. Can typically do most file types. If you are compressing something other than a MD(MarkDown) please copy it so you have a backup. When a file is in a compressed state if its not human-readable or a plugin manages it then it may have issues.
## Installation
There are two ways to install this plugin which are as follows:
1. Install it from the obsidian community plugins tab.
2. Get the latest main.js and manifest.json from the github releases and drag it into your vault's ".obsidian/plugins/compressor" folder. The "compressor" folder may not be there. If not then you should be able to create one. If you are on linux the .obsidian folder may be hidden, depending on your operating system it will be different. You should be able to look up how to view hidden folders. Or optionally you can cd into it.
## How to use
This plugin is made to need as little interaction as possible. All you need to do it create a new ctxt file(compressed text) and then open it up. Then it will do the compression for you.
If you wish to turn a file to a ctxt you can press the button with 2 arrows which will then convert the file. If you do use this then you will need to minimise and re open obsidian. Or click on the taskbar and back. This is an unknown bug.
This plugin does support markdown and uses the same editor as markdown files.
## What are the differences like?
The differences cannot be defined. But the bigger the file is the more it should be able to do. At the start it will not compress as it will not be any smaller.
## Buttons
This plugin comes with two buttons.
> **Create CTXT** - creates a new compressed file.
> **Convert to CTXT** - converts the currently open file to a ctxt file.
## Settings
> **Print Results** - Each time a compressed file is saved it will show the compression ratio(percentage of file size)
> **Show File Size** - This setting shows the currently opened/editing file's size in the status bar.
NOTICE:
- By using this plugin the "Graph View" will NOT show links(if you know of a way to do this, please let me know). Linking to a ctxt file will also not show it.!
- Compressing a file that another plugin edits will stop that plugin from being able to open it, re convert it.
- This plugin is not perfect. Data loss is possible. In the case of dataloss, please make sure that you didnt accidently edit/tamper with the compressed data. Then and only if your fine with sharing the file you could upload an issue request with the contents. DO NOT SHARE PERSONAL INFO!
If there are any issues or requests, make sure to create a request!

61
archive/netwrkjscmp.log Normal file
View file

@ -0,0 +1,61 @@
Generated log file from WDIR/version/execute.js
$> Executed Build.
> compress@1.1.2 build
> node archive/testchecks.js && tsc -noEmit -skipLibCheck && node esbuild.config.mjs production && node archive/versions/execute.js
$> Executed Tests.
Test netcompress 1. Passed ✔
Test netcompress 2. Passed ✔
Test netcompress 3. Passed ✔
Test netcompress 4. Passed ✔
Test netcompress 5. Passed ✔
Test netcompress 6. Passed ✔
Test netcompress 7. Passed ✔
Test netcompress 8. Passed ✔
Test netdecompress 9. Passed ✔
Test netdecompress 10. Passed ✔
Test netdecompress 11. Passed ✔
Test encodeSafe 12. Passed ✔
Test encodeSafe 13. Passed ✔
Test encodeSafe 14. Passed ✔
Test encodeSafe 15. Passed ✔
Test encodeSafe 16. Passed ✔
Test encodeSafe 17. Passed ✔
Test encodeSafe 18. Passed ✔
Test encodeSafe 19. Passed ✔
Test encodeSafe 20. Passed ✔
Test encodeSafe 21. Passed ✔
Test encodeSafe 22. Passed ✔
Test encodeSafe 23. Passed ✔
Test decodeSafe 24. Passed ✔
Test decodeSafe 25. Passed ✔
Test decodeSafe 26. Passed ✔
Test decodeSafe 27. Passed ✔
Test decodeSafe 28. Passed ✔
Test decodeSafe 29. Passed ✔
Test decodeSafe 30. Passed ✔
Test decodeSafe 31. Passed ✔
Test decodeSafe 32. Passed ✔
Test decodeSafe 33. Passed ✔
Test decodeSafe 34. Passed ✔
Test decodeSafe 35. Passed ✔
Finished. 35 have passed 0 have failed 35/0
(node:14788) [MODULE_TYPELESS_PACKAGE_JSON] Warning: Module type of file:///C:/Users/user/Desktop/Repos/TextEditorObsidianPluginPublic/util/BB.js is not specified and it doesn't parse as CommonJS.
Reparsing as ES module because module syntax was detected. This incurs a performance overhead.
To eliminate this warning, add "type": "module" to C:\Users\user\Desktop\Repos\TextEditorObsidianPluginPublic\package.json.
(Use `node --trace-warnings ...` to show where the warning was created)
$> Executed NMJSCompress
VC Created new TS213484. FS 56. FLAGS: --master --full --repush -m -vc --bumpall -wl --save --mini2 --complvl5 -pr --MoveTo:LENV["ObsidianDir"] --ignore:myselfAndParent -wd:ParParPar
Running minifier V2
minifier[LL:WARN]: Level 5 enabled. Level 5 is significently low. Set to approx 10+ (0-100). Use 'execute.js --printlvllist' or 'execute.js --pll' or click: [NetworkMasterCLIEnums.ConsoleLogging.EmbedTypes.New.V2.Button]{NWK::'EXEC',WTR:'/-=E;PR!BtnFt',STL:'BoxButton;BoldAndUnderline',WYD:NetworkMasterCLIEnums.ConsoleLogging.PrintingFunctions.Events.Execute_By_Meta_PREM,PREM:"__DIR.F;CD %F;EXEC_CMD __DIR.CR -pll"}(This.)
Size Difference: 857 B
Copied over files to: LENV.ObsidianDir.
$> Execution Finished
$> ?? --wl detected. Written to wd[wiesl]/netwrkjscmp.log

77
archive/testchecks.js Normal file
View file

@ -0,0 +1,77 @@
const { encodeSafe, decodeSafe } = require("../util/runlength")
const LZ = require("../util/runlengthMatch")
const { netcompress, netdecompress } = require("../util/networkmastercompression")
const funcs = [netcompress, netdecompress, encodeSafe, decodeSafe, LZ.compress, LZ.decompress]
const names = ["netcompress", "netdecompress", "encodeSafe", "decodeSafe", "LZcompress", "LZdecompress"]
const tests = [
{ type: 0, inputs: ["hi", "type1"], output: "hABoE" },
{ type: 0, inputs: ["hi,", "type1"], output: "UNCOMPhi," },
{ type: 0, inputs: ["abcdefg formats", "type2"], output: "óBUBiQDRURGdESBcEUT5kQVRF" },
{ type: 0, inputs: ["master file true", "type3"], output: "hBBoOabmMbXmNdL31dZA" },
{ type: 0, inputs: ["zygote", "type3"], output: "hBB8_D" },
{ type: 0, inputs: ["zygotezygote", "type3"], output: "hBB8_z_P" },
{ type: 0, inputs: ["hello World", "type4"], output: "BCBhello World" },
{ type: 0, inputs: ["test test test test test", "type5"], output: "hCB4-6C3XX4-6C⁑" },
{ type: 0, inputs: ["test test test test test", "type6"], output: "BDBhAB0y0D;FsmTzp50caA" },
{ type: 1, inputs: ["UNCOMPhi,"], output: "hi,"},
{ type: 1, inputs: ["hAfoE"], output: "hi" },
{ type: 1, inputs: ["BCfhello World"], output: "hello World" },
{ type: 1, inputs: ["hCB4-6C3XX4-6C•\" K"], output: "test test test test test" },
{ type: 1, inputs: ["BDfhAf0y0D;FsmTzp50caA"], output: "test test test test test" },
{ type: 2, inputs: ["A"], output: "A" },
{ type: 2, inputs: ["AA"], output: "AA" },
{ type: 2, inputs: ["AAA"], output: "óA" },
{ type: 2, inputs: ["AAAA"], output: "úA" },
{ type: 2, inputs: ["AAAAA"], output: "üA" },
{ type: 2, inputs: ["AAAAAA"], output: "ñA" },
{ type: 2, inputs: ["AAAAAAA"], output: "çA" },
{ type: 2, inputs: ["AAAAAAAA"], output: "¿A" },
{ type: 2, inputs: ["AAAAAAAAA"], output: "¡A" },
{ type: 2, inputs: ["AAAAAAAAAA"], output: "éáA" },
{ type: 2, inputs: ["AAAAAAAAAAA"], output: "ééA" },
{ type: 2, inputs: ["AAAAAAAAAAAA"], output: "éíA" },
{ type: 3, inputs: ["A"], output: "A" },
{ type: 3, inputs: ["AA"], output: "AA" },
{ type: 3, inputs: ["óA"], output: "AAA" },
{ type: 3, inputs: ["úA"], output: "AAAA" },
{ type: 3, inputs: ["üA"], output: "AAAAA" },
{ type: 3, inputs: ["ñA"], output: "AAAAAA" },
{ type: 3, inputs: ["çA"], output: "AAAAAAA" },
{ type: 3, inputs: ["¿A"], output: "AAAAAAAA" },
{ type: 3, inputs: ["¡A"], output: "AAAAAAAAA" },
{ type: 3, inputs: ["éáA"], output: "AAAAAAAAAA" },
{ type: 3, inputs: ["ééA"], output: "AAAAAAAAAAA" },
{ type: 3, inputs: ["éíA"], output: "AAAAAAAAAAAA" },
// { type: 0, inputs: ["hi"], output: "" },
]
var pass = 0
var fail = 0
for (let idx = 0; idx < tests.length; idx++) {
let test = tests[idx]
var name = names[test.type]
var func = funcs[test.type]
var out = ""
try {
out = func(...test.inputs)
} catch (err) {
throw new Error(err)
}
if (test.output == out) {
console.log(`\u001b[32mTest ${name} ${idx + 1}. Passed ✔\u001b[30m`)
pass++
} else {
console.log(`\u001b[31mTest ${name} ${idx + 1}. Failed ❌(${out} ~= ${test.output})\u001b[30m`)
fail++
}
}
console.log(`Finished. \u001b[32m${pass} have passed\u001b[31m ${fail} have failed \u001b[32m${pass}\u001b[30m/\u001b[31m${fail}\u001b[30m`)
if (fail > 0) {
throw new Error("failed")
}

View file

@ -1,13 +1,11 @@
import esbuild from "esbuild";
import process from "process";
import fs from "fs";
import path from "path";
import builtins from "builtin-modules";
import { execSync } from "child_process";
const banner =
`/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin
*/
`;
const banner = ""
const prod = (process.argv[2] === "production");
@ -41,9 +39,52 @@ const context = await esbuild.context({
minify: prod,
});
const cyrb53 = (str, seed = 0) => {
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);
h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
};
async function fscn(pth = "./") {
let hashr = '';
let files = fs.readdirSync(pth)
for (let i = 0; i < files.length; i++) {
let file = files[i]
const filePath = path.join(pth, file)
if (fs.statSync(filePath).isFile()) {
hashr += fs.readFileSync(filePath, 'utf-8')
} else if (fs.statSync(filePath).isDirectory() && !filePath.includes("node_modules") && !filePath.includes("copies") && !filePath.includes(".git")) {
hashr += await fscn(filePath)
}
}
return hashr
}
if (prod) {
await context.rebuild();
process.exit(0);
} else {
let lh = 0
setTimeout(async () => {
let ls = async () => {
await fscn().then((data) => {
let nh = cyrb53(data, 1)
if (lh != nh) {
execSync("node archive/versions/execute.js", { stdio: "inherit" })
lh = nh
}
})
await new Promise((r)=>setTimeout(r,250))
ls()
}
ls()
}, 1)
await context.watch();
}

330
main.ts
View file

@ -1,38 +1,44 @@
import { App, MarkdownView, Notice, Plugin, PluginSettingTab, Setting } from 'obsidian';
import { inflate, deflate } from "./util/pakoMinified"
import { BB } from "./util/BB"
import { encodeSafe, decodeSafe } from "./util/runlength"
import { App, MarkdownView, Notice, Plugin, PluginSettingTab, Setting, FileView } from 'obsidian';
// import { inflate, deflate } from "./util/pako"
// import { encodeSafe, decodeSafe } from "./util/runlength"
import { netcompress, netdecompress, plugincompress, plugindecompress } from "./util/networkmastercompression"
interface compressorSettingsData {
// mySetting: string;
PrintResult: boolean;
FileSize: boolean;
LeaveRawLinks: boolean;
Debug: boolean;
// PakoCompress: boolean;
ExcalidrawPrecision: number;
}
const DEFAULT_SETTINGS: compressorSettingsData = {
// mySetting: 'default',
PrintResult: true,
FileSize: true,
LeaveRawLinks: true
PrintResult: false,
FileSize: false,
LeaveRawLinks: true,
Debug: false,
// PakoCompress: true,
ExcalidrawPrecision: 2,
}
var globalLeafs: any[] = []
var NoticePool: Notice[] = []
var statusBarItemEl2: (undefined | HTMLElement);
let globalLeafs: any[] = []
let NoticePool: Notice[] = []
let statusBarItemEl2: (undefined | HTMLElement);
export default class compressorPlugin extends Plugin {
settings: compressorSettingsData;
async onload() {
var loadTime = new Date().getTime()
let loadTime = new Date().getTime()
this.registerExtensions(["ctxt"], "ctxt")
this.registerView("ctxt",
(leaf) => {
var hook = new MarkdownView(leaf)
var f = setInterval(() => {
var file = hook.file || this.app.workspace.getActiveFile()
let hook = new MarkdownView(leaf)
let f = setInterval(() => {
let file = hook.file || this.app.workspace.getActiveFile()
if (file) {
clearInterval(f)
if (file && file.extension == "ctxt") {
@ -42,12 +48,13 @@ export default class compressorPlugin extends Plugin {
hook.editor.refresh = () => { }
globalLeafs.push(["ctxt", file.path, hook])
this.app.vault.read(file).then((data) => {
if (file && data && (!data.includes("\n") && !data.includes(" "))) {
// if (file && data && (!data.includes("\n") && !data.includes(" "))) {
if (file && data && checkCompFile(data)) {
new Notice("Attempting to load.")
setTimeout(() => {
var decompress = null
let decompress = null
try {
decompress = decompressfile(data)
decompress = decompressfile(data, this.settings.Debug || false)
} catch (err) { console.log(err) }
if (decompress) {
hook.setViewData(decompress, true)
@ -60,13 +67,11 @@ export default class compressorPlugin extends Plugin {
})
}
}
}, 10)
return hook
}
)
this.app.workspace.on("file-open", (file) => {
this.registerEvent(this.app.workspace.on("file-open", (file) => {
if (file && file.extension == "ctxt" && globalLeafs.length <= 0 && (new Date().getTime() - loadTime) < 1000) {
this.app.workspace.getMostRecentLeaf()?.openFile(file)
}
@ -75,24 +80,23 @@ export default class compressorPlugin extends Plugin {
if (statusBarItemEl2 && this.settings.FileSize) statusBarItemEl2.setText(data.length + "B")
})
}
})
}))
this.registerEvent(this.app.vault.on('modify', (file) => {
var file2 = this.app.vault.getFileByPath(file.path)
let file2 = this.app.vault.getFileByPath(file.path)
if (file2 && file2.extension == "ctxt") {
this.app.vault.read(file2).then((data) => {
if (statusBarItemEl2 && this.settings.FileSize) statusBarItemEl2.setText(data.length + "B")
if (file2 && data && (data.includes("\n") || data.includes(" "))) {
var compress = null
// if (file2 && data && (data.includes("\n") || data.includes(" "))) {
if (file2 && data && !checkCompFile(data)) {
let compress = null
try {
NoticePool.forEach((n) => {
if (n) n.hide()
})
compress = compressfile(data, false, this.settings)
compress = compressfile(data, this.settings.Debug || false, this.settings)
} catch (err) { console.log(err) }
if (compress) {
globalLeafs.forEach((leaf) => {
console.log(leaf)
if (leaf[0] == "ctxt" && leaf[2] == file.path && leaf[3]) {
// leaf[3].editor.setLine("")
leaf[3].unload()
@ -105,7 +109,6 @@ export default class compressorPlugin extends Plugin {
leaf[3].setViewData(data, true)
}
})
console.log(file.name + " has been saved")
}
}
})
@ -122,10 +125,10 @@ export default class compressorPlugin extends Plugin {
// this.startAction()
// });
const ribbonIconEl2 = this.addRibbonIcon('checkmark', 'Create new ctxt', (evt: MouseEvent) => {
var filen = `newctxtfile${(new Date().getTime().toString().substring(10))}${Math.floor(Math.random() * 500)}.ctxt`
let filen = `newctxtfile${(new Date().getTime().toString().substring(10))}${Math.floor(Math.random() * 500)}.ctxt`
this.app.vault.create("./" + filen, "Write away!")//.then((filex) => {
// setTimeout(()=>{
// var file = this.app.vault.getFileByPath(filen)
// let file = this.app.vault.getFileByPath(filen)
// if (file) {
// console.log(file, this.app.workspace.getMostRecentLeaf())
// this.app.workspace.getMostRecentLeaf()?.openFile(file)
@ -136,14 +139,13 @@ export default class compressorPlugin extends Plugin {
});
const ribbonIconEl3 = this.addRibbonIcon('up-and-down-arrows', 'Convert currently opened file', (evt: MouseEvent) => {
if (confirm("Are you sure you want to convert")) { //will this work? electron confirms dont exit. Oh it does
var file = this.app.workspace.getActiveFile()
console.log(file)
let file = this.app.workspace.getActiveFile()
if (file) {
if (file.extension == "ctxt") {
this.app.vault.read(file).then((data) => {
var raw = data
let raw = data
try {
var dc = decompressfile(data, true)
let dc = decompressfile(data, this.settings.Debug || false)
if (dc) raw = dc
} catch (_) {
new Notice("Failed to decompress, plainText?")
@ -165,9 +167,9 @@ export default class compressorPlugin extends Plugin {
})
} else {
this.app.vault.read(file).then((data) => {
var raw = data
let raw = data
try {
var dc = compressfile(data, true)
let dc = compressfile(data, this.settings.Debug || false)
if (dc) raw = dc
} catch (_) {
new Notice("Failed to compress.")
@ -199,13 +201,13 @@ export default class compressorPlugin extends Plugin {
//set elsewhere
statusBarItemEl2 = this.addStatusBarItem();
statusBarItemEl2.setText("");
statusBarItemEl2.title = "File Size"
statusBarItemEl2.title = "File size"
this.addCommand({
id: 'createctxt',
name: 'Create a new ctxt file in root',
callback: () => {
var filen = `newctxtfile${(new Date().getTime().toString().substring(10))}${Math.floor(Math.random() * 500)}.ctxt`
let filen = `newctxtfile${(new Date().getTime().toString().substring(10))}${Math.floor(Math.random() * 500)}.ctxt`
this.app.vault.create("./" + filen, "Write away!")
}
});
@ -214,26 +216,18 @@ export default class compressorPlugin extends Plugin {
// this.registerInterval(window.setInterval(() => {
// var file = this.app.workspace.getActiveFile()
// let file = this.app.workspace.getActiveFile()
// if (file != null) {
// this.app.vault.read(file).then((data) => {
// if (!data.includes("\n") && /[A-z0-9\-_]/gm.test(data) && !data.includes(" ")) {
// var bb = new BB()
// bb.G(data)
// var precont = []
// while (true) {
// var now = bb.RU()
// if (now >= 0) {
// precont[precont.length] = now
// } else break
// }
// var cont = new Uint8Array(precont)
// if (cont && cont.length > 0) {
// var decomp = inflate(cont)
// if (typeof (decomp) == "object") {
// if (pane) pane.innerHTML += `<div id="AEDTCOMPDNEOUTSNTTIS!" style="position: absolute;left:0%;right:0%;width:100%;height:100%;background-color:rgba(20,20,20,0.6);z-index: 99999999999;">Content Compressed.</div>`
// }
// let isctxt = false
// try {
// decompressfile(data)
// isctxt = true
// } catch (_) { }
// if (isctxt) {
// if (pane) pane.innerHTML += `<div id="AEDTCOMPDNEOUTSNTTIS!" style="position: absolute;left:0%;right:0%;width:100%;height:100%;background-color:rgba(20,20,20,0.6);z-index: 99999999999;">Content Compressed.</div>`
// }
// }
// })
@ -243,9 +237,49 @@ export default class compressorPlugin extends Plugin {
// }
// }, 100));
//<div style="position: absolute;left:0%;right:0%;width:100%;height:100%;background-color:rgba(20,20,20,0.6);z-index: 99999999999;">Content Compressed.</div>
//NOT FINISHED.
// ; (async () => {
// while (true) {
// await new Promise((r) => { setTimeout(r, 100) })
// if (this.app.vault.getFiles().length > 0) break
// }
// var hn = false
// this.app.vault.getFiles().forEach((file) => {
// console.log(file.name)
// this.app.vault.read(file).then((data) => {
// if (file) {
// if (data.includes("excalidraw") && data.includes("```compressed-json") && !hn) {
// alert("File Compressor: You have excalidraw installed with compression, please go to settings -> excalidraw -> Saving -> Compression and disable it. That way it can be compressed by this plugin. Source cause: " + file.path)
// hn = true
// }
// // var out = plugindecompress(file, data)
// plugincompress(file, data, this.settings)
// var out = plugindecompress(file, data)
// // if (out) this.app.vault.modify(file, out)
// console.log("WRITER DISABLED.")
// }
// })
// })
// })()
this.app.workspace.on("quit", () => {
//NOT FINISHED.
// this.app.vault.getFiles().forEach((file) => {
// console.log(file.name)
// this.app.vault.read(file).then((data) => {
// if (file) {
// var out = plugincompress(file, data)
// if (out) this.app.vault.modify(file, out)
// }
// })
// })
})
}
onunload() {
alert("In order to view CTXT files you will need to re-enable the plugin. If you are deleting the plugin, please convert your files back before.")
// alert("In order to view CTXT files you will need to re-enable the plugin. If you are deleting the plugin, please convert your files back before.")
}
async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
@ -254,64 +288,113 @@ export default class compressorPlugin extends Plugin {
await this.saveData(this.settings);
}
}
// function compressfile(data: string, notice?: boolean, settings?: compressorSettingsData) {
// if (notice) new Notice("Started", 3000)
// const comp = deflate(data)
// let inf = inflate(comp)
// if (inf && typeof (inf) != "string" && new TextDecoder().decode(inf) != data) {
// if (notice) new Notice("Revert is corrupted.", 3000)
// throw new Error("Revert not same,")
// }
// if (comp) {
// if (notice) new Notice("MEST", 3000)
// let bb = new BB()
// comp.forEach((byte: Number) => {
// // console.log(byte)
// bb.WU(8,byte)
// })
// if (notice) new Notice("SWAP", 3000)
// // let linkblk = ""
// // ;[...data.matchAll(/\[\[[^\]]*\]\]/gm)].forEach((match) => {
// // console.log(match)
// // linkblk += match[0]
// // })
// let encd = encodeSafe(bb.G())
// if (notice) new Notice("RLE", 3000)
// if (encd) {
// if (notice) new Notice("METADATA SET.", 3000)
// let content = `DATABLK` + encd
// if (content && content.length < data.length) {
// if (notice) new Notice("Success!")
// if (settings && settings.PrintResult) {
// NoticePool.push(new Notice(`Compression:\nratio:${((content.length / data.length) * 100).toFixed(1)}%`))
// }
// return content
// } else {
// if (notice) new Notice("File isnt smaller.")
// }
// } else {
// new Notice("Encoder failure.")
// }
// }
// }
// function decompressfile(data: string, notice?: boolean) {
// if (notice) new Notice("START", 3000)
// let bb = new BB()
// bb.F(decodeSafe(data.substring(data.indexOf("DATABLK") + 7)))
// let precont: number[] = []
// while (true) {
// let now = bb.RU(8)
// if (!isNaN(now)) {
// precont[precont.length] = now
// } else break
// }
// if (notice) new Notice("OUT", 3000)
// let cont = new Uint8Array(precont)
// if (notice) console.log(precont)
// if (notice) console.log(cont)
// if (notice) new Notice("SWAP", 3000)
// if (cont && cont.length > 0) {
// if (notice) new Notice("INF", 3000)
// let decomp = inflate(cont)
// if (notice) console.log(decomp)
// if (notice) new Notice("DONE, DECODE", 3000)
// if (typeof (decomp) == "object") {
// decomp = new TextDecoder().decode(decomp)
// if (notice) new Notice("Success!")
// return decomp
// } else {
// if (notice) new Notice("Failed.")
// }
// } else {
// if (notice) new Notice("Couldnt Decompress.")
// }
// }
function compressfile(data: string, notice?: boolean, settings?: compressorSettingsData) {
const comp = deflate(data)
var inf = inflate(comp)
if (inf && new TextDecoder().decode(inf) != data) {
if (notice) new Notice("Revert is corrupted.", 3000)
throw new Error("Revert not same,")
}
if (comp) {
var bb = new BB()
comp.forEach((byte) => {
// console.log(byte)
bb.WU(byte)
})
// var linkblk = ""
// ;[...data.matchAll(/\[\[[^\]]*\]\]/gm)].forEach((match) => {
// console.log(match)
// linkblk += match[0]
// })
var encd = encodeSafe(bb.G())
if (encd) {
var content = `DATABLK` + encd
if (content && content.length < data.length) {
if (notice) new Notice("Success!")
if (settings && settings.PrintResult) {
NoticePool.push(new Notice(`Compression:\nratio:${((content.length / data.length) * 100).toFixed(1)}%`))
}
return content
} else {
if (notice) new Notice("File isnt smaller.")
}
} else {
new Notice("Encoder failure.")
if (notice) new Notice("Started C", 3000)
let result: (string | undefined) = ""
try {
result = netcompress(data, null, settings)
if (notice) new Notice("Done!")
if (settings && settings.PrintResult && result) {
NoticePool.push(new Notice(`Compression:\nratio:${((result.length / data.length) * 100).toFixed(1)}%`))
}
} catch (err) {
new Notice(err, 3000)
throw new Error(err)
}
if (result == "" || !result) throw new Error("Empty")
return result
}
function decompressfile(data: string, notice?: boolean) {
var bb = new BB()
bb.F(decodeSafe(data.substring(data.indexOf("DATABLK") + 7)))
var precont: number[] = []
while (true) {
var now = bb.RU()
if (!isNaN(now)) {
precont[precont.length] = now
} else break
}
var cont = new Uint8Array(precont)
if (cont && cont.length > 0) {
var decomp = inflate(cont)
if (typeof (decomp) == "object") {
decomp = new TextDecoder().decode(decomp)
if (notice) new Notice("Success!")
return decomp
} else {
if (notice) new Notice("Failed.")
}
} else {
if (notice) new Notice("Couldnt Decompress.")
if (notice) new Notice("Started D", 3000)
let result: (string | undefined) = ""
try {
result = netdecompress(data)
if (notice) new Notice("Done!")
} catch (err) {
new Notice(err, 3000)
throw new Error(err)
}
if (result == "" || !result) throw new Error("Empty")
return result
}
function checkCompFile(data: string) {
var state = false
try {
state = (netdecompress(data) || "").length > 0
} catch (_) { }
return state
}
class compressorSettings extends PluginSettingTab {
plugin: compressorPlugin;
@ -344,8 +427,41 @@ class compressorSettings extends PluginSettingTab {
this.plugin.settings.FileSize = value;
await this.plugin.saveSettings();
}));
// new Setting(containerEl)
// .setName('Pako')
// .setDesc('Use Pako to improve compression. May be intensive.')
// .addToggle(bool => bool
// .setValue(this.plugin.settings.PakoCompress)
// .onChange(async (value) => {
// this.plugin.settings.PakoCompress = value;
// await this.plugin.saveSettings();
// }));
new Setting(containerEl)
.setName('NOTICE:')
.setDesc('Unfortunetly the graph view will not maintain links. The graph cannot read the compressed data. Nor if the links are raw')
new Setting(containerEl)
.setName('Debug')
.setDesc('Toasts all the events that are happening. Useful for debugging.')
.addToggle(bool => bool
.setValue(this.plugin.settings.Debug)
.onChange(async (value) => {
this.plugin.settings.Debug = value;
await this.plugin.saveSettings();
}));
// new Setting(containerEl)
// .setName('-----Plugin Data Compression-----')
// .setDesc('Settings for compressing data that plugins save to vault')
//useless excelidraw uses whole offsets
// var ex_DP = new Setting(containerEl)
// .setName('Excalidraw: Decimal Precision')
// .setDesc(`How many decimals to have when saving numbers (${this.plugin.settings.ExcalidrawPrecision} DP)`)
// .addSlider(num => num
// .setValue(this.plugin.settings.ExcalidrawPrecision)
// .setLimits(0, 5, 1)
// .onChange(async (value) => {
// this.plugin.settings.ExcalidrawPrecision = value;
// ex_DP.setDesc(`How many decimals to have when saving numbers (${this.plugin.settings.ExcalidrawPrecision} DP) ${(this.plugin.settings.ExcalidrawPrecision==0)?"NO DECIMALS(VERY BAD)":""}`)
// await this.plugin.saveSettings();
// }));
}
}

View file

@ -1,11 +1,10 @@
{
"id": "compress",
"name": "File Compressor",
"version": "1.0.0",
"version": "1.2.5",
"minAppVersion": "0.15.0",
"description": "This plugin will add the .ctxt file type which will allow you to compress notes to a smaller size.",
"description": "Make your files smaller with compression!",
"author": "networkmastered",
"authorUrl": "https://github.com/networkmastered",
"fundingUrl": "https://github.com/networkmastered",
"isDesktopOnly": false
}
}

2
package-lock.json generated
View file

@ -1,6 +1,6 @@
{
"name": "compress",
"version": "1.0.0",
"version": "1.2.5",
"lockfileVersion": 3,
"requires": true,
"packages": {

View file

@ -1,11 +1,11 @@
{
"name": "compress",
"version": "1.0.0",
"description": "This plugin will add the .ctxt file type which will allow you to compress notes to a smaller version.\nNOTE: If you create a link in a compressed file it will NOT show in graph view.\nWhen using this plugin data loss is possible, we've tried to minimise it. But theres always a change. If you do experiance data loss we are not liable. But you may create an issue request with the raw data(do not provide personal info).",
"version": "1.2.5",
"description": "Make your files smaller with compression!",
"main": "main.js",
"scripts": {
"dev": "node esbuild.config.mjs",
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
"dev": "node archive/testchecks.js && tsc -noEmit -skipLibCheck && node esbuild.config.mjs",
"build": "node archive/testchecks.js && tsc -noEmit -skipLibCheck && node esbuild.config.mjs production && node archive/versions/execute.js",
"version": "node version-bump.mjs && git add manifest.json versions.json"
},
"keywords": [],

View file

@ -17,6 +17,7 @@ export class BB {
ch = Math.floor(ch / 2)
}
}
this.PT = 0
}
G() {
let str = ""
@ -33,33 +34,19 @@ export class BB {
}
return str
}
WU(value) {
this.BB[this.PT + 1] = value % 2
value = Math.floor(value / 2)
this.BB[this.PT + 2] = value % 2
value = Math.floor(value / 2)
this.BB[this.PT + 3] = value % 2
value = Math.floor(value / 2)
this.BB[this.PT + 4] = value % 2
value = Math.floor(value / 2)
this.BB[this.PT + 5] = value % 2
value = Math.floor(value / 2)
this.BB[this.PT + 6] = value % 2
value = Math.floor(value / 2)
this.BB[this.PT + 7] = value % 2
value = Math.floor(value / 2)
this.BB[this.PT + 8] = value % 2
value = Math.floor(value / 2)
this.PT += 8
for (let i = 0; i < 8; i++) {
WU(r, val) {
for (let i = 0; i < r; i++) {
this.PT++
this.BB[this.PT] = value % 2
value = Math.floor(value / 2)
this.BB[this.PT] = val % 2
val = Math.floor(val / 2)
}
}
RU() {
let r = this.BB[this.PT+1] * this.PO[0] + this.BB[this.PT + 2] * this.PO[1] + this.BB[this.PT + 3] * this.PO[2] + this.BB[this.PT + 4] * this.PO[3] + this.BB[this.PT + 5] * this.PO[4] + this.BB[this.PT + 6] * this.PO[5] + this.BB[this.PT + 7] * this.PO[6] + this.BB[this.PT + 8] * this.PO[7]
this.PT += 8
RU(val) {
let r = 0
for (let i = 0; i < val; i++) {
this.PT++
r += this.BB[this.PT] * this.PO[i]
}
return r
}
}

4098
util/dictionaryBIG.json Normal file

File diff suppressed because it is too large Load diff

8193
util/dictionaryBIG2.json Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

6890
util/pako.js Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,148 @@
export function CanDo(file, str, set) {
console.log(str.startsWith(`---\n\nexcalidraw-plugin: parsed\ntags: [excalidraw]\n\n---\n==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠== You can decompress Drawing data with the command palette: \'Decompress current Excalidraw file\'. For more info check in plugin settings under \'Saving\'\n\n\n# Excalidraw Data\n\n`), str.includes("```json"), str)
return str.startsWith(`---\n\nexcalidraw-plugin: parsed\ntags: [excalidraw]\n\n---\n==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠== You can decompress Drawing data with the command palette: \'Decompress current Excalidraw file\'. For more info check in plugin settings under \'Saving\'\n\n\n# Excalidraw Data\n\n`) && str.includes("```json")
}
export function CanUnDo(file, str) {
console.log(str.startsWith(`---\n\nexcalidraw-plugin: parsed\ntags: [excalidraw]\n\n---\n==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠== You can decompress Drawing data with the command palette: \'Decompress current Excalidraw file\'. For more info check in plugin settings under \'Saving\'\n\n\n# Excalidraw Data\n\n`), str.includes("```json"), str)
return str.startsWith("COMPDATA_-EXCALI:")
}
var brushTypes = [
"line",
"ellipse",
"diamond",
"arrow",
"freedraw",
"image",
"text",
"rectangle",
]
var Styles = {
fills: ["cross-hatch", "hachure", "solid"],
stroke: ["dotted", "dashed", "solid"],
}
class saveconstruct {
constructor(intl, type) {
this.out = intl || ""
this.type = type
}
addData(data) {
if (this.type && this.type == "master") {
this.out += data
} else {
this.out += data + ","
}
}
export() {
if (this.type == "master") return this.out
return this.out.substring(0, this.out.length - 1)
}
}
export function Compress(file, str, set, BB) {
// try {
var textdata = str.substring(str.indexOf("## Text Elements"), str.indexOf("%%")).substring(17)
var lack = str.substring(str.indexOf("## Drawing"), str.indexOf("%%", str.indexOf("## Drawing"))).substring(18).trim()
lack = lack.substring(0, lack.length - 3).trim()
console.log(lack)
var drawdata = JSON.parse(lack)
if (drawdata && drawdata) {
let meta = new BB()
meta.WU(5, drawdata.version || 2)
meta.WU(5, set.ExcalidrawPrecision)
drawdata.source.substring(69).split(".").forEach((v) => {
meta.WU(5, v)
})
let bld = new saveconstruct(meta.G(), "master")
// let bld = new saveconstruct(drawdata.version + "" + set.ExcalidrawPrecision + "@" + drawdata.source.substring(69), "master")
console.log(drawdata)
drawdata.elements.forEach((stroke) => {
let localStroke = new saveconstruct(";" + stroke.id + ":")
// let localStroke = stroke.id + ":"
localStroke.addData(brushTypes.indexOf(stroke.type))
localStroke.addData(Math.round(stroke.x * (10 ** set.ExcalidrawPrecision)))
localStroke.addData(Math.round(stroke.y * (10 ** set.ExcalidrawPrecision)))
localStroke.addData(Math.round(stroke.width * (10 ** set.ExcalidrawPrecision)))
localStroke.addData(Math.round(stroke.height * (10 ** set.ExcalidrawPrecision)))
localStroke.addData(Math.round(stroke.angle * (10 ** set.ExcalidrawPrecision)) % 360)
if (stroke.strokeColor.startsWith("#")) {
localStroke.addData(stroke.strokeColor.substring(1))
} else {
localStroke.addData(stroke.strokeColor)
}
if (stroke.backgroundColor.startsWith("#")) {
localStroke.addData(stroke.backgroundColor.substring(1))
} else {
localStroke.addData(stroke.backgroundColor)
}
if (stroke.backgroundColor.startsWith("#")) {
localStroke.addData(stroke.backgroundColor.substring(1))
} else {
localStroke.addData(stroke.backgroundColor)
}
localStroke.addData(Styles.fills.indexOf(stroke.fillStyle))
localStroke.addData(stroke.strokeWidth || 0)
localStroke.addData(Styles.stroke.indexOf(stroke.strokeStyle))
localStroke.addData(stroke.roughness || 0)
localStroke.addData(stroke.opacity >= 100 ? -1 : stroke.opacity)
localStroke.addData((stroke.groupIds && stroke.groupIds.length > 0) ? JSON.stringify(stroke.groupIds) : 0)
localStroke.addData(stroke.frameId || "")
localStroke.addData(stroke.index)
if (stroke.roundness) {
localStroke.addData(stroke.roundness.type || 0)
} else {
localStroke.addData(0)
}
if (stroke.seed && stroke.roughness > 0) {
localStroke.addData(stroke.seed)
} else {
localStroke.addData(0)
}
localStroke.addData(stroke.version || 0)
localStroke.addData(stroke.versionNonce || 0)
localStroke.addData(stroke.isDeleted ? 1 : 0)
localStroke.addData((stroke.boundElements && stroke.boundElements.length > 0) ? JSON.stringify(stroke.boundElements) : 0)
localStroke.addData(stroke.updated)
if (stroke.link) {
if (stroke.link.startsWith("https://")) {
localStroke.addData("”" + stroke.link.substring(8) || "")
} else if (stroke.link.startsWith("http://")) {
localStroke.addData("⁕" + stroke.link.substring(7) || "")
} else {
localStroke.addData(stroke.link || "")
}
}
localStroke.addData(stroke.locked ? 1 : 0)
bld.addData(localStroke.export())
})
console.log(bld.export())
return "COMPDATA_-EXCALI:" + bld.export()
} else {
throw new Error("OOD")
}
return str
// } catch (e) { return undefined }
}
export function Decompress(file, str, BB) {
str = str.substring(7)
console.log(str)
}

View file

@ -29,9 +29,9 @@ function DNum(encoded) {
}
//V2
export function encodeSafe(str) {
var count = 0
var typ = ""
var out = ""
let count = 0
let typ = ""
let out = ""
str.split("").forEach((char) => {
if (char != typ && typ != "") {
if (count > 2) {
@ -56,8 +56,8 @@ export function encodeSafe(str) {
}
}
export function decodeSafe(str) {
var count = ""
var out = ""
let count = ""
let out = ""
str.split("").forEach((char) => {
if (charMap.includes(char)) {
count += char

242
util/runlengthMatch.js Normal file
View file

@ -0,0 +1,242 @@
(function () {
'use strict';
// - - -
// # LZ77
// ** A minimal LZ77 [de]compressor **
var LZ77 = function () {
// ##### Private Variables
var
self = {}
, settings = {
refPrefix: '`',
refIntBase: 96,
refIntFloorCode: ' '.charCodeAt(0),
refIntCeilCode: undefined,
maxStringDistance: undefined,
minStringLength: 5,
maxStringLength: undefined,
defaultWindow: 144,
maxWindow: undefined,
windowLength: undefined
}
;
// ##### Public Variables
// ##### Private Methods
var setup = function (params) {
params = params || {};
settings = extend(settings, params);
settings.refIntCeilCode = settings.refIntFloorCode + settings.refIntBase - 1;
settings.maxStringDistance = Math.pow(settings.refIntBase, 2) - 1;
settings.maxStringLength = Math.pow(settings.refIntBase, 1) - 1 + settings.minStringLength;
settings.maxWindow = settings.maxStringDistance + settings.minStringLength;
};
// #### each()
// >`@param obj [collection]` our source collection
// >`@param iterator [function]` the function that will be called for each element in the collection
// >`@param context [object]` the context our iterator should operate within
//
// essentially copied from underscore.js
var each = function (obj, iterator, context) {
var breaker = {};
if (obj === null) { return; }
if (Array.prototype.forEach && obj.forEach === Array.prototype.forEach) {
obj.forEach(iterator, context);
} else if (obj.length === +obj.length) {
for (var i = 0, l = obj.length; i < l; i++) {
if (iterator.call(context, obj[i], i, obj) === breaker) { return; }
}
} else {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
if (iterator.call(context, obj[key], key, obj) === breaker) { return; }
}
}
}
};
// #### extend()
// >`@param obj [object]` our destination object
// >`@params * [object(s)]` objects that will overwrite the data in the destination object, in order
//
// essentially copied from underscore.js
var extend = function (obj) {
each(Array.prototype.slice.call(arguments, 1), function (source) {
if (source) {
for (var prop in source) {
obj[prop] = source[prop];
}
}
});
return obj;
};
var encodeRefInt = function (value, width) {
if ((value >= 0) && (value < (Math.pow(settings.refIntBase, width) - 1))) {
var encoded = '';
while (value > 0) {
encoded = (String.fromCharCode((value % settings.refIntBase) + settings.refIntFloorCode)) + encoded;
value = Math.floor(value / settings.refIntBase);
}
var missingLength = width - encoded.length;
var i = 0;
for (; i < missingLength; i++) {
encoded = String.fromCharCode(settings.refIntFloorCode) + encoded;
}
return encoded;
} else {
throw 'Reference int out of range: ' + value + ' (width = ' + width + ')';
}
};
var encodeRefLength = function (length) {
return encodeRefInt(length - settings.minStringLength, 1);
};
var decodeRefInt = function (data, width) {
var
value = 0
, charCode
, i = 0
;
for (; i < width; i++) {
value *= settings.refIntBase;
charCode = data.charCodeAt(i);
if ((charCode >= settings.refIntFloorCode) && (charCode <= settings.refIntCeilCode)) {
value += charCode - settings.refIntFloorCode;
} else {
throw 'Invalid char code in reference int: ' + charCode;
}
}
return value;
};
var decodeRefLength = function (data) {
return decodeRefInt(data, 1) + settings.minStringLength;
};
// ##### Public Methods
// #### LZ77.compress()
// >`@param source [string]` the source string we will be compressing
// >`@param params [object]` this allows us to overwrite defaults at call-time
//
// This is our compression method, taking the input string (and allowing for call-time
// paramters) and returning the compressed representation
self.compress = function (source, params) {
if (Object.prototype.toString.call(source) !== '[object String]') { return false; }
setup(params);
var windowLength = settings.windowLength || settings.defaultWindow;
if (windowLength > settings.maxWindow) { throw 'Window length too large'; }
var
compressed = ''
, pos = 0
, lastPos = source.length - settings.minStringLength
;
while (pos < lastPos) {
var
searchStart = Math.max(pos - windowLength, 0)
, matchLength = settings.minStringLength
, foundMatch = false
, bestMatch = {
distance: settings.maxStringDistance,
length: 0
}
, newCompressed = null
, isValidMatch
, realMatchLength
;
while ((searchStart + matchLength) < pos) {
isValidMatch = ((source.substr(searchStart, matchLength) === source.substr(pos, matchLength)) && (matchLength < settings.maxStringLength));
if (isValidMatch) {
matchLength++;
foundMatch = true;
} else {
realMatchLength = matchLength - 1;
if (foundMatch && (realMatchLength > bestMatch.length)) {
bestMatch.distance = pos - searchStart - realMatchLength;
bestMatch.length = realMatchLength;
}
matchLength = settings.minStringLength;
searchStart++;
foundMatch = false;
}
}
if (bestMatch.length) {
newCompressed = settings.refPrefix + encodeRefInt(bestMatch.distance, 2) + encodeRefLength(bestMatch.length);
pos += bestMatch.length;
} else {
if (source.charAt(pos) !== settings.refPrefix) {
newCompressed = source.charAt(pos);
} else {
newCompressed = settings.refPrefix + settings.refPrefix;
}
pos++;
}
compressed += newCompressed;
}
return compressed + source.slice(pos).replace(/`/g, '``');
};
// #### LZ77.decompress()
// >`@param source [string]` the source string of compressed data
// >`@param params [object]` this allows us to overwrite defaults at call-time
//
// decompression method, taking the compressed data (as a string, and allowing for
// call-time paramters) and returning the decompressed data
self.decompress = function (source, params) {
if (Object.prototype.toString.call(source) !== '[object String]') { return false; }
var
decompressed = ''
, pos = 0
, currentChar
, nextChar
, distance
, length
;
setup(params);
while (pos < source.length) {
currentChar = source.charAt(pos);
if (currentChar !== settings.refPrefix) {
decompressed += currentChar;
pos++;
} else {
nextChar = source.charAt(pos + 1);
if (nextChar !== settings.refPrefix) {
distance = decodeRefInt(source.substr(pos + 1, 2), 2);
length = decodeRefLength(source.charAt(pos + 3));
decompressed += decompressed.substr(decompressed.length - distance - length, length);
pos += settings.minStringLength - 1;
} else {
decompressed += settings.refPrefix;
pos += 2;
}
}
}
return decompressed;
};
return self;
}();
if (typeof define !== 'undefined' && define.amd) { // requirejs/amd
define([], function () { return LZ77; });
} else if (typeof module !== 'undefined' && module.exports) { // node
module.exports = LZ77;
} else if (window !== undefined) {
window.LZ77 = LZ77;
}
})();

View file

@ -1,3 +1,8 @@
{
"1.0.0": "0.15.0"
}
"1.0.0": "0.15.0",
"1.0.1": "0.15.0",
"1.1.0": "0.15.0",
"1.1.2": "0.15.0",
"1.2.2": "0.15.0",
"1.2.5": "0.15.0"
}