Initial Commit

This commit is contained in:
Andy Dixon
2025-10-16 11:58:46 +01:00
commit e7d2200ef0
36 changed files with 4069 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
dist
+328
View File
@@ -0,0 +1,328 @@
# Build Instructions
This document contains detailed instructions for building and distributing the Greasemonkey browser extension.
## Prerequisites
- Web browser (Chrome or Firefox)
- Text editor (for development)
- Node.js and npm (optional, for Firefox automated builds)
## Quick Start
### Testing Locally
#### Chrome
```bash
1. Open Chrome
2. Navigate to chrome://extensions/
3. Enable "Developer mode" (top-right toggle)
4. Click "Load unpacked"
5. Select the greasemonkey/chrome directory
```
#### Firefox
```bash
1. Open Firefox
2. Navigate to about:debugging#/runtime/this-firefox
3. Click "Load Temporary Add-on"
4. Select greasemonkey/firefox/manifest.json
```
## Development Workflow
### 1. Making Changes
Edit files in the appropriate directory (`chrome/` or `firefox/`).
**Key files:**
- `manifest.json` - Extension configuration
- `popup.html` - UI structure
- `popup.css` - UI styling
- `popup.js` - UI functionality
- `content.js` - Code that runs on web pages
- `background.js` - Background service/script
### 2. Testing Changes
After making changes:
**Chrome:**
1. Go to `chrome://extensions/`
2. Click the reload icon on the Greasemonkey extension card
3. Test your changes
**Firefox:**
1. Go to `about:debugging#/runtime/this-firefox`
2. Click "Reload" on the Greasemonkey extension
3. Test your changes
### 3. Debugging
**Chrome:**
- Background script logs: `chrome://extensions/` → "service worker" link
- Content script logs: Browser console on any page (F12)
- Popup logs: Right-click popup → "Inspect"
**Firefox:**
- Background script logs: `about:debugging` → "Inspect" on extension
- Content script logs: Browser console on any page (F12)
- Popup logs: Right-click popup → "Inspect Element"
## Building for Distribution
### Chrome/Edge Build
#### Method 1: Manual Packaging (Recommended for Testing)
1. Open Chrome
2. Go to `chrome://extensions/`
3. Ensure "Developer mode" is enabled
4. Click "Pack extension"
5. For "Extension root directory", select the `chrome` folder
6. Leave "Private key file" empty for first build
7. Click "Pack Extension"
8. Chrome generates two files:
- `chrome.crx` - The packaged extension
- `chrome.pem` - Private key (keep this secure for updates!)
#### Method 2: Chrome Web Store Distribution
1. Create a developer account at [Chrome Web Store](https://chrome.google.com/webstore/developer/dashboard)
2. Pay one-time $5 registration fee
3. Create a ZIP file of the `chrome` directory:
```bash
cd chrome
zip -r ../greasemonkey-chrome.zip *
```
4. Upload to Chrome Web Store dashboard
5. Fill in store listing details
6. Submit for review
**Note:** Keep your `.pem` private key file secure. You'll need it for future updates.
### Firefox Build
#### Method 1: Temporary Installation (Development)
Already covered in Quick Start - good for testing only. Temporary add-ons are removed when Firefox restarts.
#### Method 2: Self-Distribution (Unverified)
1. Create a ZIP file:
```bash
cd firefox
zip -r ../greasemonkey-firefox.zip *
```
2. Rename to `.xpi`:
```bash
mv greasemonkey-firefox.zip greasemonkey-firefox.xpi
```
3. Users can install by dragging `.xpi` file to Firefox
4. Firefox will show "unverified" warning
#### Method 3: Official Distribution (Recommended)
1. Install web-ext tool:
```bash
npm install -g web-ext
```
2. Build the extension:
```bash
cd firefox
web-ext build
```
This creates a ZIP file in `web-ext-artifacts/` directory
3. Sign with Mozilla:
- Create account at [addons.mozilla.org](https://addons.mozilla.org/developers/)
- Get API keys from [Developer Hub](https://addons.mozilla.org/developers/addon/api/key/)
- Sign the extension:
```bash
web-ext sign \
--api-key=YOUR_API_KEY \
--api-secret=YOUR_API_SECRET
```
- Or upload manually to addons.mozilla.org
4. Distribution options:
- **Listed**: Public on addons.mozilla.org (requires review)
- **Unlisted**: Self-distribute signed XPI (automatic signing)
## Directory Structure
```
greasemonkey/
├── chrome/ # Chrome extension
│ ├── manifest.json
│ ├── background.js
│ ├── content.js
│ ├── popup.html
│ ├── popup.css
│ ├── popup.js
│ ├── icon16.png
│ ├── icon48.png
│ └── icon128.png
├── firefox/ # Firefox extension
│ ├── manifest.json
│ ├── background.js
│ ├── content.js
│ ├── popup.html
│ ├── popup.css
│ ├── popup.js
│ ├── icon16.png
│ ├── icon48.png
│ └── icon128.png
├── README.md
└── BUILD.md # This file
```
## Version Updates
When releasing a new version:
1. Update version number in both `manifest.json` files:
```json
"version": "1.1.0"
```
2. Update README.md version history
3. Test thoroughly in both browsers
4. Build new packages:
- Chrome: Re-pack with same `.pem` key
- Firefox: Build and sign new version
5. Upload to respective stores/distribution channels
## Quality Checklist
Before building for distribution:
- [ ] Test all features in Chrome
- [ ] Test all features in Firefox
- [ ] Verify icons display correctly
- [ ] Check manifest.json versions match
- [ ] Test storage sync functionality
- [ ] Verify permissions are appropriate
- [ ] Test on various websites
- [ ] Check browser console for errors
- [ ] Test add/edit/delete rule functionality
- [ ] Verify enable/disable toggles work
- [ ] Test all match types (URL, Element, Both)
- [ ] Review code for security issues
- [ ] Update version numbers
- [ ] Update documentation
## File Size Optimization
To reduce extension size:
1. Minify JavaScript (optional):
```bash
npm install -g terser
terser popup.js -c -m -o popup.min.js
```
2. Optimize PNG icons:
```bash
# If you have optipng installed
optipng icon*.png
```
3. Remove development files:
- Source maps
- Test files
- Documentation (keep only in repo)
## Automated Build Script
You can create a build script for automation:
```bash
#!/bin/bash
# build.sh
VERSION="1.0.0"
echo "Building Greasemonkey v${VERSION}..."
# Chrome build
echo "Building Chrome extension..."
cd chrome
zip -r "../dist/greasemonkey-chrome-${VERSION}.zip" * -x "*.DS_Store"
cd ..
# Firefox build
echo "Building Firefox extension..."
cd firefox
zip -r "../dist/greasemonkey-firefox-${VERSION}.zip" * -x "*.DS_Store"
cd ..
echo "Build complete! Files in dist/ directory"
```
Make it executable:
```bash
chmod +x build.sh
mkdir -p dist
./build.sh
```
## Common Issues
### Chrome Extension Doesn't Load
- Check manifest.json syntax (use JSON validator)
- Ensure all referenced files exist
- Check browser console for errors
### Firefox Temporary Add-on Removed
- This is expected behavior
- Use web-ext for persistent testing:
```bash
cd firefox
web-ext run
```
### Icons Not Showing
- Verify icon files exist
- Check file paths in manifest.json
- Ensure icon sizes are correct (16x16, 48x48, 128x128)
### Storage Not Persisting
- Chrome: Check sync is enabled in browser
- Firefox: Temporary add-ons don't persist storage
- Use proper signed/installed extension for testing
## Distribution Platforms
### Official Stores
- [Chrome Web Store](https://chrome.google.com/webstore/developer/dashboard)
- [Firefox Add-ons](https://addons.mozilla.org/developers/)
- [Microsoft Edge Add-ons](https://partner.microsoft.com/dashboard/microsoftedge/overview)
### Self-Distribution
- GitHub Releases
- Personal website
- Direct XPI/CRX files
## Security Notes
- Never commit your Chrome `.pem` private key
- Keep Firefox API keys secure
- Don't bundle sensitive data in extension
- Review all code before distribution
- Test on malicious sites carefully
## Resources
- [Chrome Extension Documentation](https://developer.chrome.com/docs/extensions/)
- [Firefox Extension Documentation](https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions)
- [web-ext Documentation](https://extensionworkshop.com/documentation/develop/getting-started-with-web-ext/)
- [Bootstrap Documentation](https://getbootstrap.com/docs/5.3/)
## Support
For build issues or questions, contact Andy Dixon.
+86
View File
@@ -0,0 +1,86 @@
# Changelog
All notable changes to GreasyBoii will be documented in this file.
## [1.0.2] - 2024-10-16
### Fixed
- **CRITICAL: CSP Violation** - Fixed Content Security Policy blocking external Bootstrap CDN
- **Modal initialization** - Now works correctly with local Bootstrap files
- Extension now fully functional in Chrome Manifest V3
### Changed
- **Renamed to GreasyBoii** - Extension renamed from "Greasemonkey" to "GreasyBoii"
- **Local Bootstrap** - Downloaded Bootstrap 5.3.2 files locally (227KB CSS + 79KB JS)
- Updated all console messages to show "GreasyBoii" branding
- Updated all UI text to reflect new name
### Added
- bootstrap.min.css (227KB) - Local Bootstrap CSS
- bootstrap.bundle.min.js (79KB) - Local Bootstrap JS with Popper
## [1.0.1] - 2024-10-16
### Fixed
- **Add Rule button not working** - Fixed Bootstrap modal initialization timing issue
- Added fallback initialization for Bootstrap Modal to handle CDN load timing
- Improved error handling and logging in popup.js
- Added null checks for DOM elements to prevent errors
- Added console logging for debugging button clicks
### Changed
- Enhanced `setupEventListeners()` with better error detection
- Improved `openRuleModal()` with fallback modal initialization
- Added `initializeExtension()` wrapper function for proper initialization order
### Added
- TROUBLESHOOTING.md - Comprehensive troubleshooting guide
- Console logging for debugging initialization issues
- Better error messages when elements are not found
## [1.0.0] - 2024-10-16
### Fixed
- **Add Rule button not working** - Fixed Bootstrap modal initialization timing issue
- Added fallback initialization for Bootstrap Modal to handle CDN load timing
- Improved error handling and logging in popup.js
- Added null checks for DOM elements to prevent errors
- Added console logging for debugging button clicks
### Changed
- Enhanced `setupEventListeners()` with better error detection
- Improved `openRuleModal()` with fallback modal initialization
- Added `initializeExtension()` wrapper function for proper initialization order
### Added
- TROUBLESHOOTING.md - Comprehensive troubleshooting guide
- Console logging for debugging initialization issues
- Better error messages when elements are not found
## [1.0.0] - 2024-10-16
### Added
- Initial release
- Chrome extension (Manifest V3)
- Firefox extension (Manifest V2)
- URL pattern matching
- CSS selector (element) matching
- Combined URL + element matching
- JavaScript execution
- CSS injection
- Bootstrap 5 dark theme UI
- Add/edit/delete rules
- Enable/disable toggles
- Persistent synced storage
- Complete documentation suite
- Example rules library
- Interactive test page
- Build instructions
### Features
- Flexible matching system (URL, Element, Both)
- Modern Bootstrap dark UI
- Full CRUD operations for rules
- Form validation
- Error handling
- Cross-browser support (Chrome, Firefox, Edge, Opera)
+477
View File
@@ -0,0 +1,477 @@
# Greasemonkey Example Rules
This document contains useful example rules you can use with Greasemonkey. Copy and paste these into your extension to get started quickly.
## Table of Contents
1. [Page Styling](#page-styling)
2. [Element Manipulation](#element-manipulation)
3. [Content Blocking](#content-blocking)
4. [Automation](#automation)
5. [Custom Features](#custom-features)
---
## Page Styling
### Dark Mode for Any Site
**Match Type:** URL contains string
**URL Pattern:** `example.com` (replace with your target site)
**CSS Code:**
```css
body {
background-color: #1a1a1a !important;
color: #e0e0e0 !important;
}
a {
color: #4a9eff !important;
}
div, p, span, article {
background-color: #1a1a1a !important;
color: #e0e0e0 !important;
}
```
### Increase Font Size
**Match Type:** URL contains string
**URL Pattern:** `news-site.com`
**CSS Code:**
```css
body {
font-size: 18px !important;
line-height: 1.6 !important;
}
p {
font-size: 18px !important;
}
```
### Remove Animations
**Match Type:** URL contains string
**URL Pattern:** (any site with annoying animations)
**CSS Code:**
```css
* {
animation: none !important;
transition: none !important;
}
```
### Custom Color Scheme
**Match Type:** URL contains string
**URL Pattern:** `docs.` (targets documentation sites)
**CSS Code:**
```css
:root {
--bg-color: #282c34;
--text-color: #abb2bf;
--accent-color: #61afef;
}
body {
background-color: var(--bg-color) !important;
color: var(--text-color) !important;
}
a {
color: var(--accent-color) !important;
}
```
---
## Element Manipulation
### Highlight Search Results
**Match Type:** Page contains element
**Element Selector:** `.search-result`
**CSS Code:**
```css
.search-result:hover {
background-color: #fff3cd !important;
border-left: 4px solid #ffc107 !important;
transition: all 0.2s ease !important;
}
```
### Auto-Expand Collapsed Content
**Match Type:** Page contains element
**Element Selector:** `.collapsed`
**JavaScript Code:**
```javascript
document.querySelectorAll('.collapsed').forEach(elem => {
elem.classList.remove('collapsed');
elem.style.display = 'block';
});
```
### Add Copy Buttons to Code Blocks
**Match Type:** Page contains element
**Element Selector:** `pre code`
**JavaScript Code:**
```javascript
document.querySelectorAll('pre code').forEach(block => {
const button = document.createElement('button');
button.textContent = 'Copy';
button.style.cssText = 'position:absolute;top:5px;right:5px;padding:5px 10px;cursor:pointer;';
button.onclick = () => {
navigator.clipboard.writeText(block.textContent);
button.textContent = 'Copied!';
setTimeout(() => button.textContent = 'Copy', 2000);
};
block.parentElement.style.position = 'relative';
block.parentElement.appendChild(button);
});
```
### Enhance Tables
**Match Type:** Page contains element
**Element Selector:** `table`
**CSS Code:**
```css
table {
border-collapse: collapse !important;
width: 100% !important;
margin: 20px 0 !important;
}
table th {
background-color: #4CAF50 !important;
color: white !important;
padding: 12px !important;
text-align: left !important;
}
table td {
padding: 10px !important;
border-bottom: 1px solid #ddd !important;
}
table tr:hover {
background-color: #f5f5f5 !important;
}
```
---
## Content Blocking
### Hide Advertisements
**Match Type:** URL contains string
**URL Pattern:** (target site)
**CSS Code:**
```css
.ad,
.ads,
.advertisement,
[class*="ad-"],
[id*="ad-"],
.sponsored,
.promotion {
display: none !important;
}
```
### Remove Cookie Notices
**Match Type:** URL contains string
**URL Pattern:** (any site)
**CSS Code:**
```css
[class*="cookie"],
[id*="cookie"],
[class*="consent"],
[id*="gdpr"],
.privacy-banner {
display: none !important;
}
```
### Hide Social Media Widgets
**Match Type:** URL contains string
**URL Pattern:** (target site)
**CSS Code:**
```css
.social-share,
.share-buttons,
[class*="social-"],
iframe[src*="facebook"],
iframe[src*="twitter"] {
display: none !important;
}
```
### Remove Popups
**Match Type:** URL contains string
**URL Pattern:** (target site)
**JavaScript Code:**
```javascript
// Remove overlay popups
document.querySelectorAll('.popup, .modal, .overlay').forEach(el => {
el.remove();
});
// Re-enable scrolling
document.body.style.overflow = 'auto';
document.documentElement.style.overflow = 'auto';
```
---
## Automation
### Auto-Close Annoying Popups
**Match Type:** Page contains element
**Element Selector:** `.modal`
**JavaScript Code:**
```javascript
setTimeout(() => {
const closeButton = document.querySelector('.modal .close, .modal [aria-label="Close"]');
if (closeButton) {
closeButton.click();
console.log('Popup auto-closed');
}
}, 2000);
```
### Auto-Accept Cookies
**Match Type:** Page contains element
**Element Selector:** `[class*="cookie"]`
**JavaScript Code:**
```javascript
const acceptButton = document.querySelector(
'button[id*="accept"], button[class*="accept"], .cookie-accept, #acceptCookies'
);
if (acceptButton) {
acceptButton.click();
console.log('Cookies auto-accepted');
}
```
### Auto-Fill Test Form
**Match Type:** Both URL and element
**URL Pattern:** `test-form.html`
**Element Selector:** `#myForm`
**JavaScript Code:**
```javascript
document.getElementById('username').value = 'testuser';
document.getElementById('email').value = 'test@example.com';
console.log('Form auto-filled');
```
### Skip Video Ads
**Match Type:** Page contains element
**Element Selector:** `.video-player`
**JavaScript Code:**
```javascript
setInterval(() => {
const skipButton = document.querySelector('.skip-ad, .ytp-ad-skip-button');
if (skipButton && skipButton.offsetParent !== null) {
skipButton.click();
console.log('Ad skipped');
}
}, 1000);
```
---
## Custom Features
### Add Floating Note Pad
**Match Type:** URL contains string
**URL Pattern:** (any site where you want notes)
**JavaScript Code:**
```javascript
const notepad = document.createElement('div');
notepad.innerHTML = `
<div style="position:fixed;top:10px;right:10px;width:300px;z-index:10000;background:white;border:2px solid #333;border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,0.3);">
<div style="background:#333;color:white;padding:10px;cursor:move;border-radius:6px 6px 0 0;">
Quick Notes
</div>
<textarea id="greasemonkey-notes" style="width:100%;height:200px;padding:10px;border:none;font-family:monospace;"></textarea>
</div>
`;
document.body.appendChild(notepad);
// Load saved notes
const notes = localStorage.getItem('greasemonkey-notes') || '';
document.getElementById('greasemonkey-notes').value = notes;
// Save notes on change
document.getElementById('greasemonkey-notes').addEventListener('input', (e) => {
localStorage.setItem('greasemonkey-notes', e.target.value);
});
```
### Add Page Stats Widget
**Match Type:** URL contains string
**URL Pattern:** (any site)
**JavaScript Code:**
```javascript
const stats = document.createElement('div');
stats.style.cssText = 'position:fixed;bottom:10px;right:10px;background:#333;color:white;padding:10px;border-radius:8px;font-size:12px;z-index:10000;';
const updateStats = () => {
const wordCount = document.body.innerText.split(/\s+/).length;
const linkCount = document.querySelectorAll('a').length;
const imageCount = document.querySelectorAll('img').length;
stats.innerHTML = `
<strong>Page Stats</strong><br>
Words: ${wordCount}<br>
Links: ${linkCount}<br>
Images: ${imageCount}
`;
};
document.body.appendChild(stats);
updateStats();
```
### Reading Mode
**Match Type:** URL contains string
**URL Pattern:** `article` or `blog`
**CSS Code:**
```css
body * {
display: none !important;
}
article,
article *,
.article-content,
.article-content *,
.post-content,
.post-content * {
display: block !important;
}
body {
max-width: 800px !important;
margin: 0 auto !important;
padding: 40px 20px !important;
background: #f5f5f5 !important;
}
article {
background: white !important;
padding: 40px !important;
line-height: 1.8 !important;
font-size: 18px !important;
}
```
### Add Custom Keyboard Shortcuts
**Match Type:** URL contains string
**URL Pattern:** (target site)
**JavaScript Code:**
```javascript
document.addEventListener('keydown', (e) => {
// Ctrl+Shift+D - Toggle dark mode
if (e.ctrlKey && e.shiftKey && e.key === 'D') {
document.body.style.filter =
document.body.style.filter === 'invert(1)' ? '' : 'invert(1)';
}
// Ctrl+Shift+H - Hide images
if (e.ctrlKey && e.shiftKey && e.key === 'H') {
document.querySelectorAll('img').forEach(img => {
img.style.display = img.style.display === 'none' ? '' : 'none';
});
}
});
```
### Custom Scroll Progress Bar
**Match Type:** URL contains string
**URL Pattern:** (any site with long content)
**JavaScript Code:**
```javascript
const progressBar = document.createElement('div');
progressBar.style.cssText = 'position:fixed;top:0;left:0;width:0%;height:4px;background:linear-gradient(90deg,#667eea,#764ba2);z-index:10000;transition:width 0.3s;';
document.body.appendChild(progressBar);
window.addEventListener('scroll', () => {
const winScroll = document.documentElement.scrollTop;
const height = document.documentElement.scrollHeight - document.documentElement.clientHeight;
const scrolled = (winScroll / height) * 100;
progressBar.style.width = scrolled + '%';
});
```
---
## Tips for Creating Your Own Rules
1. **Test in Console First**: Try your JavaScript in the browser console (F12) before adding to a rule
2. **Use !important**: CSS often needs `!important` to override existing styles
3. **Check Element Selectors**: Right-click → Inspect to find correct selectors
4. **Start Simple**: Begin with basic rules and add complexity
5. **Use Console Logging**: Add `console.log()` to debug your JavaScript
6. **Test Match Conditions**: Verify URL patterns and selectors match correctly
## Combining Rules
You can combine multiple techniques in one rule:
**Match Type:** Both URL and element
**URL Pattern:** `github.com`
**Element Selector:** `.repository-content`
**CSS Code:**
```css
.repository-content {
max-width: 1400px !important;
margin: 0 auto !important;
}
```
**JavaScript Code:**
```javascript
console.log('GitHub enhancer loaded');
document.querySelectorAll('.markdown-body').forEach(md => {
md.style.fontSize = '16px';
md.style.lineHeight = '1.8';
});
```
---
## Advanced Patterns
### Waiting for Elements (Mutation Observer)
```javascript
const observer = new MutationObserver((mutations) => {
const targetElement = document.querySelector('.dynamic-element');
if (targetElement) {
targetElement.style.background = 'yellow';
observer.disconnect();
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
```
### Periodic Checks
```javascript
setInterval(() => {
const ads = document.querySelectorAll('.ad');
ads.forEach(ad => ad.remove());
}, 1000);
```
### Event Delegation
```javascript
document.addEventListener('click', (e) => {
if (e.target.matches('.my-button')) {
console.log('Button clicked!');
e.preventDefault();
}
}, true);
```
---
Happy scripting! Create your own custom rules and enhance your browsing experience. 🚀
+267
View File
@@ -0,0 +1,267 @@
# Installing GreasyBoii (Packaged Version)
Your extensions are now packaged and ready to install!
## Files Created
✅ `dist/greasyboii-chrome-v1.0.4.zip` (62 KB)
✅ `dist/greasyboii-firefox-v1.0.4.zip` (62 KB)
---
## Chrome Installation
### Method 1: Load Unpacked (Development)
1. Open `chrome://extensions/`
2. Enable "Developer mode" (top-right toggle)
3. Click "Load unpacked"
4. Select the `chrome` folder
5. ✅ Done!
### Method 2: Install ZIP (Distribution)
1. Unzip `greasyboii-chrome-v1.0.4.zip` to a folder
2. Open `chrome://extensions/`
3. Enable "Developer mode"
4. Click "Load unpacked"
5. Select the unzipped folder
6. ✅ Done!
### Method 3: Pack to CRX (Best for Sharing)
1. Open `chrome://extensions/`
2. Enable "Developer mode"
3. Click "Pack extension"
4. For "Extension root directory" → Select the `chrome` folder
5. Leave "Private key file" empty
6. Click "Pack Extension"
7. You get `chrome.crx` file (the actual extension)
8. Share this `.crx` file with others
9. Users drag it to `chrome://extensions/` to install
**Note:** Chrome will show "This extension is not from the Chrome Web Store" warning for unpublished extensions.
---
## Firefox Installation
### Method 1: Load Temporary (Development)
1. Open `about:debugging#/runtime/this-firefox`
2. Click "Load Temporary Add-on"
3. Select any file from the `firefox` folder (e.g., `manifest.json`)
4. ✅ Done! (Removed when Firefox restarts)
### Method 2: Install ZIP (Distribution)
1. Rename `greasyboii-firefox-v1.0.4.zip` to `greasyboii-firefox-v1.0.4.xpi`
2. Drag the `.xpi` file into Firefox
3. Click "Add" when prompted
4. ✅ Done!
**Note:** Unsigned extensions will show a warning. For production use, sign it through addons.mozilla.org.
### Method 3: Permanent Install (Signed)
For permanent installation without warnings:
1. Create account at [addons.mozilla.org](https://addons.mozilla.org/developers/)
2. Submit the ZIP file
3. Choose "Unlisted" for self-distribution
4. Mozilla automatically signs it
5. Download the signed XPI
6. Install the signed version
---
## Edge Installation
Edge uses Chrome extensions:
1. Open `edge://extensions/`
2. Enable "Developer mode"
3. Click "Load unpacked"
4. Select the `chrome` folder
5. ✅ Done!
---
## Opera Installation
Opera also uses Chrome extensions:
1. Open `opera://extensions/`
2. Enable "Developer mode"
3. Click "Load unpacked"
4. Select the `chrome` folder
5. ✅ Done!
---
## Verifying Installation
After installing, you should see:
- ✅ GreasyBoii icon in the browser toolbar
- ✅ Extension name: "GreasyBoii"
- ✅ Version: 1.0.4
- ✅ Author: Andy Dixon
Click the icon to open the popup and test:
1. Click "+ Add Rule"
2. Modal should open
3. Create a test rule
4. Visit a matching page
5. Script should execute!
---
## Testing the Package
Before sharing, test the packaged version:
1. **Uninstall the development version** (if loaded)
2. **Install from the package** (follow steps above)
3. **Test all features:**
- ✅ Add rule
- ✅ Edit rule
- ✅ Delete rule
- ✅ Toggle rule
- ✅ Visit target page
- ✅ Verify script executes
- ✅ Check console logs
---
## Sharing Your Extension
### For Chrome Users:
Send them:
1. The `.crx` file (after packing)
2. Installation instructions:
- Open `chrome://extensions/`
- Enable "Developer mode"
- Drag the `.crx` file into the window
- Click "Add extension"
### For Firefox Users:
Send them:
1. The `.xpi` file (renamed `.zip`)
2. Installation instructions:
- Drag the `.xpi` file into Firefox
- Click "Add"
### For All Users (Best):
1. Publish on Chrome Web Store ($5 one-time fee)
2. Publish on Firefox Add-ons (free)
3. Users can install with one click
---
## Updating the Extension
When you release a new version:
### Chrome:
1. Update version in `chrome/manifest.json`
2. Re-package the extension
3. If you packed to `.crx`, use the SAME `.pem` file
4. Users get automatic updates if installed from Chrome Web Store
### Firefox:
1. Update version in `firefox/manifest.json`
2. Re-package the extension
3. Re-sign with Mozilla (if signed)
4. Users get automatic updates if installed from Firefox Add-ons
---
## Troubleshooting
### Chrome: "CRX_REQUIRED_PROOF_MISSING"
- **Issue:** Chrome blocks unsigned extensions by default
- **Solution:** Users must enable "Developer mode" to install
- **Better:** Publish on Chrome Web Store
### Firefox: "Add-on appears corrupt"
- **Issue:** ZIP structure is incorrect
- **Solution:** Files must be at ZIP root, not in a subfolder
- **Fix:** Re-create ZIP from inside the firefox folder
### Extension doesn't work after packaging
- **Check:** All files are included
- **Check:** manifest.json is valid (use JSON validator)
- **Check:** Version number is correct
- **Test:** Install on a fresh browser profile
### Chrome: Updates not working
- **Issue:** Different `.pem` file used
- **Solution:** Always use the same `.pem` file for updates
- **Lost .pem?** You'll need to create a new extension (new ID)
---
## File Sizes
Your packaged extensions are:
- **Chrome:** 62 KB (zipped)
- **Firefox:** 62 KB (zipped)
Both include:
- Extension code
- Bootstrap 5.3.2 (CSS + JS)
- Icons
- Manifest files
---
## Publishing to Official Stores
### Chrome Web Store:
1. Cost: $5 one-time developer fee
2. URL: https://chrome.google.com/webstore/developer/dashboard
3. Upload: The ZIP file (not CRX)
4. Review: Usually 1-3 days
5. Benefits: Automatic updates, no warnings, discoverable
### Firefox Add-ons:
1. Cost: Free
2. URL: https://addons.mozilla.org/developers/
3. Upload: The ZIP file
4. Options:
- **Listed:** Public on addons.mozilla.org (reviewed)
- **Unlisted:** Self-distribute signed version (instant)
5. Benefits: Automatic signing, automatic updates
---
## Quick Commands
```bash
# Package both browsers
./package-all.sh
# Package Chrome only
cd chrome && zip -r ../dist/greasyboii-chrome.zip *
# Package Firefox only
cd firefox && zip -r ../dist/greasyboii-firefox.zip *
# Rename Firefox ZIP to XPI
mv dist/greasyboii-firefox-v1.0.4.zip dist/greasyboii-firefox-v1.0.4.xpi
# Check package contents
unzip -l dist/greasyboii-chrome-v1.0.4.zip
```
---
## Distribution Checklist
Before distributing:
- ✅ Test packaged version thoroughly
- ✅ Verify all features work
- ✅ Check version numbers are correct
- ✅ Include installation instructions
- ✅ Mention browser compatibility
- ✅ Provide support contact
- ✅ Consider publishing to official stores
---
**Current Version:** 1.0.4
**Package Size:** 62 KB
**Ready to Distribute:** ✅ Yes
Enjoy sharing GreasyBoii! 🐒✨
+94
View File
@@ -0,0 +1,94 @@
Greasemonkey Browser Extension
Author: Andy Dixon
Version: 1.0.0
=== FILE STRUCTURE ===
greasemonkey/
├── chrome/ Chrome Extension (Manifest V3)
│ ├── manifest.json Extension configuration
│ ├── background.js Background service worker
│ ├── content.js Content script (runs on pages)
│ ├── popup.html Extension popup UI
│ ├── popup.css Popup styling (Bootstrap Dark)
│ ├── popup.js Popup functionality
│ ├── icon16.png 16x16 icon
│ ├── icon48.png 48x48 icon
│ └── icon128.png 128x128 icon
│
├── firefox/ Firefox Extension (Manifest V2)
│ ├── manifest.json Extension configuration
│ ├── background.js Background script
│ ├── content.js Content script (runs on pages)
│ ├── popup.html Extension popup UI
│ ├── popup.css Popup styling (Bootstrap Dark)
│ ├── popup.js Popup functionality
│ ├── icon16.png 16x16 icon
│ ├── icon48.png 48x48 icon
│ └── icon128.png 128x128 icon
│
├── README.md Full documentation
├── BUILD.md Build and distribution instructions
├── QUICKSTART.md Quick start guide
├── test-page.html Test page for trying rules
└── MANIFEST.txt This file
=== FEATURES ===
✓ Execute custom JavaScript on any webpage
✓ Inject custom CSS styles on any webpage
✓ Three matching modes:
- URL contains string
- Page contains element (CSS selector)
- Both URL and element match
✓ Bootstrap 5 Dark theme UI
✓ Add, edit, delete, and toggle rules
✓ Persistent storage (synced across browser)
✓ Multiple rules support
✓ Visual rule indicators (type badges, JS/CSS badges)
✓ Chrome and Firefox compatible
=== QUICK INSTALL ===
Chrome:
1. chrome://extensions/
2. Enable "Developer mode"
3. Click "Load unpacked"
4. Select the chrome/ folder
Firefox:
1. about:debugging#/runtime/this-firefox
2. Click "Load Temporary Add-on"
3. Select firefox/manifest.json
=== USAGE ===
1. Click Greasemonkey icon in toolbar
2. Click "+ Add Rule"
3. Configure:
- Name your rule
- Choose match type
- Enter URL pattern and/or element selector
- Add JavaScript and/or CSS code
4. Save and reload target page
=== DOCUMENTATION ===
- README.md - Complete feature documentation and examples
- BUILD.md - Building and distribution instructions
- QUICKSTART.md - 5-minute getting started guide
- test-page.html - Interactive test page
=== NEXT STEPS ===
1. Read QUICKSTART.md to get started
2. Try the test-page.html for examples
3. Create your first rule
4. Read README.md for advanced features
5. See BUILD.md for distribution
=== SUPPORT ===
For questions or issues, contact Andy Dixon.
Enjoy! 🐒✨
+368
View File
@@ -0,0 +1,368 @@
# How to Package GreasyBoii Extension
This guide shows you how to package GreasyBoii for distribution.
## Quick Start
### Chrome/Edge (Easy Method)
1. Open `chrome://extensions/`
2. Enable "Developer mode" (top-right toggle)
3. Click "Pack extension"
4. For "Extension root directory" → Select the `chrome` folder
5. Leave "Private key file" empty (first time)
6. Click "Pack Extension"
7. ✅ Done! You get 2 files:
- `chrome.crx` - The packaged extension
- `chrome.pem` - Private key (KEEP THIS SECURE!)
### Firefox (Command Line)
```bash
cd firefox
zip -r ../greasyboii-firefox-v1.0.4.zip *
```
Done! You get `greasyboii-firefox-v1.0.4.zip`
---
## Detailed Instructions
## Chrome Extension Packaging
### Method 1: Chrome Built-in Packer (Recommended)
**Step 1: Open Extensions Page**
```
chrome://extensions/
```
**Step 2: Enable Developer Mode**
- Toggle the "Developer mode" switch in the top-right corner
**Step 3: Pack Extension**
1. Click "Pack extension" button
2. In the dialog:
- **Extension root directory**: Browse and select `/path/to/greasemonkey/chrome`
- **Private key file**: Leave empty (first time) or select your `.pem` file
3. Click "Pack Extension"
**Step 4: Files Created**
You'll get two files in the parent directory:
- `chrome.crx` - The packed extension (ready to distribute)
- `chrome.pem` - Your private key (KEEP THIS SECRET!)
**Important Notes:**
- ⚠️ **Keep the `.pem` file secure** - You need it to update your extension
- ⚠️ **Never share your `.pem` file** - Anyone with it can impersonate your extension
- ✅ The `.crx` file is what you distribute to users
### Method 2: Command Line (Using Chrome)
```bash
# Navigate to Chrome binary location
# macOS
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --pack-extension=/path/to/greasemonkey/chrome
# Windows
"C:\Program Files\Google\Chrome\Application\chrome.exe" --pack-extension=C:\path\to\greasemonkey\chrome
# Linux
google-chrome --pack-extension=/path/to/greasemonkey/chrome
```
### Method 3: Create ZIP for Chrome Web Store
If you want to publish on Chrome Web Store:
```bash
cd chrome
zip -r ../greasyboii-chrome-v1.0.4.zip *
```
Then upload the ZIP to [Chrome Web Store Developer Dashboard](https://chrome.google.com/webstore/developer/dashboard)
---
## Firefox Extension Packaging
### Method 1: Simple ZIP (Recommended for Testing)
**Create ZIP:**
```bash
cd firefox
zip -r ../greasyboii-firefox-v1.0.4.zip *
```
**Rename to XPI (optional):**
```bash
mv greasyboii-firefox-v1.0.4.zip greasyboii-firefox-v1.0.4.xpi
```
**Install:**
- Drag the `.xpi` file into Firefox
- Or: `about:addons` → gear icon → "Install Add-on From File"
**Note:** Unsigned extensions show a warning. For permanent use, you need to sign it.
### Method 2: Using web-ext (Recommended for Distribution)
**Install web-ext:**
```bash
npm install -g web-ext
```
**Build:**
```bash
cd firefox
web-ext build
```
This creates a signed ZIP in `web-ext-artifacts/` folder.
**Sign (requires Mozilla account):**
```bash
# Get API keys from https://addons.mozilla.org/developers/addon/api/key/
web-ext sign \
--api-key=YOUR_API_KEY \
--api-secret=YOUR_API_SECRET
```
**Run for testing:**
```bash
web-ext run
```
This opens Firefox with your extension loaded.
---
## Distribution Options
### Option 1: Direct Distribution (Self-Hosted)
**Chrome (.crx file):**
1. Pack the extension (see above)
2. Host the `.crx` file on your website
3. Users download and drag it to `chrome://extensions/`
4. ⚠️ Chrome will show "unverified" warning
**Firefox (.xpi file):**
1. Create and sign the extension
2. Host the `.xpi` file on your website
3. Users click to install
4. ⚠️ Unsigned extensions need special Firefox config
### Option 2: Official Store Distribution
**Chrome Web Store:**
1. Create developer account ($5 one-time fee)
2. Go to [Chrome Web Store Developer Dashboard](https://chrome.google.com/webstore/developer/dashboard)
3. Click "New Item"
4. Upload your ZIP file
5. Fill in listing details
6. Submit for review
7. ✅ Published after approval (usually 1-3 days)
**Firefox Add-ons:**
1. Create account at [addons.mozilla.org](https://addons.mozilla.org/developers/)
2. Submit new add-on
3. Upload your ZIP/XPI
4. Choose:
- **Listed**: Public on addons.mozilla.org (requires review)
- **Unlisted**: Self-distribute signed version (automatic)
5. ✅ Get signed version immediately (unlisted) or after review (listed)
---
## Automation Scripts
I've created helper scripts for you:
### package-chrome.sh
```bash
#!/bin/bash
cd chrome
zip -r ../dist/greasyboii-chrome-v1.0.4.zip * -x "*.DS_Store"
echo "✅ Chrome package created: dist/greasyboii-chrome-v1.0.4.zip"
```
### package-firefox.sh
```bash
#!/bin/bash
cd firefox
zip -r ../dist/greasyboii-firefox-v1.0.4.zip * -x "*.DS_Store"
echo "✅ Firefox package created: dist/greasyboii-firefox-v1.0.4.zip"
```
### package-all.sh
```bash
#!/bin/bash
mkdir -p dist
echo "📦 Packaging Chrome extension..."
cd chrome
zip -r ../dist/greasyboii-chrome-v1.0.4.zip * -x "*.DS_Store"
cd ..
echo "📦 Packaging Firefox extension..."
cd firefox
zip -r ../dist/greasyboii-firefox-v1.0.4.zip * -x "*.DS_Store"
cd ..
echo "✅ All packages created in dist/ folder!"
ls -lh dist/
```
---
## File Checklist
Before packaging, ensure these files exist:
### Chrome folder must have:
- ✅ manifest.json
- ✅ background.js
- ✅ content.js
- ✅ popup.html
- ✅ popup.css
- ✅ popup.js
- ✅ bootstrap.min.css
- ✅ bootstrap.bundle.min.js
- ✅ icon16.png
- ✅ icon48.png
- ✅ icon128.png
### Firefox folder must have:
- ✅ manifest.json
- ✅ background.js
- ✅ content.js
- ✅ popup.html
- ✅ popup.css
- ✅ popup.js
- ✅ bootstrap.min.css
- ✅ bootstrap.bundle.min.js
- ✅ icon16.png
- ✅ icon48.png
- ✅ icon128.png
---
## Testing Packaged Extension
### Chrome:
1. Go to `chrome://extensions/`
2. Drag the `.crx` file into the window
3. Click "Add extension"
4. Test all features
### Firefox:
1. Go to `about:addons`
2. Click gear icon → "Install Add-on From File"
3. Select your `.xpi` or `.zip` file
4. Click "Add"
5. Test all features
---
## Version Updates
When releasing a new version:
1. **Update version in manifests:**
```json
"version": "1.0.5"
```
2. **For Chrome:**
- Use the SAME `.pem` file when packing
- This ensures it's recognized as an update
3. **For Firefox:**
- Increment version number
- Re-sign with same API keys
- Upload to addons.mozilla.org
4. **Update documentation:**
- README.md
- CHANGELOG.md
- Any version references
---
## Common Issues
### Chrome: "Package is invalid: CRX_REQUIRED_PROOF_MISSING"
- **Solution**: Users must enable "Developer mode" to install unsigned extensions
- **Better solution**: Publish on Chrome Web Store
### Firefox: "This add-on could not be installed because it appears to be corrupt"
- **Solution**: Check ZIP file structure - files should be at root, not in a subfolder
- **Fix**: Re-create ZIP from inside the firefox folder
### Chrome: "Extension update failed"
- **Solution**: Use the same `.pem` file for updates
- **Lost .pem?**: You'll need to create a new extension (new ID)
### Firefox: "This add-on is not signed"
- **Solution**: Sign it with Mozilla or enable unsigned extensions
- **Enable unsigned**: `about:config` → `xpinstall.signatures.required` → false (Firefox Developer Edition only)
---
## Security Notes
- ⚠️ **Never commit `.pem` files to version control**
- ⚠️ **Keep your API keys secret**
- ⚠️ **Don't bundle unnecessary files** (documentation, source maps, etc.)
- ✅ **Only include required files** in the package
- ✅ **Test packaged version** before distributing
---
## Quick Commands Reference
```bash
# Create distribution folder
mkdir -p dist
# Package Chrome (ZIP for Web Store)
cd chrome && zip -r ../dist/greasyboii-chrome.zip * && cd ..
# Package Firefox (ZIP)
cd firefox && zip -r ../dist/greasyboii-firefox.zip * && cd ..
# Package Firefox (XPI)
cd firefox && zip -r ../dist/greasyboii-firefox.xpi * && cd ..
# Install web-ext (Firefox tool)
npm install -g web-ext
# Build with web-ext
cd firefox && web-ext build
# Run Firefox with extension
cd firefox && web-ext run
# Sign Firefox extension
cd firefox && web-ext sign --api-key=KEY --api-secret=SECRET
```
---
## Next Steps
1. **Test locally first** - Make sure everything works
2. **Create packages** - Use scripts above
3. **Test packages** - Install and verify
4. **Decide distribution method**:
- Self-host for personal use
- Chrome Web Store for public Chrome users
- Firefox Add-ons for public Firefox users
5. **Update version numbers** for future releases
---
**Current Version:** 1.0.4
**Ready to Package:** ✅ Yes
**All Files Present:** ✅ Yes
Happy packaging! 🐒✨
+151
View File
@@ -0,0 +1,151 @@
# Greasemonkey Quick Start Guide
Get up and running with Greasemonkey in 5 minutes!
## Installation
### Chrome
1. Open Chrome
2. Go to `chrome://extensions/`
3. Enable "Developer mode" (top-right toggle)
4. Click "Load unpacked"
5. Select the `chrome` folder
6. Done! 🎉
### Firefox
1. Open Firefox
2. Go to `about:debugging#/runtime/this-firefox`
3. Click "Load Temporary Add-on"
4. Select `firefox/manifest.json`
5. Done! 🎉
## Your First Rule
Let's create a simple rule that changes Google's background color:
1. **Click the Greasemonkey icon** in your browser toolbar
2. **Click "+ Add Rule"**
3. **Fill in the form:**
- Rule Name: `Google Dark Background`
- Match Type: `URL contains string`
- URL Pattern: `google.com`
- CSS Code:
```css
body {
background-color: #1a1a1a !important;
color: #e0e0e0 !important;
}
```
- Leave JavaScript blank
- Keep Enabled checked
4. **Click "Save Rule"**
5. **Visit google.com** and see the dark background!
## Testing with Test Page
A test page is included: `test-page.html`
1. Open `test-page.html` in your browser
2. Create a rule:
- Rule Name: `Test Page Alert`
- Match Type: `Page contains element`
- Element Selector: `#test-element-1`
- JavaScript Code:
```javascript
alert('Greasemonkey is working!');
```
3. Reload the test page
4. You should see an alert!
## Common Use Cases
### Hide Annoying Elements
```css
.advertisement,
.popup-banner,
#cookie-notice {
display: none !important;
}
```
### Change Colors
```css
body {
background-color: #1a1a1a !important;
color: #ffffff !important;
}
```
### Auto-Click Buttons
```javascript
const button = document.querySelector('.accept-button');
if (button) button.click();
```
### Add Custom Content
```javascript
const banner = document.createElement('div');
banner.textContent = 'Custom banner added by Greasemonkey!';
banner.style.cssText = 'position:fixed;top:0;width:100%;background:red;color:white;padding:10px;z-index:9999;';
document.body.insertBefore(banner, document.body.firstChild);
```
## Tips
- **Use `!important`** in CSS to override existing styles
- **Check the console** (F12) for error messages
- **Start simple** and test before adding complex rules
- **Toggle rules off** if they cause problems
- **Element selectors** can be found by right-clicking → Inspect Element
## Troubleshooting
**Rule not working?**
- ✓ Is the rule enabled? (check the toggle)
- ✓ Does the URL pattern match? (try a shorter string)
- ✓ Does the element exist? (test in console: `document.querySelector('selector')`)
- ✓ Did you reload the page?
**CSS not applying?**
- Add `!important` to CSS rules
- Check for syntax errors
**JavaScript not running?**
- Check browser console (F12) for errors
- Test code in console first
## Next Steps
- Read the full [README.md](README.md) for detailed documentation
- See [BUILD.md](BUILD.md) for distribution instructions
- Experiment with different selectors and code
- Share your rules with others!
## Example: Complete Rule
Here's a complete rule that works on GitHub to highlight pull requests:
- **Rule Name:** `GitHub PR Highlight`
- **Match Type:** `Both URL and element`
- **URL Pattern:** `github.com`
- **Element Selector:** `.js-issue-row`
- **CSS Code:**
```css
.js-issue-row {
border-left: 4px solid #28a745 !important;
background-color: #f0fff4 !important;
}
```
- **JavaScript Code:**
```javascript
console.log('GitHub PR highlighting active');
```
## Help
Need help? Check:
- Browser console (F12) for errors
- README.md for full documentation
- Element inspector to find correct selectors
Happy scripting! 🐒✨
+100
View File
@@ -0,0 +1,100 @@
═══════════════════════════════════════════════════════════════════════
GREASYBOII PACKAGING GUIDE
Quick Reference
═══════════════════════════════════════════════════════════════════════
✅ ALREADY PACKAGED!
Files ready in dist/ folder:
• greasyboii-chrome-v1.0.4.zip (62 KB)
• greasyboii-firefox-v1.0.4.zip (62 KB)
═══════════════════════════════════════════════════════════════════════
🚀 QUICK INSTALL
Chrome:
1. Extract ZIP
2. chrome://extensions/
3. Enable "Developer mode"
4. "Load unpacked" → select folder
5. Done!
Firefox:
1. Rename .zip to .xpi
2. Drag .xpi into Firefox
3. Click "Add"
4. Done!
═══════════════════════════════════════════════════════════════════════
📦 RE-PACKAGE (if needed)
Run the script:
./package-all.sh
Or manually:
cd chrome && zip -r ../dist/greasyboii-chrome.zip *
cd firefox && zip -r ../dist/greasyboii-firefox.zip *
═══════════════════════════════════════════════════════════════════════
🎁 CREATE .CRX FOR CHROME (Shareable)
1. chrome://extensions/
2. Developer mode ON
3. "Pack extension"
4. Select chrome/ folder
5. Leave private key empty
6. Get chrome.crx file
7. Share this file!
Users install: Drag chrome.crx to chrome://extensions/
═══════════════════════════════════════════════════════════════════════
🦊 CREATE .XPI FOR FIREFOX (Shareable)
Simply rename:
mv dist/greasyboii-firefox-v1.0.4.zip dist/greasyboii-firefox-v1.0.4.xpi
Share the .xpi file!
Users install: Drag .xpi into Firefox
═══════════════════════════════════════════════════════════════════════
📚 DOCUMENTATION
• PACKAGING.md - Full packaging guide
• INSTALL_PACKAGED.md - Installation instructions
• BUILD.md - Build instructions
• README.md - Complete documentation
═══════════════════════════════════════════════════════════════════════
🏪 PUBLISH TO STORES
Chrome Web Store:
• $5 one-time fee
• https://chrome.google.com/webstore/developer/dashboard
• Upload ZIP file
• Review takes 1-3 days
Firefox Add-ons:
• Free
• https://addons.mozilla.org/developers/
• Upload ZIP file
• Choose Listed or Unlisted
• Instant signing for unlisted
═══════════════════════════════════════════════════════════════════════
✅ YOUR PACKAGES ARE READY!
Files: dist/greasyboii-chrome-v1.0.4.zip
dist/greasyboii-firefox-v1.0.4.zip
Ready to: Install, Share, or Publish!
═══════════════════════════════════════════════════════════════════════
+314
View File
@@ -0,0 +1,314 @@
# Greasemonkey Browser Extension
**Author:** Andy Dixon
**Version:** 1.0.0
A powerful browser extension that allows you to execute custom JavaScript and CSS on web pages based on flexible matching conditions.
## Features
- 🎯 **Flexible Matching**: Execute scripts when:
- A URL contains a specific string
- A page contains a specific element (CSS selector)
- Both conditions are met simultaneously
- 🎨 **Bootstrap Dark UI**: Intuitive configuration interface with Bootstrap 5 dark theme
- 💾 **Persistent Storage**: All rules are saved and synced across your browser sessions
- ⚡ **Multiple Rules**: Add, edit, and manage unlimited custom rules
- 🔄 **Toggle Controls**: Easily enable/disable individual rules
- 📝 **JavaScript & CSS**: Support for both JavaScript execution and CSS injection
- 🔍 **Visual Indicators**: Clear badges showing rule type (URL/Element/Both) and code types (JS/CSS)
## Installation
### Chrome Installation
1. Navigate to the `chrome` directory
2. Open Chrome and go to `chrome://extensions/`
3. Enable "Developer mode" (toggle in top-right corner)
4. Click "Load unpacked"
5. Select the `chrome` directory
6. The Greasemonkey extension is now installed!
### Firefox Installation
1. Navigate to the `firefox` directory
2. Open Firefox and go to `about:debugging#/runtime/this-firefox`
3. Click "Load Temporary Add-on"
4. Select the `manifest.json` file from the `firefox` directory
5. The Greasemonkey extension is now installed!
**Note:** For permanent Firefox installation, you'll need to sign the extension through [addons.mozilla.org](https://addons.mozilla.org).
## Usage
### Creating a Rule
1. Click the Greasemonkey icon in your browser toolbar
2. Click the "+ Add Rule" button
3. Fill in the rule details:
- **Rule Name**: A descriptive name for your rule
- **Match Type**: Choose how the rule should be triggered
- *URL contains string*: Matches if URL contains the pattern
- *Page contains element*: Matches if element selector exists
- *Both URL and element*: Requires both conditions
- **URL Pattern**: The string to match in the URL (e.g., `github.com`, `/search?`)
- **Element Selector**: CSS selector to match (e.g., `#my-element`, `.my-class`, `div[data-id="123"]`)
- **JavaScript Code**: Custom JavaScript to execute
- **CSS Code**: Custom CSS to inject
- **Enabled**: Toggle to enable/disable the rule
4. Click "Save Rule"
### Editing a Rule
1. Open the Greasemonkey popup
2. Click the edit button (pencil icon) on any rule card
3. Modify the rule details
4. Click "Save Rule"
### Deleting a Rule
1. Open the Greasemonkey popup
2. Click the delete button (trash icon) on any rule card
3. Confirm the deletion
### Toggling a Rule
Use the toggle switch on each rule card to quickly enable or disable rules without editing them.
## Examples
### Example 1: Hide Ads on Specific Sites
**Match Type:** URL contains string
**URL Pattern:** `example.com`
**CSS Code:**
```css
.advertisement,
.ad-banner,
#sidebar-ads {
display: none !important;
}
```
### Example 2: Auto-fill Forms
**Match Type:** Both URL and element
**URL Pattern:** `form-site.com`
**Element Selector:** `#login-form`
**JavaScript Code:**
```javascript
document.getElementById('username').value = 'myusername';
console.log('Auto-filled username field');
```
### Example 3: Dark Mode Override
**Match Type:** URL contains string
**URL Pattern:** `news-site.com`
**CSS Code:**
```css
body {
background-color: #1a1a1a !important;
color: #e0e0e0 !important;
}
a {
color: #4a9eff !important;
}
```
### Example 4: Add Custom Features
**Match Type:** Element exists
**Element Selector:** `.video-player`
**JavaScript Code:**
```javascript
console.log('Video player detected!');
// Add a custom button
const button = document.createElement('button');
button.textContent = 'Custom Action';
button.style.cssText = 'position:fixed;top:10px;right:10px;z-index:9999;';
button.onclick = () => alert('Custom button clicked!');
document.body.appendChild(button);
```
### Example 5: Conditional Styling
**Match Type:** Both URL and element
**URL Pattern:** `dashboard`
**Element Selector:** `.data-table`
**CSS Code:**
```css
.data-table {
border: 2px solid #4CAF50;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.data-table th {
background-color: #4CAF50;
color: white;
}
```
## Technical Details
### File Structure
```
greasemonkey/
├── chrome/ # Chrome extension files
│ ├── manifest.json # Chrome extension manifest (v3)
│ ├── background.js # Background service worker
│ ├── content.js # Content script (runs on pages)
│ ├── popup.html # Extension popup UI
│ ├── popup.css # Popup styling
│ ├── popup.js # Popup functionality
│ └── icon*.png # Extension icons
├── firefox/ # Firefox extension files
│ ├── manifest.json # Firefox extension manifest (v2)
│ ├── background.js # Background script
│ ├── content.js # Content script (runs on pages)
│ ├── popup.html # Extension popup UI
│ ├── popup.css # Popup styling
│ ├── popup.js # Popup functionality
│ └── icon*.png # Extension icons
└── README.md # This file
```
### How It Works
1. **Content Script**: Runs on every page load (`document_idle`)
2. **Rule Matching**: Checks all enabled rules against current page
3. **Condition Evaluation**: Tests URL patterns and element selectors
4. **Code Execution**:
- JavaScript is injected as a script element
- CSS is injected as a style element
5. **Storage**: Rules are stored in browser's sync storage
### Storage Format
Rules are stored as JSON in browser sync storage:
```javascript
{
"rules": [
{
"name": "Example Rule",
"matchType": "url",
"urlPattern": "example.com",
"elementSelector": "",
"javascript": "console.log('Hello');",
"css": "body { background: red; }",
"enabled": true
}
]
}
```
### Browser Compatibility
- **Chrome**: Requires Chrome 88+ (Manifest V3)
- **Firefox**: Requires Firefox 48+ (Manifest V2)
- **Edge**: Should work (Chromium-based, same as Chrome)
- **Opera**: Should work (Chromium-based, same as Chrome)
## Building for Distribution
### Chrome
1. Ensure all files are in the `chrome` directory
2. Go to `chrome://extensions/`
3. Click "Pack extension"
4. Select the `chrome` directory
5. This creates a `.crx` file for distribution
### Firefox
1. Install `web-ext` tool: `npm install -g web-ext`
2. Navigate to the `firefox` directory
3. Run: `web-ext build`
4. This creates a `.zip` file in `web-ext-artifacts/`
5. Submit to [addons.mozilla.org](https://addons.mozilla.org) for signing
## Security Considerations
- **User Responsibility**: Be cautious with JavaScript code execution
- **HTTPS**: Script injection works on both HTTP and HTTPS sites
- **CSP**: Some sites with strict Content Security Policy may block injected scripts
- **Permissions**: Extension requires broad permissions (`<all_urls>`) to work on all sites
## Troubleshooting
### Rules Not Executing
1. **Check the browser console** for error messages (F12)
2. **Verify the rule is enabled** (toggle switch)
3. **Test the URL pattern** - ensure it matches the current page
4. **Test the element selector** - open console and run `document.querySelector('your-selector')`
5. **Reload the page** after creating/editing rules
### CSS Not Applying
- Use `!important` flag to override existing styles
- Check for syntax errors in CSS
- Verify the rule conditions are met
### JavaScript Not Running
- Check browser console for errors
- Ensure JavaScript syntax is correct
- Some sites may have strict Content Security Policies
### Storage Not Syncing
- Ensure browser sync is enabled
- Check available storage quota
- For Firefox temporary add-ons, sync storage may not persist
## Development
### Local Development
1. Make changes to files in `chrome/` or `firefox/` directories
2. Reload the extension:
- **Chrome**: Go to `chrome://extensions/` and click reload icon
- **Firefox**: Go to `about:debugging` and click reload
3. Test your changes
### Debugging
- **Background Script**: Check browser console in extension management page
- **Content Script**: Check page console (F12)
- **Popup**: Right-click popup and select "Inspect"
## Privacy
- No data is sent to external servers
- All rules are stored locally in browser storage
- No analytics or tracking
- No network requests made by the extension
## License
This extension is provided as-is for personal and educational use.
## Support
For issues, questions, or feature requests, please contact Andy Dixon.
## Version History
### 1.0.0 (Initial Release)
- URL pattern matching
- Element selector matching
- Combined URL + Element matching
- JavaScript execution
- CSS injection
- Bootstrap dark theme UI
- Add/Edit/Delete rules
- Enable/Disable toggle
- Persistent storage
- Chrome and Firefox support
+213
View File
@@ -0,0 +1,213 @@
# 🐒 Greasemonkey - START HERE
**Welcome to Greasemonkey!** This browser extension lets you execute custom JavaScript and CSS on any webpage.
---
## 🚀 Quick Start (Choose Your Path)
### Path 1: I Want To Use It NOW (5 minutes)
→ **Read [QUICKSTART.md](QUICKSTART.md)** - Get up and running immediately
### Path 2: I Want Example Rules
→ **Read [EXAMPLES.md](EXAMPLES.md)** - 20+ ready-to-use rules to copy/paste
### Path 3: I Want Complete Documentation
→ **Read [README.md](README.md)** - Full feature documentation and usage guide
### Path 4: I Want To Test It First
→ **Open [test-page.html](test-page.html)** in your browser - Interactive test environment
### Path 5: I Want To Distribute It
→ **Read [BUILD.md](BUILD.md)** - Building and distribution instructions
---
## 📦 Installation (Pick Your Browser)
### Google Chrome / Microsoft Edge
```
1. Open chrome://extensions/
2. Enable "Developer mode" (top-right toggle)
3. Click "Load unpacked"
4. Select the chrome/ folder
```
### Mozilla Firefox
```
1. Open about:debugging#/runtime/this-firefox
2. Click "Load Temporary Add-on"
3. Select firefox/manifest.json
```
**That's it!** The Greasemonkey icon appears in your toolbar.
---
## 📚 Documentation Map
| File | What It Contains | When To Read |
|------|------------------|--------------|
| **QUICKSTART.md** | 5-minute tutorial | First time setup |
| **EXAMPLES.md** | 20+ example rules | When you need ideas |
| **README.md** | Complete documentation | Learning all features |
| **BUILD.md** | Build instructions | Distributing the extension |
| **test-page.html** | Test environment | Testing your rules |
| **PROJECT_SUMMARY.md** | Project overview | Understanding the codebase |
---
## 🎯 What Can I Do With This?
### Popular Use Cases
- 🌙 **Add Dark Mode** to any website
- 🚫 **Block Ads** and unwanted content
- ⚡ **Automate Tasks** like form filling
- 🎨 **Customize Design** of your favorite sites
- 🔧 **Add Features** that websites are missing
- 🔐 **Enhance Privacy** by removing trackers
- ♿ **Improve Accessibility** with custom styles
- 📝 **Inject Tools** like notepads or calculators
### Example: Make Any Site Dark
1. Click Greasemonkey icon
2. Add rule: URL contains `example.com`
3. Add CSS: `body { background: #1a1a1a !important; color: #e0e0e0 !important; }`
4. Visit example.com → Dark theme!
---
## 🎓 Learning Path
**Total Time: ~30 minutes**
1. **Install** (5 min) → Follow installation steps above
2. **Quick Start** (5 min) → Read QUICKSTART.md
3. **First Rule** (5 min) → Create your first rule
4. **Test** (5 min) → Open test-page.html and experiment
5. **Examples** (10 min) → Browse EXAMPLES.md for ideas
6. **Customize** (∞) → Start customizing your favorite sites!
---
## 📋 Features Checklist
✅ URL pattern matching
✅ CSS selector matching
✅ Combined URL + element matching
✅ JavaScript execution
✅ CSS injection
✅ Bootstrap dark theme UI
✅ Add/edit/delete rules
✅ Enable/disable toggles
✅ Persistent storage
✅ Chrome support (Manifest V3)
✅ Firefox support (Manifest V2)
✅ Complete documentation
✅ Example library
✅ Test page
---
## 🔧 Technical Details
**Architecture:**
- Content scripts inject at page load
- Bootstrap 5 dark theme UI
- Sync storage for persistence
- No external dependencies
**Browser Support:**
- Chrome 88+ (Manifest V3)
- Firefox 48+ (Manifest V2)
- Edge, Opera (Chromium-based)
**File Count:**
- 9 files per browser
- 7 documentation files
- 1 test page
- Total: 26 files
---
## 🆘 Common Questions
**Q: Is it safe?**
A: Yes! No external servers, no tracking. All code runs locally. Be cautious with JavaScript you execute.
**Q: Will it slow down my browser?**
A: No. Scripts only run when conditions match, and inject efficiently at page load.
**Q: Can I share my rules?**
A: Yes! Copy the rule configuration and share. Rules are just text in your browser storage.
**Q: Do I need programming knowledge?**
A: Basic knowledge helps, but you can start with examples from EXAMPLES.md and modify them.
**Q: Can I break websites?**
A: Unlikely to cause permanent issues. Just disable the rule if something goes wrong. Refresh to reset.
**Q: Does it work on all websites?**
A: Yes, except some with strict Content Security Policy. Works on 99% of sites.
---
## 🐛 Troubleshooting
**Rule not working?**
1. Check rule is enabled (toggle switch)
2. Verify URL pattern matches current page
3. Test element selector in console: `document.querySelector('selector')`
4. Check browser console (F12) for errors
5. Reload the page after creating/editing rules
**More help:** See troubleshooting section in [README.md](README.md)
---
## 📞 Support
- **Questions?** Check README.md troubleshooting section
- **Bugs?** Check browser console for errors
- **Ideas?** Browse EXAMPLES.md for inspiration
- **Contact:** Andy Dixon (extension author)
---
## 🎉 Ready To Start!
**Recommended first steps:**
1. ✅ Install extension in your browser (see above)
2. ✅ Read [QUICKSTART.md](QUICKSTART.md) (5 minutes)
3. ✅ Open [test-page.html](test-page.html) in browser
4. ✅ Create your first rule
5. ✅ Browse [EXAMPLES.md](EXAMPLES.md) for more ideas
---
## 📁 File Reference
```
greasemonkey/
├── START_HERE.md ← You are here!
├── QUICKSTART.md ← Start here for installation
├── EXAMPLES.md ← 20+ ready-to-use rules
├── README.md ← Complete documentation
├── BUILD.md ← Distribution guide
├── test-page.html ← Test environment
├── chrome/ ← Chrome extension files
└── firefox/ ← Firefox extension files
```
---
**Author:** Andy Dixon
**Version:** 1.0.0
**License:** Personal and educational use
---
### 🚀 Let's Go!
Pick your path above and start customizing the web! 🐒✨
+231
View File
@@ -0,0 +1,231 @@
# Greasemonkey Troubleshooting Guide
## "Add Rule" Button Not Working
### Quick Fix
1. **Reload the extension**:
- Chrome: Go to `chrome://extensions/` and click the reload icon on Greasemonkey
- Firefox: Go to `about:debugging` and click reload
2. **Close and reopen the popup** by clicking the extension icon again
3. **Check the browser console**:
- Right-click on the popup window
- Select "Inspect" or "Inspect Element"
- Look at the Console tab for any error messages
### Common Causes
#### 1. Bootstrap Not Loaded
**Symptom:** Modal doesn't open, or console shows "bootstrap is not defined"
**Solution:**
- Ensure you have an internet connection (Bootstrap loads from CDN)
- If offline, the extension won't work properly without Bootstrap
- Check browser console for network errors
#### 2. Content Security Policy
**Symptom:** Console shows CSP violations
**Solution:** Extension should work in popup, but if issues persist:
- Try reloading the extension completely
- Close all popup windows and reopen
#### 3. Extension Not Properly Loaded
**Symptom:** Nothing happens when clicking buttons
**Solution:**
- Chrome:
1. Go to `chrome://extensions/`
2. Find Greasemonkey
3. Click the reload icon (circular arrow)
4. Try again
- Firefox:
1. Go to `about:debugging#/runtime/this-firefox`
2. Find Greasemonkey
3. Click "Reload"
4. Try again
### Debug Steps
1. **Open the popup inspector**:
- Right-click anywhere in the popup window
- Select "Inspect" or "Inspect Element"
- This opens DevTools for the popup
2. **Check the Console tab** for errors like:
- "bootstrap is not defined" → Bootstrap didn't load
- "Cannot read property 'show' of undefined" → Modal not initialized
- "Element not found" → HTML structure issue
3. **Try in Console**:
```javascript
// Check if Bootstrap loaded
console.log(typeof bootstrap); // Should show "object"
// Check if elements exist
console.log(document.getElementById('addRuleBtn')); // Should show button element
console.log(document.getElementById('ruleModal')); // Should show modal element
```
4. **Manual test**:
```javascript
// Try to open modal manually
const modal = new bootstrap.Modal(document.getElementById('ruleModal'));
modal.show();
```
### Testing After Fix
1. Reload the extension
2. Click the Greasemonkey icon
3. Click "+ Add Rule"
4. Modal should open with the rule form
5. If successful, you should see the modal with:
- Rule Name field
- Match Type dropdown
- URL Pattern field (visible by default)
- JavaScript code area
- CSS code area
- Enabled checkbox
- Cancel and Save buttons
## Rules Not Executing
### Check List
1. ✓ Is the rule enabled? (toggle switch should be on)
2. ✓ Does the URL pattern match the current page URL?
3. ✓ If using element selector, does the element exist on the page?
4. ✓ Did you reload the page after creating/editing the rule?
### Debug Process
1. Open the page where the rule should run
2. Open browser console (F12)
3. Look for messages like: `[Greasemonkey] Executing rule: YourRuleName`
4. Check for error messages
### Common Issues
#### Rule Never Executes
- **URL pattern too specific**: Try a shorter, more general pattern
- ❌ `https://www.example.com/page.html?id=123`
- ✅ `example.com`
- **Element doesn't exist yet**: Page might load the element dynamically
- **Rule is disabled**: Check the toggle switch
#### JavaScript Not Running
- Check console for syntax errors
- Test your code in the page console first:
```javascript
// Test in console before adding to rule
console.log('Test');
```
#### CSS Not Applying
- Use `!important` to override existing styles:
```css
body {
background: #000 !important;
}
```
- Check CSS syntax is valid
- Some sites have very specific CSS that overrides yours
## Storage Issues
### Rules Not Persisting
**Chrome:**
- Ensure sync is enabled: `chrome://settings/syncSetup`
- Storage might be full (rare)
**Firefox:**
- Temporary add-ons don't persist after restart
- Use signed version for persistence
### Can't Edit/Delete Rules
1. Check browser console for errors
2. Try exporting rules (copy from console):
```javascript
chrome.storage.sync.get(['rules'], (result) => {
console.log(JSON.stringify(result.rules, null, 2));
});
```
3. Reload extension
## Performance Issues
### Popup Slow to Open
- Too many rules? Try disabling some
- Check if Bootstrap CDN is slow (network issue)
### Page Loads Slowly
- Complex JavaScript in rules can slow down pages
- Try simplifying your rules
- Use CSS instead of JavaScript when possible
## Browser-Specific Issues
### Chrome/Edge
- **Service worker inactive**: Reload extension
- **CSP errors**: Some sites block injected scripts (rare)
### Firefox
- **Temporary add-on removed**: Expected after restart, reload it
- **Storage not syncing**: Sign the extension for production use
## Getting Help
1. **Check console** (F12) for error messages
2. **Test with simple rule** first:
- Name: Test
- Match Type: URL
- URL Pattern: (current page URL)
- JavaScript: `alert('It works!');`
3. **Reload extension** after any changes
4. **Try test-page.html** to verify extension works
## Manual Reset
If all else fails, reset the extension:
### Chrome
```javascript
// In popup inspector console
chrome.storage.sync.clear(() => {
console.log('Storage cleared');
location.reload();
});
```
### Firefox
```javascript
// In popup inspector console
browser.storage.sync.clear().then(() => {
console.log('Storage cleared');
location.reload();
});
```
## Still Not Working?
1. Check if you have the latest version
2. Try in a different browser
3. Verify extension permissions are granted
4. Look for browser console errors
5. Try with a simple test rule on test-page.html
## Version Check
Current version: 1.0.0
Make sure you have all these files:
- manifest.json
- background.js
- content.js
- popup.html
- popup.css
- popup.js
- icon files (16, 48, 128)
## Contact
If issues persist after following this guide, contact Andy Dixon with:
- Browser version
- Error messages from console
- Steps to reproduce
- Screenshot if possible
+51
View File
@@ -0,0 +1,51 @@
// Background service worker for Chrome extension
chrome.runtime.onInstalled.addListener(() => {
console.log('GreasyBoii extension installed');
});
// Listen for messages from content script
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === 'getRules') {
chrome.storage.sync.get(['rules'], (result) => {
sendResponse({ rules: result.rules || [] });
});
return true; // Keep channel open for async response
}
// Execute JavaScript via scripting API to bypass CSP
if (request.action === 'executeScript') {
if (!sender.tab || !sender.tab.id) {
sendResponse({ success: false, error: 'No tab ID available' });
return;
}
const tabId = sender.tab.id;
const code = request.code;
const ruleName = request.ruleName;
console.log(`[GreasyBoii Background] Executing script for rule "${ruleName}" on tab ${tabId}`);
// Use chrome.scripting.executeScript which bypasses CSP
chrome.scripting.executeScript({
target: { tabId: tabId },
world: 'MAIN', // Execute in main world (page context), not isolated world
func: (jsCode) => {
try {
// Use indirect eval to execute in global scope
(1, eval)(jsCode);
} catch (e) {
console.error('[GreasyBoii] Script execution error:', e);
}
},
args: [code]
}).then(() => {
console.log(`[GreasyBoii Background] ✓ Script executed successfully`);
sendResponse({ success: true });
}).catch((error) => {
console.error(`[GreasyBoii Background] ✗ Script execution failed:`, error);
sendResponse({ success: false, error: error.message });
});
return true; // Keep channel open for async response
}
});
File diff suppressed because one or more lines are too long
+6
View File
File diff suppressed because one or more lines are too long
+93
View File
@@ -0,0 +1,93 @@
// Content script that runs on all pages
(function() {
'use strict';
console.log('[GreasyBoii] Content script loaded on:', window.location.href);
// Wait for DOM to be ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initGreasyBoii);
} else {
initGreasyBoii();
}
function initGreasyBoii() {
console.log('[GreasyBoii] Initializing...');
// Get rules from storage and execute matching ones
chrome.storage.sync.get(['rules'], (result) => {
const rules = result.rules || [];
const currentUrl = window.location.href;
console.log('[GreasyBoii] Found', rules.length, 'rule(s) in storage');
rules.forEach((rule) => {
if (!rule.enabled) {
console.log('[GreasyBoii] Skipping disabled rule:', rule.name);
return;
}
let shouldExecute = false;
// Check conditions
if (rule.matchType === 'url') {
shouldExecute = currentUrl.includes(rule.urlPattern);
console.log(`[GreasyBoii] URL match test for "${rule.name}": ${currentUrl} includes "${rule.urlPattern}" = ${shouldExecute}`);
} else if (rule.matchType === 'element') {
shouldExecute = document.querySelector(rule.elementSelector) !== null;
console.log(`[GreasyBoii] Element match test for "${rule.name}": selector "${rule.elementSelector}" found = ${shouldExecute}`);
} else if (rule.matchType === 'both') {
const urlMatches = currentUrl.includes(rule.urlPattern);
const elementExists = document.querySelector(rule.elementSelector) !== null;
shouldExecute = urlMatches && elementExists;
console.log(`[GreasyBoii] Combined match test for "${rule.name}": URL=${urlMatches}, Element=${elementExists}, Result=${shouldExecute}`);
}
if (shouldExecute) {
console.log(`[GreasyBoii] ✓ Executing rule: ${rule.name}`);
executeRule(rule);
} else {
console.log(`[GreasyBoii] ✗ Rule "${rule.name}" conditions not met`);
}
});
});
}
function executeRule(rule) {
// Execute JavaScript using chrome.scripting API to bypass CSP
if (rule.javascript && rule.javascript.trim()) {
console.log(`[GreasyBoii] Sending JavaScript execution request for rule: ${rule.name}`);
// Send message to background script to execute JavaScript
chrome.runtime.sendMessage({
action: 'executeScript',
code: rule.javascript,
ruleName: rule.name
}, (response) => {
if (chrome.runtime.lastError) {
console.error(`[GreasyBoii] Error communicating with background:`, chrome.runtime.lastError);
} else if (response && response.success) {
console.log(`[GreasyBoii] ✓ JavaScript executed successfully via background script`);
} else {
console.error(`[GreasyBoii] JavaScript execution failed:`, response?.error);
}
});
}
// Execute CSS - CSS injection doesn't have CSP issues
if (rule.css && rule.css.trim()) {
try {
console.log(`[GreasyBoii] Injecting CSS for rule: ${rule.name}`);
const style = document.createElement('style');
style.textContent = rule.css;
style.setAttribute('data-greasyboii-rule', rule.name);
(document.head || document.documentElement).appendChild(style);
console.log(`[GreasyBoii] ✓ CSS injected successfully`);
} catch (error) {
console.error(`[GreasyBoii] Error applying CSS for rule "${rule.name}":`, error);
}
}
}
})();
Binary file not shown.

After

Width:  |  Height:  |  Size: 499 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 115 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

+39
View File
@@ -0,0 +1,39 @@
{
"manifest_version": 3,
"name": "GreasyBoii",
"version": "1.0.4",
"description": "Execute custom JavaScript and CSS based on URL patterns and page elements",
"author": "Andy Dixon",
"permissions": [
"storage",
"activeTab",
"scripting"
],
"host_permissions": [
"<all_urls>"
],
"background": {
"service_worker": "background.js"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"],
"run_at": "document_idle",
"all_frames": false
}
],
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "icon16.png",
"48": "icon48.png",
"128": "icon128.png"
}
},
"icons": {
"16": "icon16.png",
"48": "icon48.png",
"128": "icon128.png"
}
}
+65
View File
@@ -0,0 +1,65 @@
body {
width: 600px;
min-height: 400px;
max-height: 600px;
overflow-y: auto;
}
.rules-list {
max-height: 450px;
overflow-y: auto;
}
.rule-card {
border: 1px solid #495057;
border-radius: 0.375rem;
padding: 1rem;
margin-bottom: 0.75rem;
background-color: #212529;
}
.rule-card:hover {
border-color: #6c757d;
}
.rule-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
}
.rule-name {
font-weight: 600;
font-size: 1.1rem;
}
.rule-actions {
display: flex;
gap: 0.5rem;
}
.rule-details {
font-size: 0.875rem;
color: #adb5bd;
}
.rule-badge {
font-size: 0.75rem;
padding: 0.25rem 0.5rem;
}
.font-monospace {
font-family: 'Courier New', monospace;
font-size: 0.875rem;
}
.form-switch .form-check-input {
cursor: pointer;
width: 3rem;
height: 1.5rem;
}
.modal-lg {
max-width: 700px;
}
+91
View File
@@ -0,0 +1,91 @@
<!DOCTYPE html>
<html lang="en" data-bs-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GreasyBoii</title>
<link rel="stylesheet" href="bootstrap.min.css">
<link rel="stylesheet" href="popup.css">
</head>
<body>
<div class="container-fluid p-3">
<div class="d-flex justify-content-between align-items-center mb-3">
<h4 class="mb-0">GreasyBoii</h4>
<button class="btn btn-primary btn-sm" id="addRuleBtn">+ Add Rule</button>
</div>
<div id="rulesList" class="rules-list mb-3">
<!-- Rules will be dynamically inserted here -->
</div>
<div id="emptyState" class="text-center text-muted py-4" style="display: none;">
<p>No rules yet. Click "Add Rule" to get started.</p>
</div>
</div>
<!-- Add/Edit Rule Modal -->
<div class="modal fade" id="ruleModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalTitle">Add Rule</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="ruleForm">
<div class="mb-3">
<label for="ruleName" class="form-label">Rule Name</label>
<input type="text" class="form-control" id="ruleName" required>
</div>
<div class="mb-3">
<label for="matchType" class="form-label">Match Type</label>
<select class="form-select" id="matchType" required>
<option value="url">URL contains string</option>
<option value="element">Page contains element</option>
<option value="both">Both URL and element</option>
</select>
</div>
<div class="mb-3" id="urlPatternGroup">
<label for="urlPattern" class="form-label">URL Pattern</label>
<input type="text" class="form-control" id="urlPattern" placeholder="e.g., example.com">
<small class="form-text text-muted">Enter a string to match in the URL</small>
</div>
<div class="mb-3" id="elementSelectorGroup" style="display: none;">
<label for="elementSelector" class="form-label">Element Selector</label>
<input type="text" class="form-control" id="elementSelector" placeholder="e.g., #my-element or .my-class">
<small class="form-text text-muted">Enter a CSS selector</small>
</div>
<div class="mb-3">
<label for="javascript" class="form-label">JavaScript Code</label>
<textarea class="form-control font-monospace" id="javascript" rows="6" placeholder="console.log('Hello from GreasyBoii!');"></textarea>
</div>
<div class="mb-3">
<label for="css" class="form-label">CSS Code</label>
<textarea class="form-control font-monospace" id="css" rows="6" placeholder="body { background-color: #000; }"></textarea>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="ruleEnabled" checked>
<label class="form-check-label" for="ruleEnabled">
Enabled
</label>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="saveRuleBtn">Save Rule</button>
</div>
</div>
</div>
</div>
<script src="bootstrap.bundle.min.js"></script>
<script src="popup.js"></script>
</body>
</html>
+302
View File
@@ -0,0 +1,302 @@
// Popup script for managing rules
let currentRules = [];
let editingRuleIndex = -1;
let ruleModal;
// Initialize
document.addEventListener('DOMContentLoaded', () => {
// Wait for Bootstrap to be fully loaded
if (typeof bootstrap !== 'undefined') {
initializeExtension();
} else {
// Fallback: wait a bit for Bootstrap to load
setTimeout(initializeExtension, 100);
}
});
function initializeExtension() {
try {
ruleModal = new bootstrap.Modal(document.getElementById('ruleModal'));
} catch (e) {
console.error('Error initializing modal:', e);
}
loadRules();
setupEventListeners();
}
function setupEventListeners() {
const addRuleBtn = document.getElementById('addRuleBtn');
const saveRuleBtn = document.getElementById('saveRuleBtn');
const matchTypeSelect = document.getElementById('matchType');
if (addRuleBtn) {
addRuleBtn.addEventListener('click', () => {
console.log('Add Rule button clicked');
openRuleModal();
});
} else {
console.error('Add Rule button not found!');
}
if (saveRuleBtn) {
saveRuleBtn.addEventListener('click', saveRule);
} else {
console.error('Save Rule button not found!');
}
if (matchTypeSelect) {
matchTypeSelect.addEventListener('change', (e) => {
updateFieldVisibility(e.target.value);
});
} else {
console.error('Match Type select not found!');
}
}
function updateFieldVisibility(matchType) {
const urlGroup = document.getElementById('urlPatternGroup');
const elementGroup = document.getElementById('elementSelectorGroup');
const urlInput = document.getElementById('urlPattern');
const elementInput = document.getElementById('elementSelector');
if (matchType === 'url') {
urlGroup.style.display = 'block';
elementGroup.style.display = 'none';
urlInput.required = true;
elementInput.required = false;
} else if (matchType === 'element') {
urlGroup.style.display = 'none';
elementGroup.style.display = 'block';
urlInput.required = false;
elementInput.required = true;
} else if (matchType === 'both') {
urlGroup.style.display = 'block';
elementGroup.style.display = 'block';
urlInput.required = true;
elementInput.required = true;
}
}
function loadRules() {
chrome.storage.sync.get(['rules'], (result) => {
currentRules = result.rules || [];
renderRules();
});
}
function renderRules() {
const rulesList = document.getElementById('rulesList');
const emptyState = document.getElementById('emptyState');
if (currentRules.length === 0) {
rulesList.innerHTML = '';
emptyState.style.display = 'block';
return;
}
emptyState.style.display = 'none';
rulesList.innerHTML = currentRules.map((rule, index) => {
const matchTypeLabel = {
'url': 'URL',
'element': 'Element',
'both': 'URL + Element'
}[rule.matchType] || rule.matchType;
const hasJS = rule.javascript && rule.javascript.trim();
const hasCSS = rule.css && rule.css.trim();
return `
<div class="rule-card" data-rule-index="${index}">
<div class="rule-header">
<div class="d-flex align-items-center gap-2">
<span class="rule-name">${escapeHtml(rule.name)}</span>
<span class="badge rule-badge bg-secondary">${matchTypeLabel}</span>
${hasJS ? '<span class="badge rule-badge bg-warning">JS</span>' : ''}
${hasCSS ? '<span class="badge rule-badge bg-info">CSS</span>' : ''}
</div>
<div class="rule-actions">
<div class="form-check form-switch mb-0">
<input class="form-check-input rule-toggle" type="checkbox" ${rule.enabled ? 'checked' : ''}
data-index="${index}" title="Enable/Disable">
</div>
<button class="btn btn-sm btn-outline-light btn-edit-rule" data-index="${index}" title="Edit">
<svg width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"/>
</svg>
</button>
<button class="btn btn-sm btn-outline-danger btn-delete-rule" data-index="${index}" title="Delete">
<svg width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z"/>
<path fill-rule="evenodd" d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"/>
</svg>
</button>
</div>
</div>
<div class="rule-details">
${rule.matchType === 'url' || rule.matchType === 'both' ? `<div><strong>URL:</strong> ${escapeHtml(rule.urlPattern)}</div>` : ''}
${rule.matchType === 'element' || rule.matchType === 'both' ? `<div><strong>Element:</strong> ${escapeHtml(rule.elementSelector)}</div>` : ''}
</div>
</div>
`;
}).join('');
// Add event listeners using event delegation
attachRuleEventListeners();
}
function attachRuleEventListeners() {
const rulesList = document.getElementById('rulesList');
// Remove old listeners if any
rulesList.removeEventListener('click', handleRuleClick);
rulesList.removeEventListener('change', handleRuleToggle);
// Add new listeners
rulesList.addEventListener('click', handleRuleClick);
rulesList.addEventListener('change', handleRuleToggle);
}
function handleRuleClick(e) {
const editBtn = e.target.closest('.btn-edit-rule');
const deleteBtn = e.target.closest('.btn-delete-rule');
if (editBtn) {
const index = parseInt(editBtn.dataset.index);
editRule(index);
} else if (deleteBtn) {
const index = parseInt(deleteBtn.dataset.index);
deleteRule(index);
}
}
function handleRuleToggle(e) {
if (e.target.classList.contains('rule-toggle')) {
const index = parseInt(e.target.dataset.index);
toggleRule(index);
}
}
function openRuleModal(ruleIndex = -1) {
editingRuleIndex = ruleIndex;
const modalTitle = document.getElementById('modalTitle');
const form = document.getElementById('ruleForm');
form.reset();
if (ruleIndex >= 0) {
modalTitle.textContent = 'Edit Rule';
const rule = currentRules[ruleIndex];
document.getElementById('ruleName').value = rule.name;
document.getElementById('matchType').value = rule.matchType;
document.getElementById('urlPattern').value = rule.urlPattern || '';
document.getElementById('elementSelector').value = rule.elementSelector || '';
document.getElementById('javascript').value = rule.javascript || '';
document.getElementById('css').value = rule.css || '';
document.getElementById('ruleEnabled').checked = rule.enabled;
} else {
modalTitle.textContent = 'Add Rule';
document.getElementById('ruleEnabled').checked = true;
}
updateFieldVisibility(document.getElementById('matchType').value);
// Show modal - ensure Bootstrap Modal is initialized
if (ruleModal) {
ruleModal.show();
} else {
// Fallback: try to initialize and show
try {
ruleModal = new bootstrap.Modal(document.getElementById('ruleModal'));
ruleModal.show();
} catch (e) {
console.error('Error showing modal:', e);
alert('Error opening rule editor. Please reload the extension.');
}
}
}
function saveRule() {
const name = document.getElementById('ruleName').value.trim();
const matchType = document.getElementById('matchType').value;
const urlPattern = document.getElementById('urlPattern').value.trim();
const elementSelector = document.getElementById('elementSelector').value.trim();
const javascript = document.getElementById('javascript').value;
const css = document.getElementById('css').value;
const enabled = document.getElementById('ruleEnabled').checked;
// Validation
if (!name) {
alert('Please enter a rule name');
return;
}
if (matchType === 'url' && !urlPattern) {
alert('Please enter a URL pattern');
return;
}
if (matchType === 'element' && !elementSelector) {
alert('Please enter an element selector');
return;
}
if (matchType === 'both' && (!urlPattern || !elementSelector)) {
alert('Please enter both URL pattern and element selector');
return;
}
if (!javascript && !css) {
alert('Please enter either JavaScript or CSS code');
return;
}
const rule = {
name,
matchType,
urlPattern,
elementSelector,
javascript,
css,
enabled
};
if (editingRuleIndex >= 0) {
currentRules[editingRuleIndex] = rule;
} else {
currentRules.push(rule);
}
chrome.storage.sync.set({ rules: currentRules }, () => {
loadRules();
ruleModal.hide();
});
}
function editRule(index) {
openRuleModal(index);
}
function deleteRule(index) {
if (confirm(`Delete rule "${currentRules[index].name}"?`)) {
currentRules.splice(index, 1);
chrome.storage.sync.set({ rules: currentRules }, () => {
loadRules();
});
}
}
function toggleRule(index) {
currentRules[index].enabled = !currentRules[index].enabled;
chrome.storage.sync.set({ rules: currentRules }, () => {
loadRules();
});
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
+14
View File
@@ -0,0 +1,14 @@
// Background script for Firefox extension
browser.runtime.onInstalled.addListener(() => {
console.log('GreasyBoii extension installed');
});
// Listen for messages from content script
browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === 'getRules') {
browser.storage.sync.get(['rules']).then((result) => {
sendResponse({ rules: result.rules || [] });
});
return true; // Keep channel open for async response
}
});
File diff suppressed because one or more lines are too long
+6
View File
File diff suppressed because one or more lines are too long
+117
View File
@@ -0,0 +1,117 @@
// Content script that runs on all pages
(function() {
'use strict';
console.log('[GreasyBoii] Content script loaded on:', window.location.href);
// Wait for DOM to be ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initGreasyBoii);
} else {
initGreasyBoii();
}
function initGreasyBoii() {
console.log('[GreasyBoii] Initializing...');
// Get rules from storage and execute matching ones
browser.storage.sync.get(['rules']).then((result) => {
const rules = result.rules || [];
const currentUrl = window.location.href;
console.log('[GreasyBoii] Found', rules.length, 'rule(s) in storage');
rules.forEach((rule) => {
if (!rule.enabled) {
console.log('[GreasyBoii] Skipping disabled rule:', rule.name);
return;
}
let shouldExecute = false;
// Check conditions
if (rule.matchType === 'url') {
shouldExecute = currentUrl.includes(rule.urlPattern);
console.log(`[GreasyBoii] URL match test for "${rule.name}": ${currentUrl} includes "${rule.urlPattern}" = ${shouldExecute}`);
} else if (rule.matchType === 'element') {
shouldExecute = document.querySelector(rule.elementSelector) !== null;
console.log(`[GreasyBoii] Element match test for "${rule.name}": selector "${rule.elementSelector}" found = ${shouldExecute}`);
} else if (rule.matchType === 'both') {
const urlMatches = currentUrl.includes(rule.urlPattern);
const elementExists = document.querySelector(rule.elementSelector) !== null;
shouldExecute = urlMatches && elementExists;
console.log(`[GreasyBoii] Combined match test for "${rule.name}": URL=${urlMatches}, Element=${elementExists}, Result=${shouldExecute}`);
}
if (shouldExecute) {
console.log(`[GreasyBoii] ✓ Executing rule: ${rule.name}`);
executeRule(rule);
} else {
console.log(`[GreasyBoii] ✗ Rule "${rule.name}" conditions not met`);
}
});
});
}
function executeRule(rule) {
// For Firefox, we'll use exportFunction and cloneInto to bypass CSP
if (rule.javascript && rule.javascript.trim()) {
console.log(`[GreasyBoii] Injecting JavaScript for rule: ${rule.name}`);
try {
// Method 1: Try wrappedJSObject (Firefox specific)
if (typeof wrappedJSObject !== 'undefined') {
try {
const code = `
(function() {
try {
${rule.javascript}
} catch (e) {
console.error('[GreasyBoii] Error in injected script:', e);
}
})();
`;
wrappedJSObject.eval(code);
console.log(`[GreasyBoii] ✓ JavaScript executed via wrappedJSObject`);
return;
} catch (e) {
console.log(`[GreasyBoii] wrappedJSObject method failed, trying alternative...`);
}
}
// Method 2: Script injection (works on most sites)
const script = document.createElement('script');
script.textContent = `
(function() {
try {
${rule.javascript}
} catch (e) {
console.error('[GreasyBoii] Error in injected script:', e);
}
})();
`;
(document.head || document.documentElement).appendChild(script);
script.remove();
console.log(`[GreasyBoii] ✓ JavaScript injected successfully`);
} catch (error) {
console.error(`[GreasyBoii] Error executing JavaScript for rule "${rule.name}":`, error);
}
}
// Execute CSS - CSS injection doesn't have CSP issues
if (rule.css && rule.css.trim()) {
try {
console.log(`[GreasyBoii] Injecting CSS for rule: ${rule.name}`);
const style = document.createElement('style');
style.textContent = rule.css;
style.setAttribute('data-greasyboii-rule', rule.name);
(document.head || document.documentElement).appendChild(style);
console.log(`[GreasyBoii] ✓ CSS injected successfully`);
} catch (error) {
console.error(`[GreasyBoii] Error applying CSS for rule "${rule.name}":`, error);
}
}
}
})();
Binary file not shown.

After

Width:  |  Height:  |  Size: 499 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

+36
View File
@@ -0,0 +1,36 @@
{
"manifest_version": 2,
"name": "GreasyBoii",
"version": "1.0.4",
"description": "Execute custom JavaScript and CSS based on URL patterns and page elements",
"author": "Andy Dixon",
"permissions": [
"storage",
"activeTab",
"<all_urls>"
],
"background": {
"scripts": ["background.js"]
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"],
"run_at": "document_idle",
"all_frames": false
}
],
"browser_action": {
"default_popup": "popup.html",
"default_icon": {
"16": "icon16.png",
"48": "icon48.png",
"128": "icon128.png"
}
},
"icons": {
"16": "icon16.png",
"48": "icon48.png",
"128": "icon128.png"
}
}
+65
View File
@@ -0,0 +1,65 @@
body {
width: 600px;
min-height: 400px;
max-height: 600px;
overflow-y: auto;
}
.rules-list {
max-height: 450px;
overflow-y: auto;
}
.rule-card {
border: 1px solid #495057;
border-radius: 0.375rem;
padding: 1rem;
margin-bottom: 0.75rem;
background-color: #212529;
}
.rule-card:hover {
border-color: #6c757d;
}
.rule-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
}
.rule-name {
font-weight: 600;
font-size: 1.1rem;
}
.rule-actions {
display: flex;
gap: 0.5rem;
}
.rule-details {
font-size: 0.875rem;
color: #adb5bd;
}
.rule-badge {
font-size: 0.75rem;
padding: 0.25rem 0.5rem;
}
.font-monospace {
font-family: 'Courier New', monospace;
font-size: 0.875rem;
}
.form-switch .form-check-input {
cursor: pointer;
width: 3rem;
height: 1.5rem;
}
.modal-lg {
max-width: 700px;
}
+91
View File
@@ -0,0 +1,91 @@
<!DOCTYPE html>
<html lang="en" data-bs-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GreasyBoii</title>
<link rel="stylesheet" href="bootstrap.min.css">
<link rel="stylesheet" href="popup.css">
</head>
<body>
<div class="container-fluid p-3">
<div class="d-flex justify-content-between align-items-center mb-3">
<h4 class="mb-0">GreasyBoii</h4>
<button class="btn btn-primary btn-sm" id="addRuleBtn">+ Add Rule</button>
</div>
<div id="rulesList" class="rules-list mb-3">
<!-- Rules will be dynamically inserted here -->
</div>
<div id="emptyState" class="text-center text-muted py-4" style="display: none;">
<p>No rules yet. Click "Add Rule" to get started.</p>
</div>
</div>
<!-- Add/Edit Rule Modal -->
<div class="modal fade" id="ruleModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalTitle">Add Rule</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="ruleForm">
<div class="mb-3">
<label for="ruleName" class="form-label">Rule Name</label>
<input type="text" class="form-control" id="ruleName" required>
</div>
<div class="mb-3">
<label for="matchType" class="form-label">Match Type</label>
<select class="form-select" id="matchType" required>
<option value="url">URL contains string</option>
<option value="element">Page contains element</option>
<option value="both">Both URL and element</option>
</select>
</div>
<div class="mb-3" id="urlPatternGroup">
<label for="urlPattern" class="form-label">URL Pattern</label>
<input type="text" class="form-control" id="urlPattern" placeholder="e.g., example.com">
<small class="form-text text-muted">Enter a string to match in the URL</small>
</div>
<div class="mb-3" id="elementSelectorGroup" style="display: none;">
<label for="elementSelector" class="form-label">Element Selector</label>
<input type="text" class="form-control" id="elementSelector" placeholder="e.g., #my-element or .my-class">
<small class="form-text text-muted">Enter a CSS selector</small>
</div>
<div class="mb-3">
<label for="javascript" class="form-label">JavaScript Code</label>
<textarea class="form-control font-monospace" id="javascript" rows="6" placeholder="console.log('Hello from GreasyBoii!');"></textarea>
</div>
<div class="mb-3">
<label for="css" class="form-label">CSS Code</label>
<textarea class="form-control font-monospace" id="css" rows="6" placeholder="body { background-color: #000; }"></textarea>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="ruleEnabled" checked>
<label class="form-check-label" for="ruleEnabled">
Enabled
</label>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="saveRuleBtn">Save Rule</button>
</div>
</div>
</div>
</div>
<script src="bootstrap.bundle.min.js"></script>
<script src="popup.js"></script>
</body>
</html>
+302
View File
@@ -0,0 +1,302 @@
// Popup script for managing rules (Firefox version)
let currentRules = [];
let editingRuleIndex = -1;
let ruleModal;
// Initialize
document.addEventListener('DOMContentLoaded', () => {
// Wait for Bootstrap to be fully loaded
if (typeof bootstrap !== 'undefined') {
initializeExtension();
} else {
// Fallback: wait a bit for Bootstrap to load
setTimeout(initializeExtension, 100);
}
});
function initializeExtension() {
try {
ruleModal = new bootstrap.Modal(document.getElementById('ruleModal'));
} catch (e) {
console.error('Error initializing modal:', e);
}
loadRules();
setupEventListeners();
}
function setupEventListeners() {
const addRuleBtn = document.getElementById('addRuleBtn');
const saveRuleBtn = document.getElementById('saveRuleBtn');
const matchTypeSelect = document.getElementById('matchType');
if (addRuleBtn) {
addRuleBtn.addEventListener('click', () => {
console.log('Add Rule button clicked');
openRuleModal();
});
} else {
console.error('Add Rule button not found!');
}
if (saveRuleBtn) {
saveRuleBtn.addEventListener('click', saveRule);
} else {
console.error('Save Rule button not found!');
}
if (matchTypeSelect) {
matchTypeSelect.addEventListener('change', (e) => {
updateFieldVisibility(e.target.value);
});
} else {
console.error('Match Type select not found!');
}
}
function updateFieldVisibility(matchType) {
const urlGroup = document.getElementById('urlPatternGroup');
const elementGroup = document.getElementById('elementSelectorGroup');
const urlInput = document.getElementById('urlPattern');
const elementInput = document.getElementById('elementSelector');
if (matchType === 'url') {
urlGroup.style.display = 'block';
elementGroup.style.display = 'none';
urlInput.required = true;
elementInput.required = false;
} else if (matchType === 'element') {
urlGroup.style.display = 'none';
elementGroup.style.display = 'block';
urlInput.required = false;
elementInput.required = true;
} else if (matchType === 'both') {
urlGroup.style.display = 'block';
elementGroup.style.display = 'block';
urlInput.required = true;
elementInput.required = true;
}
}
function loadRules() {
browser.storage.sync.get(['rules']).then((result) => {
currentRules = result.rules || [];
renderRules();
});
}
function renderRules() {
const rulesList = document.getElementById('rulesList');
const emptyState = document.getElementById('emptyState');
if (currentRules.length === 0) {
rulesList.innerHTML = '';
emptyState.style.display = 'block';
return;
}
emptyState.style.display = 'none';
rulesList.innerHTML = currentRules.map((rule, index) => {
const matchTypeLabel = {
'url': 'URL',
'element': 'Element',
'both': 'URL + Element'
}[rule.matchType] || rule.matchType;
const hasJS = rule.javascript && rule.javascript.trim();
const hasCSS = rule.css && rule.css.trim();
return `
<div class="rule-card" data-rule-index="${index}">
<div class="rule-header">
<div class="d-flex align-items-center gap-2">
<span class="rule-name">${escapeHtml(rule.name)}</span>
<span class="badge rule-badge bg-secondary">${matchTypeLabel}</span>
${hasJS ? '<span class="badge rule-badge bg-warning">JS</span>' : ''}
${hasCSS ? '<span class="badge rule-badge bg-info">CSS</span>' : ''}
</div>
<div class="rule-actions">
<div class="form-check form-switch mb-0">
<input class="form-check-input rule-toggle" type="checkbox" ${rule.enabled ? 'checked' : ''}
data-index="${index}" title="Enable/Disable">
</div>
<button class="btn btn-sm btn-outline-light btn-edit-rule" data-index="${index}" title="Edit">
<svg width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"/>
</svg>
</button>
<button class="btn btn-sm btn-outline-danger btn-delete-rule" data-index="${index}" title="Delete">
<svg width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z"/>
<path fill-rule="evenodd" d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"/>
</svg>
</button>
</div>
</div>
<div class="rule-details">
${rule.matchType === 'url' || rule.matchType === 'both' ? `<div><strong>URL:</strong> ${escapeHtml(rule.urlPattern)}</div>` : ''}
${rule.matchType === 'element' || rule.matchType === 'both' ? `<div><strong>Element:</strong> ${escapeHtml(rule.elementSelector)}</div>` : ''}
</div>
</div>
`;
}).join('');
// Add event listeners using event delegation
attachRuleEventListeners();
}
function attachRuleEventListeners() {
const rulesList = document.getElementById('rulesList');
// Remove old listeners if any
rulesList.removeEventListener('click', handleRuleClick);
rulesList.removeEventListener('change', handleRuleToggle);
// Add new listeners
rulesList.addEventListener('click', handleRuleClick);
rulesList.addEventListener('change', handleRuleToggle);
}
function handleRuleClick(e) {
const editBtn = e.target.closest('.btn-edit-rule');
const deleteBtn = e.target.closest('.btn-delete-rule');
if (editBtn) {
const index = parseInt(editBtn.dataset.index);
editRule(index);
} else if (deleteBtn) {
const index = parseInt(deleteBtn.dataset.index);
deleteRule(index);
}
}
function handleRuleToggle(e) {
if (e.target.classList.contains('rule-toggle')) {
const index = parseInt(e.target.dataset.index);
toggleRule(index);
}
}
function openRuleModal(ruleIndex = -1) {
editingRuleIndex = ruleIndex;
const modalTitle = document.getElementById('modalTitle');
const form = document.getElementById('ruleForm');
form.reset();
if (ruleIndex >= 0) {
modalTitle.textContent = 'Edit Rule';
const rule = currentRules[ruleIndex];
document.getElementById('ruleName').value = rule.name;
document.getElementById('matchType').value = rule.matchType;
document.getElementById('urlPattern').value = rule.urlPattern || '';
document.getElementById('elementSelector').value = rule.elementSelector || '';
document.getElementById('javascript').value = rule.javascript || '';
document.getElementById('css').value = rule.css || '';
document.getElementById('ruleEnabled').checked = rule.enabled;
} else {
modalTitle.textContent = 'Add Rule';
document.getElementById('ruleEnabled').checked = true;
}
updateFieldVisibility(document.getElementById('matchType').value);
// Show modal - ensure Bootstrap Modal is initialized
if (ruleModal) {
ruleModal.show();
} else {
// Fallback: try to initialize and show
try {
ruleModal = new bootstrap.Modal(document.getElementById('ruleModal'));
ruleModal.show();
} catch (e) {
console.error('Error showing modal:', e);
alert('Error opening rule editor. Please reload the extension.');
}
}
}
function saveRule() {
const name = document.getElementById('ruleName').value.trim();
const matchType = document.getElementById('matchType').value;
const urlPattern = document.getElementById('urlPattern').value.trim();
const elementSelector = document.getElementById('elementSelector').value.trim();
const javascript = document.getElementById('javascript').value;
const css = document.getElementById('css').value;
const enabled = document.getElementById('ruleEnabled').checked;
// Validation
if (!name) {
alert('Please enter a rule name');
return;
}
if (matchType === 'url' && !urlPattern) {
alert('Please enter a URL pattern');
return;
}
if (matchType === 'element' && !elementSelector) {
alert('Please enter an element selector');
return;
}
if (matchType === 'both' && (!urlPattern || !elementSelector)) {
alert('Please enter both URL pattern and element selector');
return;
}
if (!javascript && !css) {
alert('Please enter either JavaScript or CSS code');
return;
}
const rule = {
name,
matchType,
urlPattern,
elementSelector,
javascript,
css,
enabled
};
if (editingRuleIndex >= 0) {
currentRules[editingRuleIndex] = rule;
} else {
currentRules.push(rule);
}
browser.storage.sync.set({ rules: currentRules }).then(() => {
loadRules();
ruleModal.hide();
});
}
function editRule(index) {
openRuleModal(index);
}
function deleteRule(index) {
if (confirm(`Delete rule "${currentRules[index].name}"?`)) {
currentRules.splice(index, 1);
browser.storage.sync.set({ rules: currentRules }).then(() => {
loadRules();
});
}
}
function toggleRule(index) {
currentRules[index].enabled = !currentRules[index].enabled;
browser.storage.sync.set({ rules: currentRules }).then(() => {
loadRules();
});
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
Executable
+13
View File
@@ -0,0 +1,13 @@
#!/bin/bash
echo "📦 Packaging GreasyBoii v1.0.4..."
mkdir -p dist
echo "Chrome..."
cd chrome && zip -r ../dist/greasyboii-chrome-v1.0.4.zip * -x "*.DS_Store" && cd ..
echo "Firefox..."
cd firefox && zip -r ../dist/greasyboii-firefox-v1.0.4.zip * -x "*.DS_Store" && cd ..
echo ""
echo "✅ Done! Packages created:"
ls -lh dist/
+134
View File
@@ -0,0 +1,134 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Greasemonkey Test Page</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 50px auto;
padding: 20px;
background: #f5f5f5;
}
h1 { color: #333; }
.test-section {
background: white;
padding: 20px;
margin: 20px 0;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.test-element {
padding: 10px;
background: #e3f2fd;
border-left: 4px solid #2196F3;
margin: 10px 0;
}
code {
background: #f5f5f5;
padding: 2px 6px;
border-radius: 3px;
font-family: 'Courier New', monospace;
}
</style>
</head>
<body>
<h1>Greasemonkey Extension Test Page</h1>
<div class="test-section">
<h2>Test Element Selector</h2>
<p>This page contains elements you can target with your Greasemonkey rules.</p>
<div id="test-element-1" class="test-element">
<strong>Element #1</strong> - ID: <code>#test-element-1</code>
</div>
<div id="test-element-2" class="test-element">
<strong>Element #2</strong> - ID: <code>#test-element-2</code>
</div>
<div class="test-class-target test-element">
<strong>Element #3</strong> - Class: <code>.test-class-target</code>
</div>
</div>
<div class="test-section">
<h2>Example Rules to Try</h2>
<h3>Example 1: Change Background Color</h3>
<ul>
<li><strong>Match Type:</strong> URL contains string</li>
<li><strong>URL Pattern:</strong> <code>test-page.html</code></li>
<li><strong>CSS Code:</strong></li>
</ul>
<pre><code>body {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
color: white !important;
}</code></pre>
<h3>Example 2: Add Alert on Element</h3>
<ul>
<li><strong>Match Type:</strong> Page contains element</li>
<li><strong>Element Selector:</strong> <code>#test-element-1</code></li>
<li><strong>JavaScript Code:</strong></li>
</ul>
<pre><code>const elem = document.getElementById('test-element-1');
elem.style.background = 'yellow';
elem.innerHTML += ' - Modified by Greasemonkey!';</code></pre>
<h3>Example 3: Combined Rule</h3>
<ul>
<li><strong>Match Type:</strong> Both URL and element</li>
<li><strong>URL Pattern:</strong> <code>test-page</code></li>
<li><strong>Element Selector:</strong> <code>.test-class-target</code></li>
<li><strong>JavaScript Code:</strong></li>
</ul>
<pre><code>const elem = document.querySelector('.test-class-target');
elem.style.border = '4px solid red';
elem.style.animation = 'pulse 2s infinite';
const style = document.createElement('style');
style.textContent = `
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
`;
document.head.appendChild(style);</code></pre>
<h3>Example 4: Hide Elements</h3>
<ul>
<li><strong>Match Type:</strong> URL contains string</li>
<li><strong>URL Pattern:</strong> <code>test-page.html</code></li>
<li><strong>CSS Code:</strong></li>
</ul>
<pre><code>#test-element-2 {
display: none !important;
}</code></pre>
</div>
<div class="test-section">
<h2>How to Test</h2>
<ol>
<li>Install the Greasemonkey extension in your browser</li>
<li>Open this test page in your browser</li>
<li>Click the Greasemonkey icon in your toolbar</li>
<li>Create a new rule using one of the examples above</li>
<li>Reload this page to see the effects</li>
<li>Check the browser console (F12) for Greasemonkey log messages</li>
</ol>
</div>
<div class="test-section">
<h2>Verification</h2>
<p>Open your browser's developer console (F12) to see Greasemonkey execution logs.</p>
<p>You should see messages like: <code>[Greasemonkey] Executing rule: Your Rule Name</code></p>
</div>
<script>
console.log('Test page loaded. Greasemonkey rules should execute now if conditions are met.');
</script>
</body>
</html>