# Bookmarklets Collection

A collection of useful bookmarklets for web development and browsing. Drag any bookmarklet button to your bookmarks bar to install, or use the copy button for mobile installation.

## Installation Instructions

### Desktop (Chrome / Firefox / Safari)

1. Make sure your bookmarks bar is visible
   - Chrome: Press `Cmd+Shift+B` (Mac) or `Ctrl+Shift+B` (Windows)
   - Firefox: Press `Cmd+B` (Mac) or `Ctrl+B` (Windows)
   - Safari: View → Show Favorites Bar
2. Drag the bookmarklet button to your bookmarks bar
3. Click the bookmarklet on any page to activate

### Mobile Safari (iPhone/iPad)

1. Click the "Copy" button next to the bookmarklet
2. Create a new bookmark for any page (Share → Add Bookmark)
3. Edit the bookmark, delete the URL, and paste the copied code
4. To use: navigate to a page, open bookmarks, and tap the bookmarklet

## Bookmarklets

### View Reference Anchors

Reveals all linkable anchor points on a page by adding visible `#` links to elements with `id` attributes or `<a name="...">` tags. Perfect for finding hidden reference targets you can link to.

Features:

- Finds all elements with `id` attributes
- Finds all `<a name="...">` anchor tags
- Adds a clickable `#` link that copies the anchor URL to clipboard
- Highlights anchors with a subtle background on hover
- Click the `#` to copy the direct link

View Anchors
View source code

    (function() {
        // Don't add twice
        if (document.getElementById('__anchor_styles')) {
            return;
        }

        // Add styles
        const style = document.createElement('style');
        style.id = '__anchor_styles';
        style.textContent = `
            .__anchor_link {
                position: relative;
                text-decoration: none !important;
                color: #2c5aa0 !important;
                font-weight: bold;
                font-size: 0.9em;
                margin-right: 4px;
                padding: 2px 5px;
                border-radius: 3px;
                background: rgba(44, 90, 160, 0.1);
                opacity: 0.7;
                transition: opacity 0.2s, background 0.2s;
            }
            .__anchor_link:hover {
                opacity: 1;
                background: rgba(44, 90, 160, 0.2);
            }
            .__anchor_added {
                scroll-margin-top: 20px;
            }
        `;
        document.head.appendChild(style);

        // Collect all anchor targets
        const anchors = new Set();

        // Find all elements with id attribute
        document.querySelectorAll('[id]').forEach(el => {
            if (el.id && !el.id.startsWith('__anchor')) anchors.add(el);
        });

        // Find all <a name="..."> elements
        document.querySelectorAll('a[name]').forEach(el => {
            if (el.getAttribute('name')) anchors.add(el);
        });

        let count = 0;
        anchors.forEach(el => {
            // Skip if already processed
            if (el.querySelector('.__anchor_link')) return;

            const id = el.id || el.getAttribute('name');
            if (!id) return;

            const link = document.createElement('a');
            link.className = '__anchor_link';
            link.href = '#' + id;
            link.textContent = '#';
            link.title = 'Click to copy link to #' + id;

            link.onclick = function(e) {
                e.preventDefault();
                e.stopPropagation();
                const url = location.href.split('#')[0] + '#' + id;
                navigator.clipboard.writeText(url).then(() => {
                    link.textContent = '\u2713';
                    setTimeout(() => link.textContent = '#', 1500);
                }).catch(() => {
                    prompt('Copy this URL:', url);
                });
            };

            el.classList.add('__anchor_added');

            // Insert as first child
            if (el.firstChild) {
                el.insertBefore(link, el.firstChild);
            } else {
                el.appendChild(link);
            }
            count++;
        });

        alert('Found ' + count + ' anchor points on this page');
    })();

### View Source

Opens a modal dialog showing the current page's HTML source (`document.documentElement.outerHTML`). Includes options to pretty-print the HTML and copy it to your clipboard.

Features:

- Shows live DOM source (not original page source)
- Pretty Print button formats the HTML with proper indentation
- Copy button copies source to clipboard
- Large, resizable modal for easy viewing
- Press Escape or click outside to close

View Source
View source code

    (function() {
        // Toggle off if already open
        if (document.getElementById('__viewsource_modal')) {
            document.getElementById('__viewsource_modal').remove();
            return;
        }

        // Create modal overlay
        const overlay = document.createElement('div');
        overlay.id = '__viewsource_modal';
        overlay.innerHTML = `
            <style>
                #__viewsource_modal {
                    position: fixed;
                    top: 0; left: 0; right: 0; bottom: 0;
                    background: rgba(0,0,0,0.7);
                    z-index: 999999;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    font-family: -apple-system, BlinkMacSystemFont, sans-serif;
                }
                #__viewsource_box {
                    background: #fff;
                    border-radius: 8px;
                    width: 90vw;
                    height: 85vh;
                    max-width: 1200px;
                    display: flex;
                    flex-direction: column;
                    box-shadow: 0 4px 20px rgba(0,0,0,0.3);
                }
                #__viewsource_header {
                    padding: 15px 20px;
                    border-bottom: 1px solid #ddd;
                    display: flex;
                    justify-content: space-between;
                    align-items: center;
                }
                #__viewsource_header h2 {
                    margin: 0;
                    font-size: 18px;
                    color: #333;
                }
                #__viewsource_buttons {
                    display: flex;
                    gap: 10px;
                }
                #__viewsource_buttons button {
                    padding: 8px 16px;
                    border: none;
                    border-radius: 4px;
                    cursor: pointer;
                    font-size: 14px;
                    font-weight: 500;
                }
                #__viewsource_pretty { background: #2c5aa0; color: #fff; }
                #__viewsource_pretty:hover { background: #1e3d6f; }
                #__viewsource_copy { background: #28a745; color: #fff; }
                #__viewsource_copy:hover { background: #218838; }
                #__viewsource_close { background: #dc3545; color: #fff; }
                #__viewsource_close:hover { background: #c82333; }
                #__viewsource_textarea {
                    flex: 1;
                    margin: 0;
                    padding: 15px;
                    border: none;
                    resize: none;
                    font-family: 'SF Mono', Monaco, 'Cascadia Code', Consolas, monospace;
                    font-size: 13px;
                    line-height: 1.5;
                    white-space: pre;
                    overflow: auto;
                }
            </style>
            <div id="__viewsource_box">
                <div id="__viewsource_header">
                    <h2>Page Source</h2>
                    <div id="__viewsource_buttons">
                        <button id="__viewsource_pretty">Pretty Print</button>
                        <button id="__viewsource_copy">Copy</button>
                        <button id="__viewsource_close">&#10005; Close</button>
                    </div>
                </div>
                <textarea id="__viewsource_textarea" readonly></textarea>
            </div>
        `;

        document.body.appendChild(overlay);

        const textarea = document.getElementById('__viewsource_textarea');
        const html = document.documentElement.outerHTML;
        textarea.value = html;

        // Close button
        document.getElementById('__viewsource_close').onclick = () => overlay.remove();

        // Click outside to close
        overlay.onclick = (e) => {
            if (e.target === overlay) overlay.remove();
        };

        // Escape key to close
        document.addEventListener('keydown', function esc(e) {
            if (e.key === 'Escape') {
                overlay.remove();
                document.removeEventListener('keydown', esc);
            }
        });

        // Copy button
        document.getElementById('__viewsource_copy').onclick = function() {
            textarea.select();
            navigator.clipboard.writeText(textarea.value).then(() => {
                this.textContent = 'Copied!';
                setTimeout(() => this.textContent = 'Copy', 1500);
            }).catch(() => {
                document.execCommand('copy');
                this.textContent = 'Copied!';
                setTimeout(() => this.textContent = 'Copy', 1500);
            });
        };

        // Pretty print button
        document.getElementById('__viewsource_pretty').onclick = function() {
            const raw = textarea.value;
            let formatted = '';
            let indent = 0;
            const tab = '  ';

            // Split by tags while preserving them
            const rawLines = raw.replace(/>\s*</g, '>\n<').split('\n');

            rawLines.forEach(line => {
                line = line.trim();
                if (!line) return;

                // Decrease indent for closing tags
                if (line.match(/^<\//)) {
                    indent = Math.max(0, indent - 1);
                }

                formatted += tab.repeat(indent) + line + '\n';

                // Increase indent for opening tags (not self-closing, not void)
                if (line.match(/^<[a-z][^>]*[^\/]>$/i) &&
                    !line.match(/^<(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)/i)) {
                    indent++;
                }
            });

            textarea.value = formatted.trim();
            this.textContent = 'Done!';
            this.disabled = true;
        };
    })();