|
|
@@ -0,0 +1,116 @@
|
|
|
+/*
|
|
|
+
|
|
|
+CollapsibleLists.js
|
|
|
+
|
|
|
+An object allowing lists to dynamically expand and collapse
|
|
|
+
|
|
|
+Created by Kate Morley - http://code.iamkate.com/ - and released under the terms
|
|
|
+of the CC0 1.0 Universal legal code:
|
|
|
+
|
|
|
+http://creativecommons.org/publicdomain/zero/1.0/legalcode
|
|
|
+
|
|
|
+*/
|
|
|
+
|
|
|
+const CollapsibleLists = (function(){
|
|
|
+
|
|
|
+ // Makes all lists with the class 'collapsibleList' collapsible. The
|
|
|
+ // parameter is:
|
|
|
+ //
|
|
|
+ // doNotRecurse - true if sub-lists should not be made collapsible
|
|
|
+ function apply(doNotRecurse){
|
|
|
+
|
|
|
+ [].forEach.call(document.getElementsByTagName('ul'), node => {
|
|
|
+
|
|
|
+ if (node.classList.contains('collapsibleList')){
|
|
|
+
|
|
|
+ applyTo(node, true);
|
|
|
+
|
|
|
+ if (!doNotRecurse){
|
|
|
+
|
|
|
+ [].forEach.call(node.getElementsByTagName('ul'), subnode => {
|
|
|
+ subnode.classList.add('collapsibleList')
|
|
|
+ });
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ })
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // Makes the specified list collapsible. The parameters are:
|
|
|
+ //
|
|
|
+ // node - the list element
|
|
|
+ // doNotRecurse - true if sub-lists should not be made collapsible
|
|
|
+ function applyTo(node, doNotRecurse){
|
|
|
+
|
|
|
+ [].forEach.call(node.getElementsByTagName('li'), li => {
|
|
|
+
|
|
|
+ if (!doNotRecurse || node === li.parentNode){
|
|
|
+
|
|
|
+ li.style.userSelect = 'none';
|
|
|
+ li.style.MozUserSelect = 'none';
|
|
|
+ li.style.msUserSelect = 'none';
|
|
|
+ li.style.WebkitUserSelect = 'none';
|
|
|
+
|
|
|
+ li.addEventListener('click', handleClick.bind(null, li));
|
|
|
+
|
|
|
+ toggle(li);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ });
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // Handles a click. The parameter is:
|
|
|
+ //
|
|
|
+ // node - the node for which clicks are being handled
|
|
|
+ function handleClick(node, e){
|
|
|
+
|
|
|
+ let li = e.target;
|
|
|
+ while (li.nodeName !== 'LI'){
|
|
|
+ li = li.parentNode;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (li === node){
|
|
|
+ toggle(node);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // Opens or closes the unordered list elements directly within the
|
|
|
+ // specified node. The parameter is:
|
|
|
+ //
|
|
|
+ // node - the node containing the unordered list elements
|
|
|
+ function toggle(node){
|
|
|
+
|
|
|
+ const open = node.classList.contains('collapsibleListClosed');
|
|
|
+ const uls = node.getElementsByTagName('ul');
|
|
|
+
|
|
|
+ [].forEach.call(uls, ul => {
|
|
|
+
|
|
|
+ let li = ul;
|
|
|
+ while (li.nodeName !== 'LI'){
|
|
|
+ li = li.parentNode;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (li === node){
|
|
|
+ ul.style.display = (open ? 'block' : 'none');
|
|
|
+ }
|
|
|
+
|
|
|
+ });
|
|
|
+
|
|
|
+ node.classList.remove('collapsibleListOpen');
|
|
|
+ node.classList.remove('collapsibleListClosed');
|
|
|
+
|
|
|
+ if (uls.length > 0){
|
|
|
+ node.classList.add('collapsibleList' + (open ? 'Open' : 'Closed'));
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return {apply, applyTo};
|
|
|
+
|
|
|
+})();
|