1'use strict'; 2 3/** 4 * The Collapser object can have mutliple instances, 5 * and should work with any nested ul/li... 6 * 7 * This function is used in both the loader and baseapp - so if you make 8 * changes, be sure to check both for potential problems. 9 * 10 * Example structure: 11 * 12 * <div class="taxonomyTree"> 13 * <ul> 14 * <li> 15 * <a class="toggle"> 16 * <a class="taxonomyNode" id="0001">My Node's Wonderful Name</a> 17 * <ul> 18 * <li> 19 * ... 20 * </li> 21 * <li> 22 * </li> 23 * </ul> 24 * </li> 25 * <li> 26 * ... 27 * </li> 28 * </ul> 29 * </div> 30 */ 31(function ($) { 32 var COLLAPSER = function COLLAPSER(collapser, shutAll, openAll) { 33 var c = this.collapser = collapser = $(collapser); 34 var CPR = this; 35 36 shutAll = shutAll === undefined ? true : shutAll; // make sure this is set if it is not passed 37 openAll = openAll === undefined ? false : openAll; // make sure this is set if it is not passed 38 39 // if there are any open/close all links 40 c.on('click', '.openAll a', function () { 41 CPR.openAll(); 42 return false; 43 }); 44 45 c.on('click', '.shutAll a', function () { 46 CPR.shutAll(); 47 return false; 48 }); 49 50 c.addClass('collapser'); 51 52 var a = this.allLis = c.find("ul,[data-toc-role='ul']").not('.actions-list').parentsUntil(c, "li,[data-toc-role='li']"); 53 54 if (a.length == 0) { 55 c.addClass('collapser-flat'); // signifies that a collapser was added, but it is flat (eg. no expandable/collapsable nodes) 56 return; // nothing left to do, there are no expandable elements 57 } 58 59 if (CPR.getToggle(a).length > 0) { 60 c.addClass('collapser-with-toggles'); 61 } else { 62 c.addClass('collapser-without-toggles'); 63 } 64 65 // attach the listener to the <li> expandable tag 66 a.each(function () { 67 var li = $(this); 68 if (!li.data('COLLAPSER')) { 69 var handleToggle = function handleToggle(event, origin) { 70 var t = $(this != null ? this : origin); // this 71 // event target 72 var tg = $(event.target); 73 // event target node name 74 var tgnn = tg.prop('nodeName'); 75 var toggle = CPR.getToggle(t); 76 // closest expandable li 77 var cli = tg.closest("li.expandable,[data-toc-role='li'].expandable"); 78 // the closests "stop" tag, meaning that if they are clicked, stop propagtion... TODO: make this overridable in a plugin! 79 var cstop = tg.closest("input, label, a, li:not(.expandable),[data-toc-role='li']:not(.expandable)"); 80 // the distance to the closest LI 81 var dcli = tg.parentsUntil(cli).length; 82 // the distance to the closest "stop" tag. 83 var dcstop = tg.parentsUntil(cstop).length; 84 85 // this is necessary so that if an "a" tag (for example) is within a collapser, it doesn't also collapse/expand the collapser! 86 if ( 87 // there is no expandable section, so give up 88 !cli || 89 // if the target is also the closest stop element... but it is not explicitly a toggle, then we need to stop propagation! 90 tg.is(cstop) && !tg.is(toggle) || 91 // if somewhere between the target and the collapsing element, there is a stop element, then don't collapse either 92 dcstop < dcli && !cstop.is(toggle)) { 93 event.stopPropagation(); 94 // if the closest li is not an expandable one, or if what was clicked was a link, stop propagation 95 return; 96 } 97 98 CPR.toggleSection(t); 99 return false; 100 }; 101 102 li.data('COLLAPSER', c); 103 var toggleBtn = CPR.getToggle(li); 104 if (toggleBtn) { 105 toggleBtn.on('click', function (event) { 106 if (li.hasClass('expandable')) { 107 if (li.hasClass('open')) { 108 toggleBtn.removeClass('c-Icon--remove').addClass('c-Icon--add'); 109 } else { 110 toggleBtn.removeClass('c-Icon--add').addClass('c-Icon--remove'); 111 } 112 } 113 }); 114 } 115 116 li.on('click', handleToggle); 117 li.on('keyup', function (event) { 118 if (event.key == "Enter") { 119 // Enter key 120 return handleToggle(event, this); 121 } 122 }); 123 } 124 }); 125 126 a.addClass('expandable'); 127 if (shutAll) { 128 CPR.shutAll(); 129 c.find("li.active-section,[data-toc-role='li'].active-section").each(function (li) { 130 CPR.openSection(this); 131 }); 132 } else if (openAll) { 133 CPR.openAll(); 134 } else { 135 // we have to make sure that if it is not "open" or "shut", we set them 136 CPR.openSection(a.not('shut').not('open')); 137 } 138 }; 139 140 var CP = COLLAPSER.prototype; 141 142 CP.openAll = function () { 143 this.openSection(this.allLis); 144 }; 145 146 CP.shutAll = function () { 147 this.shutSection(this.allLis); 148 }; 149 150 CP.setSection = function (section, addClass, removeClass, text) { 151 section = $(section); 152 var toggle = this.getToggle(section); 153 var span = toggle.find('span').first(); 154 155 section.addClass(addClass).removeClass(removeClass); 156 157 // The toggle is the reverse state of the parent 158 if (addClass === 'open') { 159 toggle.addClass('ico-toggle-' + removeClass + ' c-Icon--remove').removeClass('ico-toggle-' + addClass + ' c-Icon--add'); 160 } else { 161 toggle.addClass('ico-toggle-' + removeClass + ' c-Icon--add').removeClass('ico-toggle-' + addClass + ' c-Icon--remove'); 162 } 163 span.html(text); 164 }; 165 166 CP.openSection = function (section) { 167 this.setSection(section, 'open', 'shut', 'Close section'); 168 }; 169 170 CP.shutSection = function (section) { 171 this.setSection(section, 'shut', 'open', 'Open section'); 172 }; 173 174 CP.getToggle = function (section) { 175 return section.find('.toggle:first'); 176 }; 177 178 CP.toggleSection = function (section) { 179 var s = $(section); 180 if (s.hasClass('open')) { 181 this.shutSection(s); 182 } else { 183 this.openSection(s); 184 } 185 }; 186 187 $(function () { 188 $('.taxonomyTree').each(function () { 189 var t = $(this), 190 s = t.hasClass('all-shut'), 191 o = t.hasClass('all-open'), 192 c = new COLLAPSER(this, s, o); 193 c.openSection($(this).find('.active').closest("li,[data-toc-role='li']").parents('.expandable')); 194 var t = $(this); 195 if (t.hasClass('relatedTaxonomyTree')) { 196 c.openAll(); 197 } 198 $.data(this, 'COLLAPSER', c); 199 }); 200 201 $('.toc-menu').each(function () { 202 if ($(this).find('.issue-toc').length <= 0) { 203 //Do not add collapser to issue toc main content 204 var t = $(this), 205 s = t.hasClass('all-shut'), 206 c = new COLLAPSER(this, s); 207 208 if (t.find('.book-toc').length > 0) { 209 // this is book TOCs...
210 //t.find(".expandable").prepend("<a class='toggle minustoggle'><span>toggle</span></a>"); 211 c.shutSection(t.find("span:contains('Front Matter')").parents('.expandable')); 212 c.shutSection(t.find("span:contains('Back Matter')").parents('.expandable')); 213 } else if (t.find('.journal-toc').length > 0) { 214 // journal TOCs 215 var curr = t.find('.current'); 216 c.openSection(curr.closest("li,[data-toc-role='li']").parents('.expandable')); 217 // By default, always expand the first Volume if no other volumes are selected 218 if (!curr) { 219 c.openSection(t.find('.js-first').closest("li,[data-toc-role='li']")); 220 } 221 } 222 c.openSection($(this).find('.active').closest("li,[data-toc-role='li']").parents('.expandable')); 223 224 $.data(this, 'COLLAPSER', c); 225 } 226 }); 227 }); 228})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.