diff --git a/.gitignore b/.gitignore index 9f9d07d..5b41148 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,7 @@ local.properties [Dd]ebug/ [Rr]elease/ node_modules/ +dist/*.zip *_i.c *_p.c *.ilk diff --git a/PRIVACY.md b/PRIVACY.md new file mode 100644 index 0000000..2355918 --- /dev/null +++ b/PRIVACY.md @@ -0,0 +1,48 @@ +# Privacy Policy — The Printliminator + +**Last updated:** July 13, 2026 + +## Data Collection + +The Printliminator **does not collect, store, or transmit any personal data or user information.** + +## How the Extension Works + +The Printliminator is a client-side tool that runs entirely in your browser. When you click the toolbar button: + +1. The extension injects a stylesheet and script into the **active tab only** (via the `activeTab` and `scripting` permissions). +2. You interact with the page directly — clicking elements to remove them, removing graphics, or applying print-friendly styling. +3. All changes are made to the DOM in your current tab. Nothing is saved, sent, or shared. + +**No data ever leaves your browser.** There is no server, no analytics, no telemetry, no tracking, and no remote code execution. + +## Permissions + +| Permission | Why it's needed | +|------------|-----------------| +| `activeTab` | Access the current tab's content when you click the toolbar button, so you can select and remove page elements. | +| `scripting` | Inject the content script (`printliminator.js`) and stylesheet (`printliminator.css`) into the active tab on your click. All code is bundled in the extension — no remote code is loaded or executed. | + +## Third-Party Services + +None. The extension does not communicate with any external service. + +## Data Storage + +None. The extension does not use `localStorage`, `sessionStorage`, `chrome.storage`, cookies, or any other storage mechanism. + +## Children's Privacy + +The extension is directed at a general audience and does not knowingly collect any data from anyone, including children under 13. + +## Changes to This Policy + +If this policy ever changes, the updated version will be posted in this repository. + +## Contact + +To report a privacy concern or ask a question, please [open an issue](https://github.com/JoshuaKimsey/The-Printliminator/issues) on the GitHub repository. + +## Source Code + +The full source code is available at https://github.com/JoshuaKimsey/The-Printliminator and can be audited to verify the claims in this policy. diff --git a/PUBLISHING.md b/PUBLISHING.md new file mode 100644 index 0000000..d2ad28b --- /dev/null +++ b/PUBLISHING.md @@ -0,0 +1,207 @@ +# Publishing to the Chrome Web Store — Guide for The Printliminator + +This file contains pre-written copy for every Chrome Web Store dashboard field, so the +submission process is copy-paste + drag-drop. Read this alongside the +[official publish docs](https://developer.chrome.com/docs/webstore/publish/). + +--- + +## Before You Start + +### 1. Developer account ($5 one-time fee) +- Go to https://chrome.google.com/webstore/devconsole/ +- Sign in with your Google account +- Pay the $5 registration fee +- Set your publisher name (e.g., "Joshua Kimsey") + +### 2. Build the package +``` +npm install +npm run package +``` +This produces `dist/printliminator-v5.0.0.zip` with `manifest.json` at the zip root +(the #1 packaging gotcha — the Web Store rejects zips where the manifest is nested +in a folder). + +### 3. Screenshots (you must create these) +The Web Store requires at least one 1280x800 px screenshot. Take 1-5 of these: + +1. Load the unpacked extension (`chrome://extensions/` → Developer mode → Load unpacked → select `dist/chrome/`) +2. Navigate to a content-heavy page (news article, Wikipedia, etc.) +3. Take screenshots showing: + - The popup open with the 4 command buttons + - An element highlighted (red outline) on the page + - The page after "Remove Graphics" has been applied + - The page after "Add Print Styles" has been applied + +Save them as `screenshot-1.png`, `screenshot-2.png`, etc. (1280x800, PNG or JPEG). + +### 4. Store icon +`dist/chrome/icon128.png` — already 128x128 px, ready to use. + +### 5. Small promo tile +`src/images/web-store-tile.png` — already 440x280 px, ready to use. + +### 6. Privacy policy URL +Point the Web Store to: +``` +https://github.com/JoshuaKimsey/The-Printliminator/blob/master/PRIVACY.md +``` +(Or use the raw URL if the dashboard rejects the GitHub blob URL: +`https://raw.githubusercontent.com/JoshuaKimsey/The-Printliminator/master/PRIVACY.md`) + +--- + +## Upload the Extension + +1. Go to https://chrome.google.com/webstore/devconsole/ +2. Click **"Add new item"** +3. Choose `dist/printliminator-v5.0.0.zip` → **Upload** +4. If valid, you'll proceed to the listing editor + +--- + +## Store Listing Tab + +### Item name +``` +The Printliminator +``` +*(This comes from `_locales/en/messages.json` → `printliminatorName`, but the dashboard may ask you to confirm it.)* + +### Detailed description +``` +The Printliminator is a simple tool that lets you clean up web pages before printing them. One click activates the toolbar popup, then you can: + +• Click any element on the page to remove it +• Alt-click to remove everything EXCEPT what you clicked +• Remove all graphics (images, iframes, video, backgrounds) in one click +• Apply a print-friendly stylesheet (serif fonts, proper margins, clean tables) +• Adjust font size with keyboard shortcuts +• Undo any action with Backspace +• Print the cleaned-up page + +All processing happens locally in your browser — no data is sent anywhere. + +Keyboard commands: +• Arrow keys / PageUp / PageDown — navigate the element hierarchy +• Enter — remove the highlighted element +• Backspace — undo last action +• + / - — increase / decrease font size +• * — reset font size +• Shift+click — make element full width +• Alt+click — remove everything except the clicked element +• Esc — disable Printliminator +• F1 — toggle on-screen action messages + +This is a community-maintained fork of the original Printliminator by Chris Coyier, updated for Manifest V3 compatibility so it works in modern Chrome. Original project: https://github.com/CSS-Tricks/The-Printliminator +``` + +### Category +``` +Productivity +``` + +### Language +``` +English +``` +*(You can add localized listings for de, fr, pt_BR, sr later if you provide translated descriptions.)* + +### Graphic assets +| Field | File | Status | +|-------|------|--------| +| Store icon (128x128) | `dist/chrome/icon128.png` | Ready | +| Screenshot 1-5 (1280x800) | `screenshot-1.png` etc. | **You create these** | +| Small promo tile (440x280) | `src/images/web-store-tile.png` | Ready | +| Marquee promo tile (1400x560) | — | Optional, skip | +| YouTube video | — | Optional, skip | + +--- + +## Privacy Tab + +### Single purpose description +``` +The Printliminator lets users clean up web pages before printing by removing unwanted elements, removing graphics, and applying print-friendly styling. All processing happens locally in the browser on the active tab only, triggered by an explicit user click on the toolbar button. +``` + +### Permission justifications + +**`activeTab`:** +``` +Needed to access the current tab's DOM when the user clicks the extension's toolbar button. The user interactively selects and removes page elements, adjusts styling, and prints the cleaned-up page. No access is requested beyond the active tab and only after the user explicitly clicks the toolbar icon. +``` + +**`scripting`:** +``` +Used to inject the bundled content script (printliminator.js) and stylesheet (printliminator.css) into the active tab on user click. This is what enables the interactive element-removal and print-styling features. All code is bundled inside the extension — no remote code is loaded or executed. +``` + +### Remote code declaration +``` +Select: "No, I am not using remote code." +``` +*(This is true — all JS/CSS is bundled in the extension. This is a core MV3 requirement and you're already compliant.)* + +### Data usage certification +Check these boxes: +- [x] I do **not** sell or transfer user data to third parties +- [x] I do **not** use or transfer user data for purposes unrelated to my item's single purpose +- [x] I do **not** use or transfer user data to determine creditworthiness or for lending purposes + +And under "Data collected" — **leave all unchecked**. The extension collects nothing. + +### Privacy policy URL +``` +https://github.com/JoshuaKimsey/The-Printliminator/blob/master/PRIVACY.md +``` + +--- + +## Distribution Tab + +### Price +``` +Free +``` + +### Visibility +``` +Public +``` + +### Regions +``` +All regions (default) +``` + +--- + +## Test Instructions Tab + +Leave blank — the extension is self-explanatory and requires no credentials or special setup. Reviewers can test it by loading any public web page and clicking the toolbar button. + +--- + +## Submit for Review + +1. Click **"Submit for Review"** +2. Confirm the dialog +3. Wait for review (typically a few days to a couple weeks for a new extension) +4. You'll get an email when it's approved or if changes are requested + +### If the review requests changes +Common reasons for rejection and how to handle them: +- **"Permission X is not needed"** — unlikely, but if so, remove it from the manifest and rebuild +- **"Single purpose not clear"** — expand the single-purpose description +- **"Privacy policy not sufficient"** — expand PRIVACY.md with more detail +- **Screenshots don't match the extension** — re-take them + +--- + +## After Publication + +- The extension will get a Web Store listing URL — add it to the README +- If you publish updates later: bump the version in `package.json` + `src/chrome/manifest.json`, run `npm run package`, upload the new zip to the dashboard (version must be higher than the previous one) +- The v5.0.0 tag on your fork should match the first published version diff --git a/README.md b/README.md index 08ca725..c8fb51b 100644 --- a/README.md +++ b/README.md @@ -2,17 +2,33 @@ The Printliminator is a simple tool you can use to make websites print better. One click to activate, and then click to remove elements from the page, remove graphics, and apply better print styling. +> **This is a community-maintained fork** by [Joshua Kimsey](https://github.com/JoshuaKimsey), +> updated for Manifest V3 compatibility. The original project (last released 2015) no longer loads +> in modern Chrome. See **Recent Changes** below for migration details. + ![screenshot](https://cloud.githubusercontent.com/assets/136959/10418826/4ccd60c6-702b-11e5-8ed8-f4d4df66e6d5.png) Get the: * [Bookmarklet](//css-tricks.github.io/The-Printliminator/) -* [Chrome Extension](//chrome.google.com/webstore/detail/the-printliminator/nklechikgnfoonbfmcalddjcpmcmgapf?hl=en-US&gl=US) +* [Chrome Extension](//chrome.google.com/webstore/detail/the-printliminator/nklechikgnfoonbfmcalddjcpmcmgapf?hl=en-US&gl=US) (original MV2 listing — no longer loads in modern Chrome; build from source for the MV3 version) * [Opera Extension](//addons.opera.com/en/extensions/details/the-printliminator/?display=en) +### Building from source + +Requires Node.js and npm: + +``` +npm install +npm run build +``` + +Then load `dist/chrome/` as an unpacked extension in Chrome +(`chrome://extensions/` → enable Developer mode → Load unpacked). + ### Limitations * Bookmarklet: due to Content Security Policy directives on some sites, the Printliminator bookmarklet script is not able to load on some sites (e.g. GitHub). To get around this problem, use the Chrome or Opera extension. Hopefully, Firefox & Safari extensions/addons will quickly follow. -* Chrome/Opera extension: if a popup window is opened for printing, like Yahoo mail does, then the extension will not work in the popup. [An issue](https://code.google.com/p/chromium/issues/detail?id=530658) was submitted and it sounds like they will be providing a fix. +* Chrome/Opera extension: if a popup window is opened for printing, like Yahoo mail does, then the extension will not work in the popup. The MV3 `chrome.scripting` API has no `matchAboutBlank` equivalent; restoring this would require the `webNavigation` permission and explicit frame targeting. ### To Do @@ -34,6 +50,28 @@ Get the: ### Recent Changes +#### Version 5.0.0 (7/13/2026) + +Major version bump for Manifest V3 compatibility — the extension now loads in modern Chrome again. + +* **Manifest V3 migration:** + * `manifest_version` 2 → 3; `browser_action` → `action`. + * Added `scripting` permission; `web_accessible_resources` converted to object form with `matches`. +* **API migration in `popup.js`:** + * `chrome.tabs.executeScript` → `chrome.scripting.executeScript`. + * `chrome.tabs.insertCSS` → `chrome.scripting.insertCSS`. + * `code:` string injection → `func:` arrow functions; `file:` → `files:` arrays. + * Result reads updated from `result[0]` → `result[0].result` (affects the print ready-state polling loop). + * Implicit active-tab targeting (`null` tabId) → explicit `target: { tabId }`. + * `matchAboutBlank` dropped (no MV3 equivalent — see Limitations). +* **Build tooling modernization:** + * Replaced the 2015 Grunt 0.4 + `grunt-preprocess` pipeline with a zero-dependency `build.js` (Node). + * Single dev dependency: `sass` (Dart Sass) for SCSS compilation. + * `npm run build` / `npm run clean` scripts; outputs `dist/chrome/`. + * Bookmarklet build path is untouched (unaffected by MV3). +* Locales: the build now includes all available locales (`de`, `en`, `fr`, `pt_BR`, `sr`); the previous committed `dist/` only shipped `en` and `fr`. +* Functionally identical to 4.0.5 on normal pages; verified in Chrome MV3. + #### Version 4.0.5 (10/11/2015) * Readme: diff --git a/build.js b/build.js new file mode 100644 index 0000000..5120391 --- /dev/null +++ b/build.js @@ -0,0 +1,352 @@ +#!/usr/bin/env node +'use strict'; + +// Printliminator build script (replaces the old Grunt pipeline). +// +// Produces a Manifest V3 Chrome extension in dist/chrome/ from src/. +// The bookmarklet distribution path is untouched by this build. +// +// Handles the grunt-preprocess directives used in the original source: +// @echo settings.X (block-comment form) -> value from src/options.json +// @echo X (block-comment form) -> top-level key in options.json +// @echo X (HTML comment form) -> same, in HTML files +// @if MODE='EXT' ... @endif -> conditional block (kept for ext) +// {version} -> version from package.json + +var fs = require('fs'); +var path = require('path'); + +var ROOT = __dirname; +var SRC = path.join(ROOT, 'src'); +var DIST = path.join(ROOT, 'dist', 'chrome'); +var MODE = 'EXT'; + +var pkg = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8')); +var SETTINGS = JSON.parse(fs.readFileSync(path.join(SRC, 'options.json'), 'utf8')); +var VERSION = pkg.version; + +// --------------------------------------------------------------------------- +// File helpers +// --------------------------------------------------------------------------- + +function rmrf(dir) { + if (!fs.existsSync(dir)) return; + for (var entry of fs.readdirSync(dir)) { + var full = path.join(dir, entry); + if (fs.statSync(full).isDirectory()) { + rmrf(full); + } else { + fs.unlinkSync(full); + } + } + fs.rmdirSync(dir); +} + +function mkdirp(dir) { + if (fs.existsSync(dir)) return; + mkdirp(path.dirname(dir)); + fs.mkdirSync(dir); +} + +function read(file) { + return fs.readFileSync(file, 'utf8'); +} + +function write(file, content) { + mkdirp(path.dirname(file)); + fs.writeFileSync(file, content, 'utf8'); +} + +function copyFile(src, dest) { + mkdirp(path.dirname(dest)); + fs.copyFileSync(src, dest); +} + +function copyDir(srcDir, destDir) { + if (!fs.existsSync(srcDir)) return; + mkdirp(destDir); + for (var entry of fs.readdirSync(srcDir)) { + var src = path.join(srcDir, entry); + var dest = path.join(destDir, entry); + if (fs.statSync(src).isDirectory()) { + copyDir(src, dest); + } else { + copyFile(src, dest); + } + } +} + +// --------------------------------------------------------------------------- +// Preprocessor (replicates grunt-preprocess semantics for the subset used here) +// --------------------------------------------------------------------------- + +function resolveKey(context, key) { + var parts = key.split('.'); + var val = context; + for (var i = 0; i < parts.length; i++) { + if (val == null) return ''; + val = val[parts[i]]; + } + return val === undefined ? '' : String(val); +} + +function stripIfBlocks(code, targetMode) { + var lines = code.split('\n'); + var result = []; + var skipping = false; + for (var i = 0; i < lines.length; i++) { + var line = lines[i]; + var ifMatch = line.match(/@if\s+MODE\s*=\s*['"](\w+)['"]/); + if (ifMatch) { + skipping = (ifMatch[1] !== targetMode); + continue; // drop the directive line itself + } + if (/@endif/.test(line)) { + skipping = false; + continue; // drop the directive line itself + } + if (!skipping) { + result.push(line); + } + } + return result.join('\n'); +} + +function substituteEcho(code, context) { + code = code.replace(/\/\*\s*@echo\s+([\w.]+)\s*\*\//g, function (m, key) { + return resolveKey(context, key); + }); + code = code.replace(//g, function (m, key) { + return resolveKey(context, key); + }); + return code; +} + +function preprocess(code, context) { + code = stripIfBlocks(code, MODE); + code = substituteEcho(code, context); + code = code.replace(/\{version\}/g, VERSION); + return code; +} + +// --------------------------------------------------------------------------- +// SCSS compilation +// --------------------------------------------------------------------------- + +function compileScss(preprocessedScss) { + var sass; + try { + sass = require('sass'); + } catch (e) { + throw new Error( + 'sass is not installed. Run "npm install" first.\n' + e.message + ); + } + // Use modern API where available, fall back to legacy renderSync. + if (typeof sass.compileString === 'function') { + return sass.compileString(preprocessedScss, { style: 'expanded' }).css; + } + return sass.renderSync({ + data: preprocessedScss, + outputStyle: 'expanded', + sourceMap: false + }).css.toString('utf8'); +} + +// --------------------------------------------------------------------------- +// Build +// --------------------------------------------------------------------------- + +function build() { + var started = Date.now(); + console.log('Building Printliminator v' + VERSION + ' (Chrome MV3)...'); + + // 1. Clean output + rmrf(DIST); + + // Contexts for the preprocessor. + // popup.* use flat settings; printliminator.* use { MODE, settings }. + var flatCtx = Object.assign({}, SETTINGS); + var extCtx = { MODE: MODE, settings: SETTINGS }; + + // 2. popup.js + write( + path.join(DIST, 'popup.js'), + preprocess(read(path.join(SRC, 'chrome', 'popup.js')), flatCtx) + ); + + // 3. popup.html + write( + path.join(DIST, 'popup.html'), + preprocess(read(path.join(SRC, 'chrome', 'popup.html')), flatCtx) + ); + + // 4. popup.css (preprocess SCSS -> sass) + var popupScss = preprocess(read(path.join(SRC, 'chrome', 'popup.scss')), flatCtx); + write(path.join(DIST, 'popup.css'), compileScss(popupScss)); + + // 5. printliminator.css (preprocess SCSS with MODE=EXT -> sass) + var mainScss = preprocess(read(path.join(SRC, 'printliminator.scss')), extCtx); + write(path.join(DIST, 'printliminator.css'), compileScss(mainScss)); + + // 6. printliminator.js (preprocess with MODE=EXT) + write( + path.join(DIST, 'printliminator.js'), + preprocess(read(path.join(SRC, 'printliminator.js')), extCtx) + ); + + // 7. manifest.json (copy as-is; version already set in source) + copyFile(path.join(SRC, 'chrome', 'manifest.json'), path.join(DIST, 'manifest.json')); + + // 8. Icons (src/icons/*.png) + var iconsDir = path.join(SRC, 'icons'); + for (var icon of fs.readdirSync(iconsDir)) { + if (icon.endsWith('.png')) { + copyFile(path.join(iconsDir, icon), path.join(DIST, icon)); + } + } + + // 9. Locales (_locales/** -> dist/chrome/_locales/) + copyDir(path.join(ROOT, '_locales'), path.join(DIST, '_locales')); + + var ms = Date.now() - started; + console.log('Done in ' + ms + 'ms -> ' + path.relative(ROOT, DIST) + '/'); +} + +// --------------------------------------------------------------------------- +// Packaging (produces a Web Store-ready zip with manifest.json at the root) +// --------------------------------------------------------------------------- + +function packageZip() { + var zlib = require('zlib'); + + // Build first so dist/ is current. + build(); + + var outPath = path.join(ROOT, 'dist', 'printliminator-v' + VERSION + '.zip'); + mkdirp(path.dirname(outPath)); + + // Gather files relative to dist/chrome/ so manifest.json lands at zip root. + var entries = []; + function walk(dir, rel) { + for (var entry of fs.readdirSync(dir)) { + var full = path.join(dir, entry); + var relPath = rel ? rel + '/' + entry : entry; + if (fs.statSync(full).isDirectory()) { + walk(full, relPath); + } else { + entries.push({ full: full, rel: relPath }); + } + } + } + walk(DIST, ''); + + // Minimal zip writer (stored, no compression — extensions are tiny and + // the Web Store recompresses anyway; avoids a dependency on archiver). + // Each local file header + central directory record is written by hand. + var buffers = []; + var central = []; + var offset = 0; + var now = new Date(); + var dosTime = ((now.getHours() & 0x1f) << 11) | ((now.getMinutes() & 0x3f) << 5) | ((now.getSeconds() / 2) & 0x1f); + var dosDate = (((now.getFullYear() - 1980) & 0x7f) << 9) | (((now.getMonth() + 1) & 0xf) << 5) | (now.getDate() & 0x1f); + + function u16(n) { var b = Buffer.alloc(2); b.writeUInt16LE(n, 0); return b; } + function u32(n) { var b = Buffer.alloc(4); b.writeUInt32LE(n, 0); return b; } + + for (var i = 0; i < entries.length; i++) { + var data = fs.readFileSync(entries[i].full); + var nameBuf = Buffer.from(entries[i].rel, 'utf8'); + var crc = crc32(data); + + // Local file header + var local = Buffer.concat([ + u32(0x04034b50), // signature + u16(20), // version needed + u16(0), // flags + u16(0), // method (0 = stored) + u16(dosTime), // mod time + u16(dosDate), // mod date + u32(crc), // crc-32 + u32(data.length), // compressed size + u32(data.length), // uncompressed size + u16(nameBuf.length), // filename length + u16(0), // extra field length + nameBuf, // filename + data // file data + ]); + buffers.push(local); + + // Central directory record + var cen = Buffer.concat([ + u32(0x02014b50), // signature + u16(20), // version made by + u16(20), // version needed + u16(0), // flags + u16(0), // method + u16(dosTime), // mod time + u16(dosDate), // mod date + u32(crc), // crc-32 + u32(data.length), // compressed size + u32(data.length), // uncompressed size + u16(nameBuf.length), // filename length + u16(0), // extra field length + u16(0), // comment length + u16(0), // disk number start + u16(0), // internal attrs + u32(0), // external attrs + u32(offset), // local header offset + nameBuf // filename + ]); + central.push(cen); + + offset += local.length; + } + + var centralBuf = Buffer.concat(central); + var endRecord = Buffer.concat([ + u32(0x06054b50), // end of central dir signature + u16(0), // disk number + u16(0), // disk with central dir + u16(entries.length), // entries on this disk + u16(entries.length), // total entries + u32(centralBuf.length), // central dir size + u32(offset), // central dir offset + u16(0) // comment length + ]); + + fs.writeFileSync(outPath, Buffer.concat([Buffer.concat(buffers), centralBuf, endRecord])); + console.log('Packaged ' + entries.length + ' files -> ' + path.relative(ROOT, outPath)); +} + +// Standard CRC-32 (IEEE 802.3 polynomial) for zip checksums. +function crc32(buf) { + if (!crc32.table) { + crc32.table = new Uint32Array(256); + for (var n = 0; n < 256; n++) { + var c = n; + for (var k = 0; k < 8; k++) { + c = (c & 1) ? (0xedb88320 ^ (c >>> 1)) : (c >>> 1); + } + crc32.table[n] = c >>> 0; + } + } + var crc = 0xffffffff; + for (var i = 0; i < buf.length; i++) { + crc = (crc >>> 8) ^ crc32.table[(crc ^ buf[i]) & 0xff]; + } + return (crc ^ 0xffffffff) >>> 0; +} + +// --------------------------------------------------------------------------- +// CLI +// --------------------------------------------------------------------------- + +if (process.argv.includes('--clean')) { + rmrf(DIST); + console.log('Cleaned ' + path.relative(ROOT, DIST) + '/'); +} else if (process.argv.includes('--package')) { + packageZip(); +} else { + build(); +} diff --git a/dist/chrome/_locales/de/messages.json b/dist/chrome/_locales/de/messages.json new file mode 100644 index 0000000..e4d6b0b --- /dev/null +++ b/dist/chrome/_locales/de/messages.json @@ -0,0 +1,198 @@ +{ + "printliminatorName" : { + "message" : "The Printliminator", + "description" : "Extension name" + }, + "printliminatorDescription" : { + "message" : "Ein einfaches Tool um Websiten besser zu drucken.", + "description" : "Extension Beschreibung" + }, + "commandButtons" : { + "message" : "Andere nützliche Superpowers", + "description" : "Command buttons: undo, stylize, remove graphics & drucken" + }, + "clickToRemove" : { + "message" : "Klicke einfach auf Sachen um sie verschwinden zu lassen", + "description" : "Klick um das Element verschwinden zu lassen" + }, + "altClickRemove" : { + "message" : "Alt-click um das Gegenteil zu entfernen", + "description" : "Alt-click um alles ausser das Element verschwinden zu lassen" + }, + "undoLast" : { + "message" : "Undo
Last", + "description" : "Letzte Änderung rückgängig machen" + }, + "addStylize" : { + "message" : "Druck
Styles hinzufügen", + "description" : "Druck-stylesheet hinzufügen" + }, + "removeGraphics" : { + "message" : "Grafiken
entfernen", + "description" : "Alle Bilder, iframes und videos von der Seite entfernen" + }, + "sendToPrint" : { + "message" : "Drucken", + "description" : "Diese Seite drucken" + }, + "viewKeyboardCommands" : { + "message" : "Tastaturbefehle anzeigen", + "description" : "Öffne die Liste mit den Tastaturbefehlen" + }, + "hideKeyboardCommands" : { + "message" : "Tastaturbefehle verstecken", + "description" : "Schliesse die Liste mit den Tastaturbefehlen" + }, + "keyColumn" : { + "message" : "Taste", + "description" : "Tastenname" + }, + "descriptionColumn" : { + "message" : "Beschreibung", + "description" : "Beschreibung der Aktion durch die Taste" + }, + "orText" : { + "message" : "oder", + "description" : "oder text" + }, + "keyPageUp" : { + "message" : "PageUp", + "description" : "Page up Taste" + }, + "keyUpArrow" : { + "message" : "Pfeil nach oben", + "description" : "Pfeil nach oben Taste Text zum Titel hinzugefügt" + }, + "upDescription" : { + "message" : "Finde die Umhüllung der hervorgehobenen Box", + "description" : "Finde die Umhüllung der hervorgehobenen Box" + }, + "keyPageDown" : { + "message" : "PageDown", + "description" : "Page down key" + }, + "keyDownArrow" : { + "message" : "Pfeil nach unten", + "description" : "Pfeil nach unten Text zum Titel hinzugefügt" + }, + "downDescription" : { + "message" : "Find content of highlighted box", + "description" : "Find first visible child element of highlight" + }, + "keyRightArrow" : { + "message" : "Rechter Pfeil", + "description" : "Rechter Pfeil Text zum Titel hinzugefügt" + }, + "rightDescription" : { + "message" : "Find next box inside same wrapper", + "description" : "Find next visible sibling element of highlight" + }, + "keyLeftArrow" : { + "message" : "Linker Pfeil", + "description" : "Linker Pfeil Text zum Titel hinzugefügt" + }, + "leftDescription" : { + "message" : "Finde das nächst kleinere Element in dieser Box", + "description" : "" + }, + "keyEnter" : { + "message" : "Enter", + "description" : "Drücke Enter um die Hervorhebung zu entfernen" + }, + "removeHighlight" : { + "message" : "Die hervorgehobene Box löschen", + "description" : "Lösche die hervorgehobene Box" + }, + "keyBackspace" : { + "message" : "Backspace", + "description" : "Drücke backspace um die letze Handlung rückgängig zu machen" + }, + "undoAction" : { + "message" : "Letzte Handlung ungeschehen machen", + "description" : "Letzte Handlung ungeschehen machen" + }, + "keyNumpad" : { + "message" : "Numpad", + "description" : "Numpad Taste Text Beschreibung" + }, + "keyNumpadPlus" : { + "message" : "Numpad Plus", + "description" : "Numpad+ Taste" + }, + "fontUp" : { + "message" : "Schriftgrösse um 1 vergrössern", + "description" : "Nutze keyNumpad+ um die Schriftgrösse zu vergrössern" + }, + "keyNumpadMinus" : { + "message" : "Numpad Minus", + "description" : "Numpad- Taste" + }, + "fontDown" : { + "message" : "Schriftgrösse um 1 verkleinern", + "description" : "Nutze keyNumpad- um die Schriftgrösse zu verkleinern" + }, + "keyNumpadAsterisk" : { + "message" : "Numpad Asterisk (Multiply)", + "description" : "" + }, + "fontReset" : { + "message" : "Schriftgrösse zurücksetzen", + "description" : "Schriftgrösse zurück auf Standard setzen" + }, + "mouseLeftClick" : { + "message" : "linker Mausklick", + "description" : "linker Mausklick" + }, + "keyAlt" : { + "message" : "Alt", + "description" : "Alt-Taste mit Mausklick" + }, + "removeOpposite" : { + "message" : "Entferne alles ausser die hervorgehobene Box", + "description" : "Entferne das Gegenteil vom Hervorgehobenen" + }, + "keyShift" : { + "message" : "Shift", + "description" : "Shift-taste mit Mausklick" + }, + "fullWidth" : { + "message" : "Set box width to 100% & margins to zero (highlight turns blue)", + "description" : "Make highlighted element full width & add blue outline" + }, + "keyF1" : { + "message" : "F1", + "description" : "F1 Taste" + }, + "keyF1Title" : { + "message" : "Function One", + "description" : "F1 Taste genutzt um Nachrichten ein/auszuschalten" + }, + "toggleMessages" : { + "message" : "Toggle action messages", + "description" : "Action message display is toggled by the F1 key" + }, + "keyPS" : { + "message" : "PrtScn", + "description" : "Print Screen key (abbreviation)" + }, + "keyPSTitle" : { + "message" : "Print Screen", + "description" : "Print Screen key full name added to title" + }, + "printPage" : { + "message" : "Seite drucken", + "description" : "Diese Seite ausdrucken" + }, + "keyEsc" : { + "message" : "Esc", + "description" : "Escapetaste um den Printliminator zu schliessen" + }, + "keyEscTitle" : { + "message" : "Escape", + "description" : "Escapetaste full name" + }, + "abort" : { + "message" : "Printliminator deaktivieren, aber Verlauf sichern", + "description" : "Printliminator deaktivieren, aber Verlauf sichern." + } +} diff --git a/dist/chrome/_locales/pt_BR/messages.json b/dist/chrome/_locales/pt_BR/messages.json new file mode 100644 index 0000000..246d505 --- /dev/null +++ b/dist/chrome/_locales/pt_BR/messages.json @@ -0,0 +1,198 @@ +{ + "printliminatorName" : { + "message" : "The Printliminator", + "description" : "Extension name" + }, + "printliminatorDescription" : { + "message" : "Uma ferramenta simples para tornar a impressão de sites melhor", + "description" : "Extension Description" + }, + "commandButtons" : { + "message" : "Outros Superpoderes Úteis", + "description" : "Command buttons: undo, stylize, remove graphics & print" + }, + "clickToRemove" : { + "message" : "Apenas clique em algo na página para removê-la", + "description" : "Click highlight to remove item" + }, + "altClickRemove" : { + "message" : "Alt-clique para remover o oposto", + "description" : "Alt-click highlight to remove everything expect the item" + }, + "undoLast" : { + "message" : "Desfazer
Último", + "description" : "Undo the last action" + }, + "addStylize" : { + "message" : "Adicionar estilos
de impressão", + "description" : "Add print stylesheet" + }, + "removeGraphics" : { + "message" : "Remover
Gráficos", + "description" : "Removes all images, iframes and video from the page" + }, + "sendToPrint" : { + "message" : "Enviar para
impressão", + "description" : "Print the page" + }, + "viewKeyboardCommands" : { + "message" : "Ver atalhos de teclado", + "description" : "Open a list of keyboard commands" + }, + "hideKeyboardCommands" : { + "message" : "Esconder atalhos de teclado", + "description" : "Close list of keyboard commands" + }, + "keyColumn" : { + "message" : "Chave", + "description" : "Key name column" + }, + "descriptionColumn" : { + "message" : "Descrição", + "description" : "Description of key action" + }, + "orText" : { + "message" : "ou", + "description" : "or text" + }, + "keyPageUp" : { + "message" : "PageUp", + "description" : "Page up key" + }, + "keyUpArrow" : { + "message" : "Seta para cima", + "description" : "Up arrow key text added to title" + }, + "upDescription" : { + "message" : "Encontrar o elemento que envolve a caixa destacada", + "description" : "Find parent element of current highlight" + }, + "keyPageDown" : { + "message" : "PageDown", + "description" : "Page down key" + }, + "keyDownArrow" : { + "message" : "Seta para baixo", + "description" : "Down arrow key text added to title" + }, + "downDescription" : { + "message" : "Encontrar o conteúdo da caixa destacada", + "description" : "Find first visible child element of highlight" + }, + "keyRightArrow" : { + "message" : "Seta para Direita", + "description" : "Right arrow text added to title" + }, + "rightDescription" : { + "message" : "Encontrar o próximo elemento no mesmo container", + "description" : "Find next visible sibling element of highlight" + }, + "keyLeftArrow" : { + "message" : "Seta para Esquerda", + "description" : "Left arrow text added to title" + }, + "leftDescription" : { + "message" : "Encontrar o elemento anterior no mesmo container", + "description" : "" + }, + "keyEnter" : { + "message" : "Enter", + "description" : "Press enter to remove highlight" + }, + "removeHighlight" : { + "message" : "Remove a caixa selecionada", + "description" : "Remove highlighted box" + }, + "keyBackspace" : { + "message" : "Backspace", + "description" : "Press backspace to undo last action" + }, + "undoAction" : { + "message" : "Desfazer a última ação", + "description" : "Undo last action" + }, + "keyNumpad" : { + "message" : "Numpad", + "description" : "Numpad key text description" + }, + "keyNumpadPlus" : { + "message" : "+(mais) do teclado numérico", + "description" : "Numpad+ key" + }, + "fontUp" : { + "message" : "Aumenta o tamanho da fonte em 1", + "description" : "Use keyNumpad+ to increase font size" + }, + "keyNumpadMinus" : { + "message" : "-(menos) do teclado numério", + "description" : "Numpad- key" + }, + "fontDown" : { + "message" : "Diminui o tamanho da fonte em 1", + "description" : "Use keyNumpad- to decrease font size" + }, + "keyNumpadAsterisk" : { + "message" : "*(asterisco) do teclado numérico", + "description" : "" + }, + "fontReset" : { + "message" : "Resetar tamanho da fonte", + "description" : "Reset font size to original" + }, + "mouseLeftClick" : { + "message" : "Clique com o botão esquerdo do mouse", + "description" : "left mouse click" + }, + "keyAlt" : { + "message" : "Alt", + "description" : "Alt-key used with mouse-click" + }, + "removeOpposite" : { + "message" : "Remover tudo menos a caixa selecionada", + "description" : "Remove opposite of highlight" + }, + "keyShift" : { + "message" : "Shift", + "description" : "Shift-key used with mouse-click" + }, + "fullWidth" : { + "message" : "Muda a largura da caixa para 100% & margem para zero (destaque se torna azul)", + "description" : "Make highlighted element full width & add blue outline" + }, + "keyF1" : { + "message" : "F1", + "description" : "F1 key name" + }, + "keyF1Title" : { + "message" : "Função Um", + "description" : "F1 key used to toggle messages" + }, + "toggleMessages" : { + "message" : "Alternar mensagens de ação", + "description" : "Action message display is toggled by the F1 key" + }, + "keyPS" : { + "message" : "PrtScn", + "description" : "Print Screen key (abbreviation)" + }, + "keyPSTitle" : { + "message" : "Print Screen", + "description" : "Print Screen key full name added to title" + }, + "printPage" : { + "message" : "Imprimir Página", + "description" : "Print the current page" + }, + "keyEsc" : { + "message" : "Esc", + "description" : "Escape key to close Printliminator" + }, + "keyEscTitle" : { + "message" : "Escape", + "description" : "Escape key full name" + }, + "abort" : { + "message" : "Desabilita Printliminator, mas salva o histórico", + "description" : "Disable Printliminator, but save undo history." + } +} diff --git a/dist/chrome/_locales/sr/messages.json b/dist/chrome/_locales/sr/messages.json new file mode 100644 index 0000000..0c31a40 --- /dev/null +++ b/dist/chrome/_locales/sr/messages.json @@ -0,0 +1,198 @@ +{ + "printliminatorName" : { + "message" : "The Printliminator", + "description" : "Extension name" + }, + "printliminatorDescription" : { + "message" : "Једноставан алат за боље штампање веб страница.", + "description" : "Extension Description" + }, + "commandButtons" : { + "message" : "Остале корисне могућности", + "description" : "Command buttons: undo, stylize, remove graphics & print" + }, + "clickToRemove" : { + "message" : "Кликни на елемент на страници за уклањање", + "description" : "Click highlight to remove item" + }, + "altClickRemove" : { + "message" : "Alt-клик за уклањање супротног", + "description" : "Alt-click highlight to remove everything expect the item" + }, + "undoLast" : { + "message" : "Опозови
последње", + "description" : "Undo the last action" + }, + "addStylize" : { + "message" : "Додај стилове
штампања", + "description" : "Add print stylesheet" + }, + "removeGraphics" : { + "message" : "Уклони
графику", + "description" : "Removes all images, iframes and video from the page" + }, + "sendToPrint" : { + "message" : "Одштампај
страницу", + "description" : "Print the page" + }, + "viewKeyboardCommands" : { + "message" : "Види тастерске пречице", + "description" : "Open a list of keyboard commands" + }, + "hideKeyboardCommands" : { + "message" : "Сакриј тастерске пречице", + "description" : "Close list of keyboard commands" + }, + "keyColumn" : { + "message" : "Тастер", + "description" : "Key name column" + }, + "descriptionColumn" : { + "message" : "Опис", + "description" : "Description of key action" + }, + "orText" : { + "message" : "или", + "description" : "or text" + }, + "keyPageUp" : { + "message" : "PageUp", + "description" : "Page up key" + }, + "keyUpArrow" : { + "message" : "Стрелица горе", + "description" : "Up arrow key text added to title" + }, + "upDescription" : { + "message" : "Нађи матични елемент који укључује истакнути оквир", + "description" : "Find parent element of current highlight" + }, + "keyPageDown" : { + "message" : "PageDown", + "description" : "Page down key" + }, + "keyDownArrow" : { + "message" : "Стрелица доле", + "description" : "Down arrow key text added to title" + }, + "downDescription" : { + "message" : "Нађи садржај истакнутог оквира", + "description" : "Find first visible child element of highlight" + }, + "keyRightArrow" : { + "message" : "Стрелица десно", + "description" : "Right arrow text added to title" + }, + "rightDescription" : { + "message" : "Нађи следећи оквир унутар истог матичног елемента", + "description" : "Find next visible sibling element of highlight" + }, + "keyLeftArrow" : { + "message" : "Стрелица лево", + "description" : "Left arrow text added to title" + }, + "leftDescription" : { + "message" : "Нађи претходни оквир унутар истог матичног елемента", + "description" : "" + }, + "keyEnter" : { + "message" : "Enter", + "description" : "Press enter to remove highlight" + }, + "removeHighlight" : { + "message" : "Уклони садржај оквира", + "description" : "Remove highlighted box" + }, + "keyBackspace" : { + "message" : "Backspace", + "description" : "Press backspace to undo last action" + }, + "undoAction" : { + "message" : "Опозови последњу радњу", + "description" : "Undo last action" + }, + "keyNumpad" : { + "message" : "Numpad", + "description" : "Numpad key text description" + }, + "keyNumpadPlus" : { + "message" : "Плус на нумеричкој тастатури", + "description" : "Numpad+ key" + }, + "fontUp" : { + "message" : "Повећај величину фонта за 1", + "description" : "Use keyNumpad+ to increase font size" + }, + "keyNumpadMinus" : { + "message" : "Минус на нумеричкој тастатури", + "description" : "Numpad- key" + }, + "fontDown" : { + "message" : "Смањи величину фонта за 1", + "description" : "Use keyNumpad- to decrease font size" + }, + "keyNumpadAsterisk" : { + "message" : "Звездица на нумеричкој тастатури (множење)", + "description" : "" + }, + "fontReset" : { + "message" : "Врати почетну величину фонта", + "description" : "Reset font size to original" + }, + "mouseLeftClick" : { + "message" : "клик левим тастером миша", + "description" : "left mouse click" + }, + "keyAlt" : { + "message" : "Alt", + "description" : "Alt-key used with mouse-click" + }, + "removeOpposite" : { + "message" : "Уклони све изван истакнутог оквира", + "description" : "Remove opposite of highlight" + }, + "keyShift" : { + "message" : "Shift", + "description" : "Shift-key used with mouse-click" + }, + "fullWidth" : { + "message" : "Постави ширину оквира на 100% и ивице на нула (истакнуто постаје плаво)", + "description" : "Make highlighted element full width & add blue outline" + }, + "keyF1" : { + "message" : "F1", + "description" : "F1 key name" + }, + "keyF1Title" : { + "message" : "Function One", + "description" : "F1 key used to toggle messages" + }, + "toggleMessages" : { + "message" : "Укључи/искључи информације о радњи", + "description" : "Action message display is toggled by the F1 key" + }, + "keyPS" : { + "message" : "PrtScn", + "description" : "Print Screen key (abbreviation)" + }, + "keyPSTitle" : { + "message" : "Print Screen", + "description" : "Print Screen key full name added to title" + }, + "printPage" : { + "message" : "Одштампај страницу", + "description" : "Print the current page" + }, + "keyEsc" : { + "message" : "Esc", + "description" : "Escape key to close Printliminator" + }, + "keyEscTitle" : { + "message" : "Escape", + "description" : "Escape key full name" + }, + "abort" : { + "message" : "Онемогући Printliminator али сачувај историју", + "description" : "Disable Printliminator, but save undo history." + } +} diff --git a/dist/chrome/manifest.json b/dist/chrome/manifest.json index 1d4a932..5c35600 100644 --- a/dist/chrome/manifest.json +++ b/dist/chrome/manifest.json @@ -1,10 +1,15 @@ { "name": "__MSG_printliminatorName__", - "version": "4.0.5", - "manifest_version": 2, + "version": "5.0.0", + "manifest_version": 3, "author": "Chris Coyier", + "contributors": [ + "Devon Govett", + "Rob Garrison", + "Joshua Kimsey (Manifest V3 migration)" + ], "description": "__MSG_printliminatorDescription__", - "homepage_url": "https://github.com/CSS-Tricks/The-Printliminator", + "homepage_url": "https://github.com/JoshuaKimsey/The-Printliminator", "default_locale": "en", "icons": { "16": "icon16.png", @@ -13,16 +18,19 @@ "64": "icon64.png", "128": "icon128.png" }, - "browser_action": { + "action": { "default_icon": "icon32.png", "default_title": "__MSG_printliminatorName__", "default_popup": "popup.html" }, "permissions": [ - "activeTab" + "activeTab", + "scripting" ], "web_accessible_resources": [ - "printliminator.js", - "printliminator.css" + { + "resources": ["printliminator.js", "printliminator.css"], + "matches": [""] + } ] } \ No newline at end of file diff --git a/dist/chrome/popup.css b/dist/chrome/popup.css index dbb7093..37cdf99 100644 --- a/dist/chrome/popup.css +++ b/dist/chrome/popup.css @@ -13,7 +13,7 @@ html, body { margin: 0; background: #eee; font-size: 14px; - font-family: "Lucida Grande","Lucida Sans Unicode", Tahoma, sans-serif; + font-family: "Lucida Grande", "Lucida Sans Unicode", Tahoma, sans-serif; overflow: hidden; } @@ -153,7 +153,7 @@ li.busy .icon { /* keyboard table */ table { - font-family: Optima, Segoe, 'Segoe UI', Candara, Calibri, Arial, sans-serif; + font-family: Optima, Segoe, "Segoe UI", Candara, Calibri, Arial, sans-serif; font-size: 12px; text-transform: initial; margin-top: 4px 0 0 0; @@ -208,4 +208,4 @@ span.small { .asterisk { position: relative; top: 2px; -} +} \ No newline at end of file diff --git a/dist/chrome/popup.js b/dist/chrome/popup.js index 7a3068b..01e4a4a 100644 --- a/dist/chrome/popup.js +++ b/dist/chrome/popup.js @@ -1,35 +1,43 @@ /*globals chrome */ // inject printliminator from popup & control from there. +// Manifest V3: uses chrome.scripting.executeScript / insertCSS instead of +// the removed chrome.tabs.executeScript / insertCSS APIs. var commands = { + tabId: null, remove : function() { - chrome.tabs.executeScript( null, { - code: 'thePrintliminator.removeGraphics();' + chrome.scripting.executeScript({ + target: { tabId: commands.tabId }, + func: () => thePrintliminator.removeGraphics() }); }, print : function() { document.querySelector( 'li.print' ).classList.add( 'busy' ); // ready state is delayed when a file on the page is not found - chrome.tabs.executeScript( null, { - code : 'document.readyState === "complete";' + chrome.scripting.executeScript({ + target: { tabId: commands.tabId }, + func: () => document.readyState === 'complete' }, function( result ) { - if ( result && result[ 0 ] === true ) { + if ( result && result[ 0 ].result === true ) { window.close(); - chrome.tabs.executeScript( null, { - code : 'thePrintliminator.print();' + chrome.scripting.executeScript({ + target: { tabId: commands.tabId }, + func: () => thePrintliminator.print() }); } else { // keep checking ready state for 20 seconds // if still not ready, abort, but still call print function var loopy = function( i ) { setTimeout(function () { - chrome.tabs.executeScript( null, { - code : 'document.readyState === "complete";' + chrome.scripting.executeScript({ + target: { tabId: commands.tabId }, + func: () => document.readyState === 'complete' }, function( result ) { - if ( result && result[ 0 ] === true || i === 1 ) { + if ( result && result[ 0 ].result === true || i === 1 ) { i = 0; window.close(); - chrome.tabs.executeScript( null, { - code : 'thePrintliminator.print();' + chrome.scripting.executeScript({ + target: { tabId: commands.tabId }, + func: () => thePrintliminator.print() }); } if ( --i > 0 ) { @@ -44,8 +52,9 @@ var commands = { }); }, stylize : function() { - chrome.tabs.executeScript( null, { - code : 'thePrintliminator.stylize();' + chrome.scripting.executeScript({ + target: { tabId: commands.tabId }, + func: () => thePrintliminator.stylize() }); }, keyboard : function() { @@ -56,8 +65,9 @@ var commands = { this.innerHTML = chrome.i18n.getMessage( mode ? 'hideKeyboardCommands' : 'viewKeyboardCommands' ); }, undo : function() { - chrome.tabs.executeScript( null, { - code : 'thePrintliminator.undo();' + chrome.scripting.executeScript({ + target: { tabId: commands.tabId }, + func: () => thePrintliminator.undo() }); }, setLanguage : function(){ @@ -91,23 +101,30 @@ chrome.windows.getCurrent( function( win ) { return false; } + // store tab id for the command methods + commands.tabId = tabArray[ 0 ].id; + // inject css & js only on initial click - chrome.tabs.executeScript( null, { - code : 'document.querySelector( "body" ).classList.contains( "_printliminator_enabled" );', - matchAboutBlank : true + // NOTE: MV3 scripting API has no matchAboutBlank equivalent; the + // Yahoo Mail print-popup edge case is deferred (would need + // webNavigation + explicit frame targeting). + chrome.scripting.executeScript({ + target: { tabId: commands.tabId }, + func: () => document.querySelector( "body" ).classList.contains( "_printliminator_enabled" ) }, function( result ) { - if ( result && !result[ 0 ] ) { - chrome.tabs.insertCSS( null, { - file : 'printliminator.css', - matchAboutBlank : true + if ( result && !result[ 0 ].result ) { + chrome.scripting.insertCSS({ + target: { tabId: commands.tabId }, + files: [ 'printliminator.css' ] }); - chrome.tabs.executeScript( null, { - file: 'printliminator.js', - matchAboutBlank : true + chrome.scripting.executeScript({ + target: { tabId: commands.tabId }, + files: [ 'printliminator.js' ] }, function() { - chrome.tabs.executeScript( null, { - code : 'thePrintliminator.init();' + chrome.scripting.executeScript({ + target: { tabId: commands.tabId }, + func: () => thePrintliminator.init() }); }); } diff --git a/dist/chrome/printliminator.css b/dist/chrome/printliminator.css index edfbefc..e725753 100644 --- a/dist/chrome/printliminator.css +++ b/dist/chrome/printliminator.css @@ -10,8 +10,6 @@ background: none !important; font-size: 12pt !important; /*Headings */ - /* Images */ - /* Table */ } body._printliminator_stylized h1, body._printliminator_stylized h2, body._printliminator_stylized h3, body._printliminator_stylized h4, body._printliminator_stylized h5, body._printliminator_stylized h6 { font-family: Helvetica, Arial, sans-serif !important; @@ -39,6 +37,9 @@ body._printliminator_stylized hr { background-color: #ccc !important; } + body._printliminator_stylized { + /* Images */ + } body._printliminator_stylized img { float: left !important; margin: 1em 1.5em 1.5em 0 !important; @@ -46,6 +47,9 @@ body._printliminator_stylized a img { border: none !important; } + body._printliminator_stylized { + /* Table */ + } body._printliminator_stylized table { margin: 1px !important; text-align: left !important; @@ -76,11 +80,9 @@ body._printliminator_stylized tr { page-break-inside: avoid !important; } - ._printliminator_hidden { display: none !important; } - ._printliminator_full_width { width: 100% !important; min-width: 100% !important; @@ -97,7 +99,6 @@ body._printliminator_stylized { padding: 20px !important; } - ._printliminator_highlight { outline: 3px solid red !important; } @@ -111,7 +112,6 @@ -ms-user-select: none !important; user-select: none !important; } - /* ul & li have same class to make it easier to ignore and reset all possible settings */ @@ -132,7 +132,6 @@ ._printliminator_messages:before, ._printliminator_messages:after { display: none !important; } - ul._printliminator_messages { background: transparent !important; list-style-type: none !important; @@ -142,7 +141,6 @@ z-index: 999999 !important; max-width: 400px !important; } - li._printliminator_messages { background: rgba(0, 0, 0, 0.8) !important; color: #ddd !important; @@ -150,4 +148,4 @@ margin: 5px !important; max-width: none !important; } -} +} \ No newline at end of file diff --git a/dist/chrome/printliminator.js b/dist/chrome/printliminator.js index 2711e5a..d614035 100644 --- a/dist/chrome/printliminator.js +++ b/dist/chrome/printliminator.js @@ -1,4 +1,4 @@ -/* Printliminator v4.0.5 +/* Printliminator v5.0.0 * https://github.com/CSS-Tricks/The-Printliminator */ /*jshint expr:false */ @@ -8,7 +8,7 @@ var pl = window.thePrintliminator = { - version : '4.0.5', + version : '5.0.0', // preprocess is used to echo in settings from options.json css : { diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..5318abb --- /dev/null +++ b/package-lock.json @@ -0,0 +1,452 @@ +{ + "name": "Printliminator", + "version": "4.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "Printliminator", + "version": "4.1.0", + "license": "LGPL-3.0", + "devDependencies": { + "sass": "^1.77.0" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", + "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.3", + "is-glob": "^4.0.3", + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.6", + "@parcel/watcher-darwin-arm64": "2.5.6", + "@parcel/watcher-darwin-x64": "2.5.6", + "@parcel/watcher-freebsd-x64": "2.5.6", + "@parcel/watcher-linux-arm-glibc": "2.5.6", + "@parcel/watcher-linux-arm-musl": "2.5.6", + "@parcel/watcher-linux-arm64-glibc": "2.5.6", + "@parcel/watcher-linux-arm64-musl": "2.5.6", + "@parcel/watcher-linux-x64-glibc": "2.5.6", + "@parcel/watcher-linux-x64-musl": "2.5.6", + "@parcel/watcher-win32-arm64": "2.5.6", + "@parcel/watcher-win32-ia32": "2.5.6", + "@parcel/watcher-win32-x64": "2.5.6" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz", + "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz", + "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz", + "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz", + "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz", + "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz", + "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz", + "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz", + "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz", + "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz", + "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz", + "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz", + "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz", + "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/immutable": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz", + "integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/sass": { + "version": "1.101.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.101.0.tgz", + "integrity": "sha512-OL3GoQyoUdDt843DpVmDO6y2k1sc5IhUDSpu8XucEI+35neq5QivZ1iuegnpraEVTJXlQGK1gl27zKcTLEPbQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^5.0.0", + "immutable": "^5.1.5", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=20.19.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + } + } +} diff --git a/package.json b/package.json index bb6f289..35e30ae 100644 --- a/package.json +++ b/package.json @@ -1,28 +1,23 @@ { "name": "Printliminator", - "version": "4.0.5", + "version": "5.0.0", "description": "Printliminator is a simple tool you can use to make websites print better. One click to activate, and then click to remove elements from the page, remove graphics, and apply better print styling.", "repository": { "type": "git", "url": "https://github.com/CSS-Tricks/The-Printliminator" }, "author": "Chris Coyier", - "license": "GPL-3.0", + "license": "LGPL-3.0", "bugs": { "url": "https://github.com/CSS-Tricks/The-Printliminator/issues" }, "homepage": "https://github.com/CSS-Tricks/The-Printliminator", + "scripts": { + "build": "node build.js", + "clean": "node build.js --clean", + "package": "node build.js --package" + }, "devDependencies": { - "grunt": "^0.4.5", - "grunt-cli": "~0.1.13", - "grunt-contrib-clean": "^0.6.0", - "grunt-contrib-compress": "^0.13.0", - "grunt-contrib-copy": "^0.7.0", - "grunt-contrib-jasmine": "^0.9.1", - "grunt-contrib-jshint": "^0.11.2", - "grunt-contrib-sass": "^0.9.2", - "grunt-contrib-uglify": "^0.7.0", - "grunt-crx": "^1.0.3", - "grunt-preprocess": "^5.0.1" + "sass": "^1.77.0" } } diff --git a/src/chrome/manifest.json b/src/chrome/manifest.json index 1d4a932..5c35600 100644 --- a/src/chrome/manifest.json +++ b/src/chrome/manifest.json @@ -1,10 +1,15 @@ { "name": "__MSG_printliminatorName__", - "version": "4.0.5", - "manifest_version": 2, + "version": "5.0.0", + "manifest_version": 3, "author": "Chris Coyier", + "contributors": [ + "Devon Govett", + "Rob Garrison", + "Joshua Kimsey (Manifest V3 migration)" + ], "description": "__MSG_printliminatorDescription__", - "homepage_url": "https://github.com/CSS-Tricks/The-Printliminator", + "homepage_url": "https://github.com/JoshuaKimsey/The-Printliminator", "default_locale": "en", "icons": { "16": "icon16.png", @@ -13,16 +18,19 @@ "64": "icon64.png", "128": "icon128.png" }, - "browser_action": { + "action": { "default_icon": "icon32.png", "default_title": "__MSG_printliminatorName__", "default_popup": "popup.html" }, "permissions": [ - "activeTab" + "activeTab", + "scripting" ], "web_accessible_resources": [ - "printliminator.js", - "printliminator.css" + { + "resources": ["printliminator.js", "printliminator.css"], + "matches": [""] + } ] } \ No newline at end of file diff --git a/src/chrome/popup.js b/src/chrome/popup.js index 37b6976..24ba6de 100644 --- a/src/chrome/popup.js +++ b/src/chrome/popup.js @@ -1,35 +1,43 @@ /*globals chrome */ // inject printliminator from popup & control from there. +// Manifest V3: uses chrome.scripting.executeScript / insertCSS instead of +// the removed chrome.tabs.executeScript / insertCSS APIs. var commands = { + tabId: null, remove : function() { - chrome.tabs.executeScript( null, { - code: 'thePrintliminator.removeGraphics();' + chrome.scripting.executeScript({ + target: { tabId: commands.tabId }, + func: () => thePrintliminator.removeGraphics() }); }, print : function() { - document.querySelector( 'li.print' ).classList.add( '/* @echo busy */' ); + document.querySelector( 'li./* @echo print */' ).classList.add( '/* @echo busy */' ); // ready state is delayed when a file on the page is not found - chrome.tabs.executeScript( null, { - code : 'document.readyState === "complete";' + chrome.scripting.executeScript({ + target: { tabId: commands.tabId }, + func: () => document.readyState === 'complete' }, function( result ) { - if ( result && result[ 0 ] === true ) { + if ( result && result[ 0 ].result === true ) { window.close(); - chrome.tabs.executeScript( null, { - code : 'thePrintliminator.print();' + chrome.scripting.executeScript({ + target: { tabId: commands.tabId }, + func: () => thePrintliminator.print() }); } else { // keep checking ready state for 20 seconds // if still not ready, abort, but still call print function var loopy = function( i ) { setTimeout(function () { - chrome.tabs.executeScript( null, { - code : 'document.readyState === "complete";' + chrome.scripting.executeScript({ + target: { tabId: commands.tabId }, + func: () => document.readyState === 'complete' }, function( result ) { - if ( result && result[ 0 ] === true || i === 1 ) { + if ( result && result[ 0 ].result === true || i === 1 ) { i = 0; window.close(); - chrome.tabs.executeScript( null, { - code : 'thePrintliminator.print();' + chrome.scripting.executeScript({ + target: { tabId: commands.tabId }, + func: () => thePrintliminator.print() }); } if ( --i > 0 ) { @@ -44,8 +52,9 @@ var commands = { }); }, stylize : function() { - chrome.tabs.executeScript( null, { - code : 'thePrintliminator.stylize();' + chrome.scripting.executeScript({ + target: { tabId: commands.tabId }, + func: () => thePrintliminator.stylize() }); }, keyboard : function() { @@ -56,8 +65,9 @@ var commands = { this.innerHTML = chrome.i18n.getMessage( mode ? 'hideKeyboardCommands' : 'viewKeyboardCommands' ); }, undo : function() { - chrome.tabs.executeScript( null, { - code : 'thePrintliminator.undo();' + chrome.scripting.executeScript({ + target: { tabId: commands.tabId }, + func: () => thePrintliminator.undo() }); }, setLanguage : function(){ @@ -91,23 +101,30 @@ chrome.windows.getCurrent( function( win ) { return false; } + // store tab id for the command methods + commands.tabId = tabArray[ 0 ].id; + // inject css & js only on initial click - chrome.tabs.executeScript( null, { - code : 'document.querySelector( "body" ).classList.contains( "/* @echo enabled */" );', - matchAboutBlank : true + // NOTE: MV3 scripting API has no matchAboutBlank equivalent; the + // Yahoo Mail print-popup edge case is deferred (would need + // webNavigation + explicit frame targeting). + chrome.scripting.executeScript({ + target: { tabId: commands.tabId }, + func: () => document.querySelector( "body" ).classList.contains( "/* @echo enabled */" ) }, function( result ) { - if ( result && !result[ 0 ] ) { - chrome.tabs.insertCSS( null, { - file : 'printliminator.css', - matchAboutBlank : true + if ( result && !result[ 0 ].result ) { + chrome.scripting.insertCSS({ + target: { tabId: commands.tabId }, + files: [ 'printliminator.css' ] }); - chrome.tabs.executeScript( null, { - file: 'printliminator.js', - matchAboutBlank : true + chrome.scripting.executeScript({ + target: { tabId: commands.tabId }, + files: [ 'printliminator.js' ] }, function() { - chrome.tabs.executeScript( null, { - code : 'thePrintliminator.init();' + chrome.scripting.executeScript({ + target: { tabId: commands.tabId }, + func: () => thePrintliminator.init() }); }); }