├── 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 |
  1. This is a normal comment.
  2. 45 |
  3. This is a marginalia-comment.
  4. 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 |
  1. This is a marginalia-comment.
  2. 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 |
  1. This is a marginalia-comment.
  2. 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