1angular.module('TocUI', []); 2 3/*Templates*/ 4angular.module("TocUI").run(["$templateCache",function ($templateCache) { 5 /*Modal Templates*/ 6 $templateCache.put('uiTocRoot.html', [ 7 '<div class="toc list-group">', 8 ' <div ng-repeat="row in $data.hierarchy" ui-toc-node="row"></div>', 9 '</div>' 10 ].join('')); 11 $templateCache.put('uiTocNode.html', [ 12 '<div>', 13 ' <a data-toggle="collapse" data-target="#menu-{{row.item._id}}" class="list-group-item" ng-class="{\'active\':row.item.active, \'active-path\':row.item.activePath}">', 14 ' <span ng-click="go()">{{row.item.title}}</span> <b class="caret" ng-if="row.child.length>0"></b>', 15 ' </a>', 16 ' <div class="list-group collapse" id="menu-{{row.item._id}}" ng-if="row.child.length>0" ng-class="{\'in\':row.item.active || row.item.activePath}">', 17 ' <div ng-repeat="item in row.child" ui-toc-node="item"></div>', 18 ' </div>', 19 '</div>' 20 ].join('')); 21}]); 22 23 24/* 25Panel 26*/ 27angular.module("TocUI").directive('uiToc', ['tocService', function (tocService) { 28 return { 29 restrict: 'A', 30 scope: { 31 selector:'@uiToc' 32 }, 33 templateUrl: 'uiTocRoot.html', 34 link: function (scope, element, attrs) { 35 tocService.setSelector(scope.selector); 36 scope.$data = tocService.data; 37 38 scope.$on('$locationChangeSuccess', function (event) { 39 $('.sidebar').removeClass('active'); 40 }); 41 } 42 }; 43}]); 44 45angular.module("TocUI").directive('uiTocNode', ['tocService', '$anchorScroll', '$location', function (tocService, $anchorScroll, $location) { 46 return { 47 restrict: 'A', 48 scope: { 49 row: '=uiTocNode' 50 }, 51 templateUrl: 'uiTocNode.html', 52 replace: true, 53 link: function (scope, element, attrs) { 54 scope.go = function () { 55 var newHash = scope.row.id; 56 if ($location.hash() !== newHash) { 57 // set the $location.hash to `newHash` and $anchorScroll will automatically scroll to it 58 $location.hash(newHash); 59 } else { 60 // call $anchorScroll() explicitly, since $location.hash hasn't changed 61 $anchorScroll(); 62 } 63 } 64 } 65 }; 66}]); 67 68angular.module("TocUI").directive('tocItem', ['tocService', function (tocService) { 69 return { 70 restrict: 'C', 71 scope: false, 72 link: function (scope, element, attrs) { 73 /*Fires TOC Update on create or destroy*/ 74 tocService.update(); 75 scope.$on('$destroy', function () { 76 tocService.remove(element); 77 }); 78 } 79 }; 80}]); 81 82angular.module("TocUI").service('tocService', ['$rootScope', function ($rootScope) { 83 var svc = { 84 _selector: 'body', 85 data: { 86 items: [], 87 hierarchy: [] 88 }, 89 activeItem: {}, 90 lastId: 0 91 }; 92 svc.setSelector = function (newVal) { 93 $(svc._selector).unbind('scroll'); 94 svc._selector = newVal; 95 $(svc._selector).bind('scroll', _.debounce(svc._onScroll, 100)); 96 }; 97 svc._update = function () { 98 svc.elements = $(svc._selector).find('.toc-item'); 99 svc.elements.each(function (i, ui) { 100 var el = $(ui), tocItem; 101 if (el.data('tocItem')) tocItem = el.data('tocItem'); 102 if (!tocItem) { 103 var tocItem = { _id: svc.lastId++ }; 104 if (!el.attr('id')) el.attr('id', 'toc-' + tocItem._id); 105 tocItem.id = el.attr('id'); 106 107 var parents = el.parents('.toc-item'); 108 if (parents) { 109 tocItem.parentId = parents.attr('id'); 110 } 111 112 /*find a suitable title*/ 113 if (!tocItem.title) tocItem.title = svc.findTextRecursive(el, function (el) { return el.attr('title'); }); 114 if (!tocItem.title) tocItem.title = svc.findTextRecursive(el, function (el) { return (el[0].nodeType === 3)?el.text():''; }); 115 tocItem.top = el.offset().top; 116 el.data('tocItem', tocItem); 117 svc.data.items.push(tocItem); 118 } 119 //Always update top positions because elements afftect eachother 120 tocItem.top = el.offset().top; 121 }); 122 123 //Rebuild Hierarchy 124 svc.data.hierarchy = svc.getChild(null); 125 svc._onScroll(); 126 $rootScope.$applyAsync(); 127 } 128 svc.update = _.debounce(svc._update, 100); 129 svc.remove = function (element) { 130 var el = $(element); 131 var id = el.attr('id'); 132 _.remove(svc.data.items, function (i) { return i.id == id; }); 133 svc.update(); 134 } 135 136 svc.findTextRecursive = function (element, fn) { 137 var title = fn(element); 138 if (title && title.match(/\S/)) return title; 139 var child = element.contents(); 140 for (var i = 0; i < child.length; i++) { 141 var t = svc.findTextRecursive($(child[i]),fn); 142 if (t != '') return t; 143 } 144 return ''; 145 }; 146 svc.getChild = function (parentId) { 147 return _(svc.data.items) 148 .filter(function (i) { return i.parentId == parentId; }) 149 .sortBy('top') 150 .map(function (i) { 151 return { 152 id: i.id, 153 item: i, 154 child: svc.getChild(i.id) 155 }; 156 }) 157 .value(); 158 } 159 160 //Start watching for scroll events 161 svc._onScroll = function () { 162 if (svc.elements) { 163 if (svc.activeItem) svc.activeItem.active = false; 164 var pageTop = $(svc._selector).offset().top;//$(window).scrollTop(); 165 var pageBottom = $(window).height() - pageTop;// pageTop + $(window).height(); 166 var activeItems = [], lastElementBottom=0; 167 for (var i = 0; i < svc.elements.length; i++) { 168 var element = $(svc.elements[i]); 169 var elementTop = $(element).offset().top; 170 var elementBottom = elementTop + $(element).height(); 171 if (lastElementBottom < elementBottom) lastElementBottom = elementBottom; 172 var item = _.find(svc.data.items, function (o) { return o.id == element.attr('id'); }) || {}; 173 item.top = elementTop; 174 if ((pageTop < elementBottom) && (pageBottom > elementTop)) { 175 activeItems.push(item); 176 } 177 } 178 var newActiveItem = activeItems[0]; 179 var positionTarget = pageTop;//$(svc._selector).height() * $(svc._selector).scrollTop() / ($(svc._selector).offset().top + $(svc._selector).scrollTop() + lastElementBottom - $(svc._selector).height()); 180 //Select the item clostst to the top 181 _.forEach(activeItems, function (i) { if (Math.abs(i.top - positionTarget) < Math.abs(newActiveItem.to
181p - positionTarget)) newActiveItem = i; }); 182 if (newActiveItem != svc.activeItem) { 183 svc.activeItem = newActiveItem; 184 svc.clearPath(); 185 if (svc.activeItem) { 186 svc.activeItem.active = true; 187 svc.updatePath(svc.activeItem); 188 } 189 $rootScope.$applyAsync(); 190 } 191 192 } 193 } 194 svc.clearPath = function () { 195 _.forEach(svc.data.items, function (i) { i.activePath=false; }); 196 } 197 svc.updatePath = function (item){ 198 item.activePath = true; 199 var parent = _.find(svc.data.items, function (o) { return o.id == item.parentId; }); 200 if (parent) svc.updatePath(parent); 201 } 202 203 return svc; 204}]);
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.