/** * {注入的js} * @author yutent * @date 2023/07/21 17:38:11 */ const MIME_TYPES = { html: 'text/html', json: 'application/json', js: 'application/javascript', htm: 'text/html', txt: 'text/plain', css: 'text/css', webp: 'image/webp', jpg: 'image/jpg', jpeg: 'image/jpeg', png: 'image/png', gif: 'image/gif', svg: 'image/svg+xml', ico: 'image/ico', mp3: 'audio/mpeg', ogg: 'audio/ogg', m4a: 'audio/m4a', amr: 'audio/amr', mp4: 'video/mp4', webm: 'video/webm', wasm: 'application/wasm', asm: 'application/asm', zip: 'application/zip', '7z': 'application/x-7z-compressed', eot: 'application/vnd.ms-fontobject', ttf: 'font/ttf', otf: 'font/otf', woff: 'font/woff', woff2: 'font/woff2', xls: 'application/vnd.ms-excel', doc: 'application/msword', xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' } function defer() { let obj = {} obj.promise = new Promise((resolve, reject) => { obj.resolve = resolve obj.reject = reject }) return obj } function rand(prefix = 'cb_') { return prefix + Math.random().toString().slice(2) } function handler(event, data = {}, once = true) { let _ = defer() let callback if (typeof once === 'boolean') { callback = rand() native[once ? '$once' : '$on'](callback, _.resolve) } else { _.resolve(true) } window.webkit.messageHandlers.app.postMessage({ event, data, callback }) return _.promise } class NativeImage { #origin constructor(obj) { this.#origin = obj this.width = obj.width this.height = obj.height this.type = MIME_TYPES[obj.filepath.split('.').pop()] } toPixbuf() { return this.#origin } export(type) { let _ = defer() let canvas = document.createElement('canvas') canvas.width = this.width canvas.height = this.height let ctx = canvas.getContext('2d') let imgData = ctx.getImageData(0, 0, this.width, this.height) let data = imgData.data for (let i = 0; i < this.#origin.bytes.length; i += 4) { imgData.data[i] = this.#origin.bytes[i] imgData.data[i + 1] = this.#origin.bytes[i + 1] imgData.data[i + 2] = this.#origin.bytes[i + 2] imgData.data[i + 3] = this.#origin.bytes[i + 3] } ctx.putImageData(imgData, 0, 0) canvas.toBlob(_.resolve, type || this.type, 1) return _.promise } } class EventEmitter { // __events__ = Object.create(null) $on(name, fn) { if (this.__events__[name]) { this.__events__[name].push(fn) } else { this.__events__[name] = [fn] } } $once(name, fn) { fn.__once__ = true this.$on(name, fn) } $off(name, fn) { if (this.__events__[name]) { if (fn) { this.__events__[name] = this.__events__[name].filter(it => it !== fn) } else { this.__events__[name] = [] } } } $emit(name, ...args) { if (this.__events__[name]) { for (let fn of this.__events__[name]) { try { fn.apply(this, args) if (fn.__once__) { this.$off(name, fn) } } catch (e) { console.error(e) } } } } $destroy() { this.__events__ = Object.create(null) } } window.native = new EventEmitter() Object.assign(native, { quit() { return handler('quit') }, fs: { read(filepath) {} }, image(filepath) { return handler('image', { value: filepath }).then(r => new NativeImage(r)) }, clipboard: { readText() { return handler('clipboard', { action: 'wait_for_text' }) }, writeText(value) { return handler('clipboard', { action: 'set_text', value }, null) }, readImage() { return handler('clipboard', { action: 'wait_for_image' }) }, writeImage(value) { if (typeof value === 'object') { value = value.toPixbuf() } return handler('clipboard', { action: 'set_image', value }, null) }, clear() { return handler('clipboard', { action: 'clear' }) } }, handler })