Skip to content

Commit

Permalink
Add HTML-to- dream-html import bookmarklet
Browse files Browse the repository at this point in the history
  • Loading branch information
yawaramin committed Jan 21, 2024
1 parent 972926c commit 3a29f82
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 0 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,25 @@ utop # p [class_ "hello"] [txt "world"];;
- : node = <p class="hello">world</p>
```

## Import HTML

One issue that you may come across is that the syntax of HTML is different from
the syntax of dream-html markup. To ease this problem, you may use the
bookmarklet `import_html.js` provided in this project. Simply create a new
bookmark in your browser with any name, and set the URL to the content of that
file (make sure it is exactly the given content).

Then, whenever you have a web page open, just click on the bookmarklet to copy
its markup to the clipboard in dream-html format. From there you can simple
paste it into your project.

Note that the dream-html version is not formatted nicely, because the
expectation is that you will use ocamlformat to fix the formatting.

Also note that the translation done by this bookmarklet is on a best-effort
basis. Many web pages don't strictly conform to the rules of correct HTML
markup, so you will likely need to fix those issues for your build to work.

## Test

Run the test and print out diff if it fails:
Expand Down
112 changes: 112 additions & 0 deletions import_html.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
javascript: (async () => {
/* Copyright 2024 Yawar Amin
This file is part of dream-html.
dream-html is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option) any
later version.
dream-html is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along with
dream-html. If not, see <https://www.gnu.org/licenses/>. */

const suffixAttrs = [
'cite', 'class', 'data', 'for', 'form', 'label', 'method', 'object', 'open',
'slot', 'span', 'style', 'title', 'type',
];
const polyVarAttrs = [
'autocapitalize', 'autocomplete', 'capture', 'crossorigin', 'decoding', 'dir',
'enctype', 'fetchpriority', 'formenctype', 'formmethod', 'hidden',
'http_equiv', 'inputmode', 'kind', 'low', 'method', 'preload',
'referrerpolicy', 'translate', 'wrap',
];
const intAttrs = [
'cols', 'colspan', 'maxlength', 'minlength', 'rows', 'rowspan', 'span',
'start', 'tabindex',
];
const boolAttrs = [
'async', 'autofocus', 'autoplay', 'checked', 'controls', 'default', 'defer',
'disabled', 'draggable', 'formnovalidate', 'ismap', 'loop', 'multiple',
'muted', 'novalidate', 'open', 'playsinline', 'readonly', 'required',
'reversed', 'selected',
];
const voidTags = [
'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta',
'source', 'track', 'wbr',
];
const textTags = ['option', 'script', 'style', 'textarea', 'title'];

const attr = name => {
if (suffixAttrs.indexOf(name) > -1) return name + '_';
if (name.indexOf('-') > -1) return 'string_attr "' + name + '"';
return name;
};

const polyvar = v =>
'`' + (v == 'get' || v == 'post' ? v.toUpperCase() : v).replaceAll('-', '_');

const stringify = (v, nm = '') => {
if (v == null) return '';
if (polyVarAttrs.indexOf(nm) > -1) return polyvar(v);
if (intAttrs.indexOf(nm) > -1) return v;
if (boolAttrs.indexOf(nm) > -1) return '';
if (v.indexOf('"') > -1) return '{|' + v + '|}';
return '"' + v + '"';
};

let res = '';

const writeTag = t => {
switch (t.nodeType) {
case Node.COMMENT_NODE:
res += 'comment ';
res += stringify(t.data);
break;

case Node.TEXT_NODE:
res += 'txt ';
res += stringify(t.data);
break;

case Node.ELEMENT_NODE:
const name = t.tagName.toLowerCase();
res += name;

res += ' [';
for (const a of t.attributes) {
res += attr(a.name);
res += ' ';
res += stringify(a.value, a.name);
res += '; ';
}
res += '] ';

if (voidTags.indexOf(name) > -1) {
/* do nothing */
} else if (textTags.indexOf(name) > -1) {
res += stringify(t.innerText);
} else {
res += '[';
for (const child of t.childNodes) {
res += '\n';
writeTag(child);
res += ';';
}
res += ']';
}
break;

default:
console.warn(t.nodeType);
}
};

writeTag(document.querySelector('html'));
await navigator.clipboard.writeText(res);
})()

0 comments on commit 3a29f82

Please sign in to comment.