~bouncepaw/betula

betula: Bookmarklet patchset desc. v1 SUPERSEDED

I couldn't find a way to generate JS dynamically inside href.
So, I just hardcoded all the code to href and replaced site URL using go
templates variables.

Bookmarklet can fetch selected text from HTML page and convert it to
mycomarkup, thanks to Umar.

Danila Gorelko (1):
  Added bookmarklet to settings page.

 web/bookmarklet.js  | 91 +++++++++++++++++++++++++++++++++++++++++++++
 web/settings.gohtml | 14 +++++++
 web/style.css       | 12 ++++++
 3 files changed, 117 insertions(+)
 create mode 100644 web/bookmarklet.js

-- 
2.38.5
#1024770 .build.yml failed
betula/patches/.build.yml: FAILED in 49s

[Bookmarklet patchset desc.][0] from [~danilax86][1]

[0]: https://lists.sr.ht/~bouncepaw/betula/patches/42709
[1]: mailto:danila@danilax86.space

✗ #1024770 FAILED betula/patches/.build.yml https://builds.sr.ht/~bouncepaw/job/1024770
Oh, that's a long-awaited addition! Some issues:

You include the original bookmarklet script, but the file `bookmarklet.js` is not really used.
I don't see the point of it then.

Then, the actual bookmarklet is a condensed one-liner. Neither readable nor editable. The
condensed version should be generated on Betula startup somehow. The `bookmarklet.js` is read
by embed.FS on startup, so I think something could be done in this direction.

You could generate the bookmark URL client-side with some JS party tricks, see the link below.
I don't think it's a good idea.

=> https://stackoverflow.com/questions/7395686/how-can-i-serialize-a-function-in-javascript

Also, the bookmarklet uses the URL known at the moment the page was loaded. But the user can
change it one the same page! Having one URL written and a different one used doesn't really
make sense. I think it makes sense to put the bookmarklet on a different page, `/bookmarklet`.
There won't be such conflicts this way. It can be linked on the Settings page.

It's not like the bookmarklet is a setting really btw...
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~bouncepaw/betula/patches/42709/mbox | git am -3
Learn more about email & git

[PATCH betula 1/1] Added bookmarklet to settings page. Export this patch

From: Danila Gorelko <danila@danilax86.space>

Thanks to Umar, https://handlerug.me/betula-save-bookmarklet

Fixes: https://todo.sr.ht/~bouncepaw/betula/59
---
 web/bookmarklet.js  | 91 +++++++++++++++++++++++++++++++++++++++++++++
 web/settings.gohtml | 14 +++++++
 web/style.css       | 12 ++++++
 3 files changed, 117 insertions(+)
 create mode 100644 web/bookmarklet.js

diff --git a/web/bookmarklet.js b/web/bookmarklet.js
new file mode 100644
index 0000000..4d4fad2
--- /dev/null
+++ b/web/bookmarklet.js
@@ -0,0 +1,91 @@
// Save link bookmarklet for Betula
// 2023 Umar Getagazov <umar@handlerug.me>
// Public domain, but attribution appreciated.
// https://handlerug.me/betula-save-bookmarklet

(($) => {
    function getSelectionInMycomarkup() {
        function convert(node, parentNodeName = '') {
            if (node instanceof Text) {
                if (node.textContent.trim() === '') {
                    return '';
                }

                return node.textContent
                    .replace(/\\/g,   '\\\\')
                    .replace(/\*\*/g, '\\**')
                    .replace(/\/\//g, '\\//')
                    .replace(/\+\+/g, '\\++');
            }

            let nodeName = node.nodeName.toLowerCase();

            let result = '';
            for (const child of node.childNodes) {
                result += convert(child, nodeName);
            }

            if (nodeName === 'p') {
                return `\n\n${result.trim()}\n\n`;
            } else if (nodeName === 'br') {
                return '\n';
            } else if (nodeName === 'a') {
                return `[[${decodeURI(node.href)} | ${result}]]`;
            } else if (nodeName === 'b' || nodeName === 'strong') {
                return `**${result}**`;
            } else if (nodeName === 'i' || nodeName === 'em') {
                return `//${result}//`;
            } else if (nodeName === 'h1') {
                return `\n\n${result}\n\n`;
            } else if (nodeName === 'h2') {
                return `= ${result}\n\n`;
            } else if (nodeName === 'h3') {
                return `== ${result}\n\n`;
            } else if (nodeName === 'h4') {
                return `=== ${result}\n\n`;
            } else if (nodeName === 'h5') {
                return `==== ${result}\n\n`;
            } else if (nodeName === 'li') {
                if (node.children.length === 1) {
                    let link = node.children[0];
                    if (link.nodeName.toLowerCase() === 'a') {
                        if (link.href === link.innerText || decodeURI(link.href) === link.innerText) {
                            return `=> ${decodeURI(link.href)}\n`;
                        } else {
                            return `=> ${decodeURI(link.href)} | ${link.innerText}\n`;
                        }
                    }
                }
                return parentNodeName === 'ol'
                    ? `*. ${result}\n`
                    : `* ${result}\n`;
            } else {
                return result;
            }
        }

        let selection = window.getSelection();
        if (selection.rangeCount === 0) {
            return '';
        }
        let range = selection.getRangeAt(0);
        let contents = range.cloneContents();
        return convert(contents).replace(/\n\n+/g, '\n\n');
    }

    let u = 'https://links.handlerug.me/save-link?' + new URLSearchParams({
        url: ($('link[rel=canonical]') || location).href,
        title: $('meta[property="og:title"]')?.content || document.title,
        description: (
            getSelectionInMycomarkup() ||
            $('meta[property="og:description"]')?.content ||
            $('meta[name=description]')?.content
        )?.trim().replace(/^/gm, '> ') || ''
    });

    try {
        window.open(u, '_blank', 'location=yes,width=600,height=800,scrollbars=yes,status=yes,noopener,noreferrer');
    } catch {
        location.href = u;
    }
})(document.querySelector.bind(document));
diff --git a/web/settings.gohtml b/web/settings.gohtml
index c692111..ac66e9d 100644
--- a/web/settings.gohtml
+++ b/web/settings.gohtml
@@ -38,6 +38,20 @@
					</p>
				</div>

                <h3>Bookmarklet</h3>

                <p class="input-caption">
                    This special link allows you to add a link to your betula directly by using a bookmark in your web browser.
                </p>
                <div class="bookmarklet">
                    <a href="javascript:(function(){(e=>{let n=&quot;{{.SiteURL}}/save-link?&quot;+new URLSearchParams({url:(e(&quot;link[rel=canonical]&quot;)||location).href,title:e('meta[property=&quot;og:title&quot;]')?.content||document.title,description:(function(){let e=window.getSelection();return 0===e.rangeCount?&quot;&quot;:function e(n,t=&quot;&quot;){if(n instanceof Text)return&quot;&quot;===n.textContent.trim()?&quot;&quot;:n.textContent.replace(/\\/g,&quot;\\\\&quot;).replace(/\*\*/g,&quot;\\**&quot;).replace(/\/\//g,&quot;\\//&quot;).replace(/\+\+/g,&quot;\\++&quot;);let r=n.nodeName.toLowerCase(),o=&quot;&quot;;for(const t of n.childNodes)o+=e(t,r);if(&quot;p&quot;===r)return`\n\n${o.trim()}\n\n`;if(&quot;br&quot;===r)return&quot;\n&quot;;if(&quot;a&quot;===r)return`[[${decodeURI(n.href)} | ${o}]]`;if(&quot;b&quot;===r||&quot;strong&quot;===r)return`**${o}**`;if(&quot;i&quot;===r||&quot;em&quot;===r)return`//${o}//`;if(&quot;h1&quot;===r)return`\n\n${o}\n\n`;if(&quot;h2&quot;===r)return`= ${o}\n\n`;if(&quot;h3&quot;===r)return`== ${o}\n\n`;if(&quot;h4&quot;===r)return`=== ${o}\n\n`;if(&quot;h5&quot;===r)return`==== ${o}\n\n`;if(&quot;li&quot;===r){if(1===n.children.length){let e=n.children[0];if(&quot;a&quot;===e.nodeName.toLowerCase())return e.href===e.innerText||decodeURI(e.href)===e.innerText?`=> ${decodeURI(e.href)}\n`:`=> ${decodeURI(e.href)} | ${e.innerText}\n`}return&quot;ol&quot;===t?`*. ${o}\n`:`* ${o}\n`}return o}(e.getRangeAt(0).cloneContents()).replace(/\n\n+/g,&quot;\n\n&quot;)}()||e('meta[property=&quot;og:description&quot;]')?.content||e(&quot;meta[name=description]&quot;)?.content)?.trim().replace(/^/gm,&quot;> &quot;)||&quot;&quot;});try{window.open(n,&quot;_blank&quot;,&quot;location=yes,width=600,height=800,scrollbars=yes,status=yes,noopener,noreferrer&quot;)}catch{location.href=n}})(document.querySelector.bind(document));}());">
                        Add to Betula
                    </a>
                </div>
                <p class="input-caption">
                    Drag and drop this link to your bookmarks.
                </p>

				<h3>Advanced</h3>

				<div>
diff --git a/web/style.css b/web/style.css
index 47bd7ae..eafeb9b 100644
--- a/web/style.css
+++ b/web/style.css
@@ -391,6 +391,18 @@ a.btn_destructive:visited,
    color: white;
}

.bookmarklet {
    border: 1px dashed #999;
    border-radius: 5px;
    padding: 15px;
    text-align: center;
}

.bookmarklet a {
    text-decoration: none;
    font-weight: bold;
}

@media (prefers-color-scheme: dark) {
    .btn {
        border: #444 solid 1px;
-- 
2.38.5
betula/patches/.build.yml: FAILED in 49s

[Bookmarklet patchset desc.][0] from [~danilax86][1]

[0]: https://lists.sr.ht/~bouncepaw/betula/patches/42709
[1]: mailto:danila@danilax86.space

✗ #1024770 FAILED betula/patches/.build.yml https://builds.sr.ht/~bouncepaw/job/1024770
Oh, that's a long-awaited addition! Some issues:

You include the original bookmarklet script, but the file `bookmarklet.js` is not really used.
I don't see the point of it then.

Then, the actual bookmarklet is a condensed one-liner. Neither readable nor editable. The
condensed version should be generated on Betula startup somehow. The `bookmarklet.js` is read
by embed.FS on startup, so I think something could be done in this direction.

You could generate the bookmark URL client-side with some JS party tricks, see the link below.
I don't think it's a good idea.

=> https://stackoverflow.com/questions/7395686/how-can-i-serialize-a-function-in-javascript

Also, the bookmarklet uses the URL known at the moment the page was loaded. But the user can
change it one the same page! Having one URL written and a different one used doesn't really
make sense. I think it makes sense to put the bookmarklet on a different page, `/bookmarklet`.
There won't be such conflicts this way. It can be linked on the Settings page.

It's not like the bookmarklet is a setting really btw...