├── LICENSE
├── README.md
├── marginalia.js
└── marginalia.min.js
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Kartik Prabhu
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Marginalia
2 |
3 | Marginalia (or annotations) are comments addressed to a particular part of a webpage (or blog post) instead of the entire page. This is a javascript to implement an indieweb-style marginalia system using [fragmention](http://www.kevinmarks.com/fragmentions.html). See example post using this code: [Marginalia](https://www.kartikprabhu.com/article/marginalia)
4 |
5 | ## Customisation
6 |
7 | The script has a few variables in the beginning that you can set to customise some of the HTML added by marginalia.js. These are:
8 |
9 | ```CONTAINER_EXTRA_CLASS``` is a space-separated string of classes you want to add to the container of the marginalia. defaults to ```""``` empty string.
10 |
11 | ```BTN_EXTRA_CLASS``` is a space-separated string of classes you want to add to the button toggling the marginlaia. defaults to ```""``` empty string.
12 |
13 | ```BTN_HTML_SHOW``` is the HTML inside the button element when marginalia is hidden. defaults to ```"Show"```.
14 |
15 | ```BTN_HTML_HIDE``` is the HTML inside the button element when marginalia is visible. defaults to ```"Hide"```.
16 |
17 |
18 | ## Usage Example
19 |
20 | The marginalia script uses certain mark-up in the HTML to identify marginalia. The container for all comments should have an ```id=response-list ``` while each marginalia-comment element should have a data attribute containing the fragmention. The next example uses the custom variables ```CONTAINER_EXTRA_CLASS = "my-container"``` and ```BTN_EXTRA_CLASS = "my-button"```:
21 |
22 | ```html
23 |
24 |
One morning, when Gregor Samsa woke from troubled dreams, he found
25 | himself transformed in his bed into a horrible vermin. He lay on
26 | his armour-like back, and if he lifted his head a little he could
27 | see his brown belly, slightly domed and divided by arches into stiff
28 | sections. The bedding was hardly able to cover it and seemed ready
29 | to slide off any moment. His many legs, pitifully thin compared
30 | with the size of the rest of him, waved about helplessly as he
31 | looked.
32 |
33 |
"What's happened to me?" he thought. It wasn't a dream. His room,
34 | a proper human room although a little too small, lay peacefully
35 | between its four familiar walls. A collection of textile samples
36 | lay spread out on the table - Samsa was a travelling salesman - and
37 | above it there hung a picture that he had recently cut out of an
38 | illustrated magazine and housed in a nice, gilded frame. It showed
39 | a lady fitted out with a fur hat and fur boa who sat upright,
40 | raising a heavy fur muff that covered the whole of her lower arm
41 | towards the viewer.
42 |
43 |
44 |
This is a normal comment.
45 |
This is a marginalia-comment.
46 |
47 |
48 | ```
49 |
50 | On execution, the marginalia.js _moves_ the marginalia-comments _right after_ the appropriate element inside a container of the same tag name (in this case ```ol```) with ``` class="marginalia-list" ```
51 |
52 | ```html
53 |
54 |
This is a marginalia-comment.
55 |
56 | ```
57 |
58 | It also adds a button to toggle marginalia visibility _inside_ the referenced element. The button is marked up as:
59 |
60 | ```html
61 |
62 | ```
63 | when the marginalia is hidden and with text "Hide" when marginalia are visible. So the relevant paragraph looks like this:
64 |
65 | ```html
66 |
One morning, when Gregor Samsa woke from troubled dreams, he found
67 | himself transformed in his bed into a horrible vermin. He lay on
68 | his armour-like back, and if he lifted his head a little he could
69 | see his brown belly, slightly domed and divided by arches into stiff
70 | sections. The bedding was hardly able to cover it and seemed ready
71 | to slide off any moment. His many legs, pitifully thin compared
72 | with the size of the rest of him, waved about helplessly as he
73 | looked.
74 |
75 |
This is a marginalia-comment.
76 |
77 | ```
78 |
79 | On click the button adds an attribute ``` data-marginalia-active ``` to the parent element like so ```
One morning, when Gregor Samsa...
```
80 |
81 | All styling can be done using these classes as CSS hooks.
82 |
83 |
84 |
85 |
86 | ## Go forth
87 |
88 | Now [use this yourself](https://github.com/kartikprabhu/marginalia) and [give feedback](https://github.com/kartikprabhu/marginalia/issues).
89 |
--------------------------------------------------------------------------------
/marginalia.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | (function(){
4 |
5 | // mustard-cutting
6 | if (!document.querySelector || !document.querySelectorAll) {
7 | return;
8 | }
9 |
10 | var
11 | CONTAINER_EXTRA_CLASS = "",
12 | BTN_EXTRA_CLASS = "",
13 | BTN_HTML_SHOW = "Show",
14 | BTN_HTML_HIDE = "Hide";
15 |
16 | function insertAfter(referenceNode, newNode) {
17 | referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
18 | }
19 |
20 | function getElementsByText(scope, text) {
21 | // iterate descendants of scope
22 | for (var all = scope.childNodes, index = 0, element, list = []; (element = all[index]); ++index) {
23 | // conditionally return element containing visible, whitespace-insensitive, case-sensitive text (a match)
24 | if (element.nodeType == 1 && (element.innerText || element.textContent || '').replace(/\s+/g, ' ').indexOf(text) !== -1) {
25 | list = list.concat(getElementsByText(element, text));
26 | }
27 | }
28 |
29 | // return scope (no match)
30 | return list.length ? list : scope;
31 | }
32 |
33 | function toggle_marginalia(el, button) {
34 |
35 | var attr = 'data-marginalia-active';
36 |
37 | var matches = document.querySelectorAll("[data-marginalia-active]");
38 |
39 | for(var i = 0, node; node = matches[i]; i++) {
40 | if (node != el) {
41 | node.removeAttribute(attr);
42 | var btn = node.querySelector(".marginalia-button");
43 | btn.innerHTML = BTN_HTML_SHOW;
44 | }
45 | }
46 |
47 | if (el.hasAttribute(attr)) {
48 | // if active make inactive
49 | el.removeAttribute(attr);
50 | button.innerHTML = BTN_HTML_SHOW;
51 |
52 | }else{
53 | // if inactive make active
54 | el.setAttribute(attr, '');
55 | button.innerHTML = BTN_HTML_HIDE;
56 | }
57 |
58 | }
59 |
60 | function add_marginalia(element, note) {
61 |
62 | var ol = element.nextElementSibling || false;
63 |
64 | if (!ol || !ol.classList.contains("marginalia-list")) {
65 |
66 | ol = document.createElement(CONTAINER_NAME);
67 | ol.setAttribute('class', 'marginalia-list '+CONTAINER_EXTRA_CLASS);
68 |
69 | var button = document.createElement('button');
70 | button.setAttribute('class', 'marginalia-button '+BTN_EXTRA_CLASS);
71 | button.innerHTML = BTN_HTML_SHOW;
72 |
73 | button.addEventListener('click', function() {toggle_marginalia(element, button);}, false)
74 |
75 | element.appendChild( button );
76 | insertAfter(element, ol);
77 |
78 | }
79 | ol.appendChild(note);
80 | }
81 |
82 |
83 | function process_marginalia(el) {
84 |
85 | var frag = el.getAttribute("data-fragmention")
86 |
87 | // if fragmention exists
88 | if (frag) {
89 |
90 | // decode frag to string
91 | var match = decodeURIComponent(frag.substring(2)).replace(/\+/g, ' ').split(' ');
92 | frag = match[0];
93 | var fragIndex = parseFloat(match[1]) || 0;
94 |
95 | // search for element with that string
96 | var
97 | // get all elements containing text (or document)
98 | elements = getElementsByText(document, frag),
99 | // get total number of elements
100 | length = elements.length,
101 | // get index of element
102 | modulus = length && fragIndex % length,
103 | index = length && modulus >= 0 ? modulus : length + modulus;
104 |
105 | // get element
106 | var element = length && elements[index];
107 |
108 | // do marginalia things if not whole document
109 | if (element && element != document) {
110 |
111 | add_marginalia(element, el);
112 |
113 | }
114 |
115 | }
116 |
117 | }
118 |
119 |
120 | // get all responses and process them
121 | var container = document.querySelector('#response-list');
122 | //if container for responses is found then process
123 | if (container) {
124 | var CONTAINER_NAME = container.tagName;
125 | var responses = container.querySelectorAll('[data-fragmention]');
126 |
127 | for (var i = 0; i < responses.length; i++) {
128 |
129 | process_marginalia(responses[i]);
130 |
131 | }
132 | }
133 | })();
134 |
--------------------------------------------------------------------------------
/marginalia.min.js:
--------------------------------------------------------------------------------
1 | (function(){function k(d,b){for(var a=d.childNodes,c=0,f,g=[];f=a[c];++c)1==f.nodeType&&-1!==(f.innerText||f.textContent||"").replace(/\s+/g," ").indexOf(b)&&(g=g.concat(k(f,b)));return g.length?g:d}function l(d,b){var a=d.nextElementSibling||!1;if(!a||!a.classList.contains("marginalia-list")){a=document.createElement(m);a.setAttribute("class","marginalia-list ");var c=document.createElement("button");c.setAttribute("class","marginalia-button ");c.innerHTML="Show";c.addEventListener("click",
2 | function(){for(var a=document.querySelectorAll("[data-marginalia-active]"),b=0,e;e=a[b];b++)e!=d&&(e.removeAttribute("data-marginalia-active"),e.querySelector(".marginalia-button").innerHTML="Show");d.hasAttribute("data-marginalia-active")?(d.removeAttribute("data-marginalia-active"),c.innerHTML="Show"):(d.setAttribute("data-marginalia-active",""),c.innerHTML="Hide")},!1);d.appendChild(c);d.parentNode.insertBefore(a,d.nextSibling)}a.appendChild(b)}function n(d){var b=
3 | d.getAttribute("data-fragmention");if(b){var a=decodeURIComponent(b.substring(2)).replace(/\+/g," ").split(" "),b=a[0],a=parseFloat(a[1])||0,b=k(document,b),c=b.length,a=c&&a%c;(a=c&&b[c&&0<=a?a:c+a])&&a!=document&&l(a,d)}}for(var e=document.querySelector("#response-list"),m=e.tagName,e=e.querySelectorAll("[data-fragmention]"),h=0;h