Browse Source

remove lodash, make post quotes in hidden messages clickable

pull/46/head
coomdev 2 years ago
parent
commit
75659cea1e
  1. 2
      build.js
  2. 44
      main.d.ts
  3. 2
      main.meta.js
  4. 15403
      main.user.js
  5. 27
      package-lock.json
  6. 1
      package.json
  7. 3
      src/Components/HydrusSearch.svelte
  8. 4
      src/Components/TextEmbeddings.svelte
  9. 127
      src/debounce.ts
  10. 33
      src/main.ts
  11. 6
      src/thirdeye.ts
  12. 15
      src/websites/index.ts

2
build.js

@ -18,6 +18,7 @@ let rev = +res.stdout;
.build({
entryPoints: ["src/main.ts"],
bundle: true,
treeShaking: true,
outfile: "./dist/main.js",
define: {
global: 'window',
@ -39,6 +40,7 @@ let rev = +res.stdout;
metafile: true
})
console.log(res.metafile.inputs);
console.log(Object.entries(res.metafile.inputs).sort((a, b) => a[1].bytes - b[1].bytes).map(e => `${e[0]} -> ${e[1].bytes}`).join('\n'));
writeFileSync('./main.user.js', extheader + readFileSync('./dist/main.js'));
writeFileSync('./main.meta.js', extheader);

44
main.d.ts

@ -7,15 +7,53 @@ declare module '*.png' {
}
declare module 'blockhash' {
export const hammingDistance: (a: string, b: string) => number;
export const blockhash: () => void;
export const blockhashData: (imgData: {
export const hammingDistance: (a: string, b: string) => number;
export const blockhash: () => void;
export const blockhashData: (imgData: {
width: number,
height: number,
data: Uint8Array
}, bits: number, method: number) => string;
}
declare module "jpeg-js/lib/decoder" {
export interface RawImageData<T> {
width: number;
height: number;
data: T;
}
type BufferRet = RawImageData<Buffer>;
type UintArrRet = RawImageData<Uint8Array>;
type ImageData = BufferRet | UintArrRet;
type BufferLike = Buffer | Uint8Array | ArrayLike<number> | Iterable<number> | ArrayBuffer;
export declare function decode(
jpegData: BufferLike,
opts: {
useTArray: true;
colorTransform?: boolean;
formatAsRGBA?: boolean;
tolerantDecoding?: boolean;
maxResolutionInMP?: number;
maxMemoryUsageInMB?: number;
},
): UintArrRet & { comments?: string[] };
export declare function decode(
jpegData: BufferLike,
opts?: {
useTArray?: false;
colorTransform?: boolean;
formatAsRGBA?: boolean;
tolerantDecoding?: boolean;
maxResolutionInMP?: number;
maxMemoryUsageInMB?: number;
},
): BufferRet & { comments?: string[] };
}
declare const QR: any;
declare const BUILD_VERSION: [number, number];
declare const execution_mode: 'userscript' | 'chrome_api' | 'ff_api';

2
main.meta.js

@ -1,7 +1,7 @@
// ==UserScript==
// @name PNGExtraEmbed
// @namespace https://coom.tech/
// @version 0.187
// @version 0.188
// @description uhh
// @author You
// @match https://boards.4channel.org/*

15403
main.user.js

File diff suppressed because one or more lines are too long

27
package-lock.json

@ -16,6 +16,7 @@
"file-type": "^17.0.2",
"image-hash": "^5.0.1",
"jpeg-js": "^0.4.3",
"linkify-string": "^3.0.4",
"lodash": "^4.17.21",
"png-js": "^1.0.0",
"readable-stream": "^3.6.0",
@ -3098,6 +3099,20 @@
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
},
"node_modules/linkify-string": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/linkify-string/-/linkify-string-3.0.4.tgz",
"integrity": "sha512-OnNqqRjlYXaXipIAbBC8sDXsSumI1ftatzFg141Pw9HEXWjTVLFcMZoKbFupshqWRavtNJ6QHLa+u6AlxxgeRw==",
"peerDependencies": {
"linkifyjs": "^3.0.0"
}
},
"node_modules/linkifyjs": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-3.0.5.tgz",
"integrity": "sha512-1Y9XQH65eQKA9p2xtk+zxvnTeQBG7rdAXSkUG97DmuI/Xhji9uaUzaWxRj6rf9YC0v8KKHkxav7tnLX82Sz5Fg==",
"peer": true
},
"node_modules/loader-utils": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.0.tgz",
@ -6961,6 +6976,18 @@
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
},
"linkify-string": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/linkify-string/-/linkify-string-3.0.4.tgz",
"integrity": "sha512-OnNqqRjlYXaXipIAbBC8sDXsSumI1ftatzFg141Pw9HEXWjTVLFcMZoKbFupshqWRavtNJ6QHLa+u6AlxxgeRw==",
"requires": {}
},
"linkifyjs": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-3.0.5.tgz",
"integrity": "sha512-1Y9XQH65eQKA9p2xtk+zxvnTeQBG7rdAXSkUG97DmuI/Xhji9uaUzaWxRj6rf9YC0v8KKHkxav7tnLX82Sz5Fg==",
"peer": true
},
"loader-utils": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.0.tgz",

1
package.json

@ -21,6 +21,7 @@
"file-type": "^17.0.2",
"image-hash": "^5.0.1",
"jpeg-js": "^0.4.3",
"linkify-string": "^3.0.4",
"lodash": "^4.17.21",
"png-js": "^1.0.0",
"readable-stream": "^3.6.0",

3
src/Components/HydrusSearch.svelte

@ -1,6 +1,5 @@
<script lang="ts">
import { map } from "lodash";
import { each, onMount } from "svelte/internal";
import { onMount } from "svelte/internal";
import type { EmbeddedFile } from "../main";
import { appState } from "../stores";

4
src/Components/TextEmbeddings.svelte

@ -1,5 +1,6 @@
<script lang="ts">
import type { EmbeddedFile } from "../main";
import sanLink from 'linkify-string'
export let files: EmbeddedFile[];
@ -8,7 +9,7 @@
{#each contents as content}
<div class="additionnal">
{content}
{@html sanLink(content)}
</div>
{/each}
@ -18,5 +19,6 @@
clear: both;
margin-top: 10px;
padding-top: 10px;
white-space: pre-wrap;
}
</style>

127
src/debounce.ts

@ -0,0 +1,127 @@
const nativeMax = Math.max;
const nativeMin = Math.min;
export function debounce(func: (...args: any[]) => any, wait: number, options: any) {
let lastArgs : any,
lastThis: any,
maxWait: number | undefined,
result: any,
timerId: number | undefined,
lastCallTime: number | undefined,
lastInvokeTime = 0,
leading = false,
maxing = false,
trailing = true;
wait = Number(wait) || 0;
if (typeof options === 'object') {
leading = !!options.leading;
maxing = 'maxWait' in options;
maxWait = maxing
? nativeMax(Number(options.maxWait) || 0, wait)
: maxWait;
trailing = 'trailing' in options
? !!options.trailing
: trailing;
}
function invokeFunc(time: number) {
const args = lastArgs,
thisArg = lastThis;
lastArgs = lastThis = undefined;
lastInvokeTime = time;
result = func.apply(thisArg, args);
return result;
}
function leadingEdge(time: number) {
// Reset any `maxWait` timer.
lastInvokeTime = time;
// Start the timer for the trailing edge.
timerId = setTimeout(timerExpired, wait) as unknown as number;
// Invoke the leading edge.
return leading
? invokeFunc(time)
: result;
}
function remainingWait(time: number) {
const timeSinceLastCall = time - lastCallTime!,
timeSinceLastInvoke = time - lastInvokeTime,
result = wait - timeSinceLastCall;
console.log('remainingWait');
return maxing
? nativeMin(result, maxWait! - timeSinceLastInvoke)
: result;
}
function shouldInvoke(time: number) {
const timeSinceLastCall = time - lastCallTime!,
timeSinceLastInvoke = time - lastInvokeTime;
// Either this is the first call, activity has stopped and we're at the trailing
// edge, the system time has gone backwards and we're treating it as the
// trailing edge, or we've hit the `maxWait` limit.
return (lastCallTime === undefined || (timeSinceLastCall >= wait) || (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait!));
}
function timerExpired() {
const time = Date.now();
if (shouldInvoke(time)) {
return trailingEdge(time);
}
// Restart the timer.
timerId = setTimeout(timerExpired, remainingWait(time)) as unknown as number;
}
function trailingEdge(time: number) {
timerId = undefined;
// Only invoke if we have `lastArgs` which means `func` has been debounced at
// least once.
if (trailing && lastArgs) {
return invokeFunc(time);
}
lastArgs = lastThis = undefined;
return result;
}
function cancel() {
if (timerId !== undefined) {
clearTimeout(timerId);
}
lastInvokeTime = 0;
lastArgs = lastCallTime = lastThis = timerId = undefined;
}
function flush() {
return timerId === undefined
? result
: trailingEdge(Date.now());
}
function debounced(this: any, ...args: any[]) {
const time = Date.now(),
isInvoking = shouldInvoke(time);
lastArgs = args;
lastThis = this;
lastCallTime = time;
if (isInvoking) {
if (timerId === undefined) {
return leadingEdge(lastCallTime);
}
if (maxing) {
// Handle invocations in a tight loop.
timerId = setTimeout(timerExpired, wait) as unknown as number;
return invokeFunc(lastCallTime);
}
}
if (timerId === undefined) {
timerId = setTimeout(timerExpired, wait) as unknown as number;
}
return result;
}
debounced.cancel = cancel;
debounced.flush = flush;
return debounced;
}

33
src/main.ts

@ -1,6 +1,6 @@
import { Buffer } from "buffer";
import { appState, settings, initial_settings } from "./stores";
import _ from 'lodash';
import { debounce } from './debounce';
import globalCss from './global.css';
import pngv3 from "./pngv3";
@ -14,15 +14,16 @@ import App from "./Components/App.svelte";
import ScrollHighlighter from "./Components/ScrollHighlighter.svelte";
import PostOptions from "./Components/PostOptions.svelte";
import SettingsButton from './Components/SettingsButton.svelte';
//import Embedding from './Components/Embedding.svelte';
import Embeddings from './Components/Embeddings.svelte';
import EyeButton from './Components/EyeButton.svelte';
import NotificationsHandler from './Components/NotificationsHandler.svelte';
import { fireNotification, getSelectedFile } from "./utils";
import { getQueryProcessor, QueryProcessor } from "./websites";
import { ifetch, streamRemote, supportedAltDomain } from "./platform";
import TextEmbeddingsSvelte from "./Components/TextEmbeddings.svelte";
import { HydrusClient } from "./hydrus";
import { registerPlugin } from 'linkifyjs';
export interface ImageProcessor {
skip?: true;
@ -48,7 +49,7 @@ settings.subscribe(async b => {
try {
const valid = await hydCli.verify();
if (!valid)
herror = "Hydrus appears to not be running or the key is wrong.";
herror = "Hydrus appears to not be running or the key is wrong.";
appState.set({ ...cappState, akValid: valid, client: hydCli, herror });
} catch {
herror = "Hydrus appears to not be running";
@ -129,7 +130,7 @@ const textToElement = <T = HTMLElement>(s: string) =>
let pendingPosts: { id: number, op: number }[] = [];
const signalNewEmbeds = _.debounce(async () => {
const signalNewEmbeds = debounce(async () => {
// ensure user explicitely enabled telemetry
if (!csettings.tm)
return;
@ -335,6 +336,26 @@ const startup = async (is4chanX = true) => {
if (csettings.vercheck)
versionCheck();
const postQuote = ({ scanner, parser, utils }: any) => {
const { CLOSEANGLEBRACKET, NUM } = scanner.tokens;
const START_STATE = parser.start;
const pref = qp.getPostIdPrefix();
const endQuote = utils.createTokenClass('postQuote', {
isLink: true,
toHref() {
return `#${pref}${this.toString().substr(2)}`;
}
});
// A post quote (>>123456789) is made of
const MEMEARROW1 = START_STATE.tt(CLOSEANGLEBRACKET); // One meme arrow followed by
const MEMEARROW2 = MEMEARROW1.tt(CLOSEANGLEBRACKET); // another meme arrow, terminated by
const POSTNUM_STATE = MEMEARROW2.tt(NUM, endQuote); // a number
};
registerPlugin('quote', postQuote);
if (!is4chanX && location.host.startsWith('boards.4chan')) {
const qr = QR;
const show = qr.show.bind(qr);
@ -463,7 +484,7 @@ document.addEventListener('QRDialogCreation', <any>((e: CustomEvent<HTMLElement>
target: a,
props: { processors, textinput: (e.detail || e.target).querySelector('textarea')! }
});
let prevFile: File;
let target;
const somethingChanged = async (m: any) => {
@ -522,7 +543,7 @@ function processAttachments(post: HTMLDivElement, ress: [EmbeddedFile, boolean][
if (!isCatalog) {
const ft = qp.getFileThumbnail(post);
const info = qp.getInfoBox(post);
const quot = post.querySelector('blockquote');
const quot = qp.getTextBox(post);
const textInsertCursor = document.createElement('div');
quot?.appendChild(textInsertCursor);
const filehost: HTMLElement | null = ft.querySelector('.filehost');

6
src/thirdeye.ts

@ -1,7 +1,7 @@
import type { EmbeddedFile, ImageProcessor } from "./main";
import { localLoad, settings } from "./stores";
import { Buffer } from "buffer";
import jpeg from 'jpeg-js';
import {decode} from 'jpeg-js/lib/decoder';
import { bmvbhash_even } from "./phash";
import { ifetch, Platform } from "./platform";
@ -78,7 +78,7 @@ export let boorus: Booru[] =
}));
const bufferingTime = 2000;
let expired: number | undefined = undefined;
let expired: number | NodeJS.Timeout | undefined = undefined;
type ApiResult = { [md5 in string]: { [domain in string]: BooruMatch[] } };
let reqQueue: [string, (a: ApiResult) => void][] = [];
let unlockQueue = Promise.resolve();
@ -181,7 +181,7 @@ const extract = async (b: Buffer, fn?: string) => {
};
const phash = (b: Buffer) => {
const res = jpeg.decode(b);
const res = decode(b);
return bmvbhash_even(res, 8);
};

15
src/websites/index.ts

@ -9,6 +9,8 @@ export type QueryProcessor = {
getFilename: (post: HTMLElement) => string;
getMD5: (post: HTMLElement) => string;
getInfoBox: (post: HTMLElement) => HTMLElement;
getPostIdPrefix: () => string;
getTextBox: (post: HTMLElement) => HTMLElement;
};
export const V4chan: QueryProcessor = {
@ -26,7 +28,9 @@ export const V4chan: QueryProcessor = {
},
getMD5: (post: HTMLElement) => post.querySelector("img[data-md5]")?.getAttribute("data-md5") || '',
getThumbnailLink: (post: HTMLElement) => post.querySelector("img[data-md5]")?.getAttribute("src") || '',
getInfoBox: post => post.querySelector("div.fileText")!
getInfoBox: post => post.querySelector("div.fileText")!,
getPostIdPrefix: () => 'p',
getTextBox: (post) => post.querySelector('blockquote')!
};
export const X4chan: QueryProcessor = {
@ -43,7 +47,9 @@ export const X4chan: QueryProcessor = {
},
getMD5: (post: HTMLElement) => post.querySelector("img[data-md5]")?.getAttribute("data-md5") || '',
getThumbnailLink: (post: HTMLElement) => post.querySelector("img[data-md5]")?.getAttribute("src") || '',
getInfoBox: post => post.querySelector("span.file-info")!
getInfoBox: post => post.querySelector("span.file-info")!,
getPostIdPrefix: V4chan.getPostIdPrefix,
getTextBox: V4chan.getTextBox
};
export const FoolFuuka: QueryProcessor = {
@ -65,7 +71,10 @@ export const FoolFuuka: QueryProcessor = {
const e = post.querySelector("img[data-md5]");
return e?.getAttribute("src") || e?.getAttribute("data-src") || '';
},
getInfoBox: post => post.querySelector("span.post_controls")!
getInfoBox: post => post.querySelector("span.post_controls")!,
getPostIdPrefix: () => '',
getTextBox: post => post.querySelector('.text')!
};
export const getQueryProcessor = (is4chanX: boolean) => {

Loading…
Cancel
Save