[Doc] Update template with docfx v2.37.2
[platform/core/csapi/tizenfx.git] / docs / template / tizen / styles / docfx.js
1 // Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.
2 $(function () {
3   var active = 'active';
4   var expanded = 'in';
5   var collapsed = 'collapsed';
6   var filtered = 'filtered';
7   var show = 'show';
8   var hide = 'hide';
9   var util = new utility();
10
11   workAroundFixedHeaderForAnchors();
12   highlight();
13   enableSearch();
14
15   renderTables();
16   renderAlerts();
17   renderLinks();
18   renderNavbar();
19   renderSidebar();
20   renderAffix();
21   renderFooter();
22   renderLogo();
23
24   breakText();
25   renderTabs();
26
27   window.refresh = function (article) {
28     // Update markup result
29     if (typeof article == 'undefined' || typeof article.content == 'undefined')
30       console.error("Null Argument");
31     $("article.content").html(article.content);
32
33     highlight();
34     renderTables();
35     renderAlerts();
36     renderAffix();
37     renderTabs();
38   }
39
40   // Add this event listener when needed
41   // window.addEventListener('content-update', contentUpdate);
42
43   function breakText() {
44     $(".xref").addClass("text-break");
45     var texts = $(".text-break");
46     texts.each(function () {
47       $(this).breakWord();
48     });
49   }
50
51   // Styling for tables in conceptual documents using Bootstrap.
52   // See http://getbootstrap.com/css/#tables
53   function renderTables() {
54     $('table').addClass('table table-bordered table-striped table-condensed').wrap('<div class=\"table-responsive\"></div>');
55   }
56
57   // Styling for alerts.
58   function renderAlerts() {
59     $('.NOTE, .TIP').addClass('alert alert-info');
60     $('.WARNING').addClass('alert alert-warning');
61     $('.IMPORTANT, .CAUTION').addClass('alert alert-danger');
62   }
63
64   // Enable anchors for headings.
65   (function () {
66     anchors.options = {
67       placement: 'left',
68       visible: 'touch'
69     };
70     anchors.add('article h2:not(.no-anchor), article h3:not(.no-anchor), article h4:not(.no-anchor)');
71   })();
72
73   // Open links to different host in a new window.
74   function renderLinks() {
75     if ($("meta[property='docfx:newtab']").attr("content") === "true") {
76       $(document.links).filter(function () {
77         return this.hostname !== window.location.hostname;
78       }).attr('target', '_blank');
79     }
80   }
81
82   // Enable highlight.js
83   function highlight() {
84     $('pre code').each(function (i, block) {
85       hljs.highlightBlock(block);
86     });
87     $('pre code[highlight-lines]').each(function (i, block) {
88       if (block.innerHTML === "") return;
89       var lines = block.innerHTML.split('\n');
90
91       queryString = block.getAttribute('highlight-lines');
92       if (!queryString) return;
93
94       var ranges = queryString.split(',');
95       for (var j = 0, range; range = ranges[j++];) {
96         var found = range.match(/^(\d+)\-(\d+)?$/);
97         if (found) {
98           // consider region as `{startlinenumber}-{endlinenumber}`, in which {endlinenumber} is optional
99           var start = +found[1];
100           var end = +found[2];
101           if (isNaN(end) || end > lines.length) {
102             end = lines.length;
103           }
104         } else {
105           // consider region as a sigine line number
106           if (isNaN(range)) continue;
107           var start = +range;
108           var end = start;
109         }
110         if (start <= 0 || end <= 0 || start > end || start > lines.length) {
111           // skip current region if invalid
112           continue;
113         }
114         lines[start - 1] = '<span class="line-highlight">' + lines[start - 1];
115         lines[end - 1] = lines[end - 1] + '</span>';
116       }
117
118       block.innerHTML = lines.join('\n');
119     });
120   }
121
122   // Support full-text-search
123   function enableSearch() {
124     var query;
125     var relHref = $("meta[property='docfx\\:rel']").attr("content");
126     if (typeof relHref === 'undefined') {
127       return;
128     }
129     try {
130       var worker = new Worker(relHref + 'styles/search-worker.js');
131       if (!worker && !window.worker) {
132         localSearch();
133       } else {
134         webWorkerSearch();
135       }
136
137       renderSearchBox();
138       highlightKeywords();
139       addSearchEvent();
140     } catch (e) {
141       console.error(e);
142     }
143
144     //Adjust the position of search box in navbar
145     function renderSearchBox() {
146       autoCollapse();
147       $(window).on('resize', autoCollapse);
148       $(document).on('click', '.navbar-collapse.in', function (e) {
149         if ($(e.target).is('a')) {
150           $(this).collapse('hide');
151         }
152       });
153
154       function autoCollapse() {
155         var navbar = $('#autocollapse');
156         if (navbar.height() === null) {
157           setTimeout(autoCollapse, 300);
158         }
159         navbar.removeClass(collapsed);
160         if (navbar.height() > 60) {
161           navbar.addClass(collapsed);
162         }
163       }
164     }
165
166     // Search factory
167     function localSearch() {
168       console.log("using local search");
169       var lunrIndex = lunr(function () {
170         this.ref('href');
171         this.field('title', { boost: 50 });
172         this.field('keywords', { boost: 20 });
173       });
174       lunr.tokenizer.seperator = /[\s\-\.]+/;
175       var searchData = {};
176       var searchDataRequest = new XMLHttpRequest();
177
178       var indexPath = relHref + "index.json";
179       if (indexPath) {
180         searchDataRequest.open('GET', indexPath);
181         searchDataRequest.onload = function () {
182           if (this.status != 200) {
183             return;
184           }
185           searchData = JSON.parse(this.responseText);
186           for (var prop in searchData) {
187             if (searchData.hasOwnProperty(prop)) {
188               lunrIndex.add(searchData[prop]);
189             }
190           }
191         }
192         searchDataRequest.send();
193       }
194
195       $("body").bind("queryReady", function () {
196         var hits = lunrIndex.search(query);
197         var results = [];
198         hits.forEach(function (hit) {
199           var item = searchData[hit.ref];
200           results.push({ 'href': item.href, 'title': item.title, 'keywords': item.keywords });
201         });
202         handleSearchResults(results);
203       });
204     }
205
206     function webWorkerSearch() {
207       console.log("using Web Worker");
208       var indexReady = $.Deferred();
209
210       worker.onmessage = function (oEvent) {
211         switch (oEvent.data.e) {
212           case 'index-ready':
213             indexReady.resolve();
214             break;
215           case 'query-ready':
216             var hits = oEvent.data.d;
217             handleSearchResults(hits);
218             break;
219         }
220       }
221
222       indexReady.promise().done(function () {
223         $("body").bind("queryReady", function () {
224           worker.postMessage({ q: query });
225         });
226         if (query && (query.length >= 3)) {
227           worker.postMessage({ q: query });
228         }
229       });
230     }
231
232     // Highlight the searching keywords
233     function highlightKeywords() {
234       var q = url('?q');
235       if (q !== null) {
236         var keywords = q.split("%20");
237         keywords.forEach(function (keyword) {
238           if (keyword !== "") {
239             $('.data-searchable *').mark(keyword);
240             $('article *').mark(keyword);
241           }
242         });
243       }
244     }
245
246     function addSearchEvent() {
247       $('body').bind("searchEvent", function () {
248         $('#search-query').keypress(function (e) {
249           return e.which !== 13;
250         });
251
252         $('#search-query').keyup(function () {
253           query = $(this).val();
254           if (query.length < 3) {
255             flipContents("show");
256           } else {
257             flipContents("hide");
258             $("body").trigger("queryReady");
259             $('#search-results>.search-list').text('Search Results for "' + query + '"');
260           }
261         }).off("keydown");
262       });
263     }
264
265     function flipContents(action) {
266       if (action === "show") {
267         $('.hide-when-search').show();
268         $('#search-results').hide();
269       } else {
270         $('.hide-when-search').hide();
271         $('#search-results').show();
272       }
273     }
274
275     function relativeUrlToAbsoluteUrl(currentUrl, relativeUrl) {
276       var currentItems = currentUrl.split(/\/+/);
277       var relativeItems = relativeUrl.split(/\/+/);
278       var depth = currentItems.length - 1;
279       var items = [];
280       for (var i = 0; i < relativeItems.length; i++) {
281         if (relativeItems[i] === '..') {
282           depth--;
283         } else if (relativeItems[i] !== '.') {
284           items.push(relativeItems[i]);
285         }
286       }
287       return currentItems.slice(0, depth).concat(items).join('/');
288     }
289
290     function extractContentBrief(content) {
291       var briefOffset = 512;
292       var words = query.split(/\s+/g);
293       var queryIndex = content.indexOf(words[0]);
294       var briefContent;
295       if (queryIndex > briefOffset) {
296         return "..." + content.slice(queryIndex - briefOffset, queryIndex + briefOffset) + "...";
297       } else if (queryIndex <= briefOffset) {
298         return content.slice(0, queryIndex + briefOffset) + "...";
299       }
300     }
301
302     function handleSearchResults(hits) {
303       var numPerPage = 10;
304       $('#pagination').empty();
305       $('#pagination').removeData("twbs-pagination");
306       if (hits.length === 0) {
307         $('#search-results>.sr-items').html('<p>No results found</p>');
308       } else {
309         $('#pagination').twbsPagination({
310           totalPages: Math.ceil(hits.length / numPerPage),
311           visiblePages: 5,
312           onPageClick: function (event, page) {
313             var start = (page - 1) * numPerPage;
314             var curHits = hits.slice(start, start + numPerPage);
315             $('#search-results>.sr-items').empty().append(
316               curHits.map(function (hit) {
317                 var currentUrl = window.location.href;
318                 var itemRawHref = relativeUrlToAbsoluteUrl(currentUrl, relHref + hit.href);
319                 var itemHref = relHref + hit.href + "?q=" + query;
320                 var itemTitle = hit.title;
321                 var itemBrief = extractContentBrief(hit.keywords);
322
323                 var itemNode = $('<div>').attr('class', 'sr-item');
324                 var itemTitleNode = $('<div>').attr('class', 'item-title').append($('<a>').attr('href', itemHref).attr("target", "_blank").text(itemTitle));
325                 var itemHrefNode = $('<div>').attr('class', 'item-href').text(itemRawHref);
326                 var itemBriefNode = $('<div>').attr('class', 'item-brief').text(itemBrief);
327                 itemNode.append(itemTitleNode).append(itemHrefNode).append(itemBriefNode);
328                 return itemNode;
329               })
330             );
331             query.split(/\s+/).forEach(function (word) {
332               if (word !== '') {
333                 $('#search-results>.sr-items *').mark(word);
334               }
335             });
336           }
337         });
338       }
339     }
340   };
341
342   // Update href in navbar
343   function renderNavbar() {
344     var navbar = $('#navbar ul')[0];
345     if (typeof (navbar) === 'undefined') {
346       loadNavbar();
347     } else {
348       $('#navbar ul a.active').parents('li').addClass(active);
349       renderBreadcrumb();
350     }
351
352     function loadNavbar() {
353       var navbarPath = $("meta[property='docfx\\:navrel']").attr("content");
354       if (!navbarPath) {
355         return;
356       }
357       navbarPath = navbarPath.replace(/\\/g, '/');
358       var tocPath = $("meta[property='docfx\\:tocrel']").attr("content") || '';
359       if (tocPath) tocPath = tocPath.replace(/\\/g, '/');
360       $.get(navbarPath, function (data) {
361         $(data).find("#toc>ul").appendTo("#navbar");
362         if ($('#search-results').length !== 0) {
363           $('#search').show();
364           $('body').trigger("searchEvent");
365         }
366         var index = navbarPath.lastIndexOf('/');
367         var navrel = '';
368         if (index > -1) {
369           navrel = navbarPath.substr(0, index + 1);
370         }
371         $('#navbar>ul').addClass('navbar-nav');
372         var currentAbsPath = util.getAbsolutePath(window.location.pathname);
373         // set active item
374         $('#navbar').find('a[href]').each(function (i, e) {
375           var href = $(e).attr("href");
376           if (util.isRelativePath(href)) {
377             href = navrel + href;
378             $(e).attr("href", href);
379
380             // TODO: currently only support one level navbar
381             var isActive = false;
382             var originalHref = e.name;
383             if (originalHref) {
384               originalHref = navrel + originalHref;
385               if (util.getDirectory(util.getAbsolutePath(originalHref)) === util.getDirectory(util.getAbsolutePath(tocPath))) {
386                 isActive = true;
387               }
388             } else {
389               if (util.getAbsolutePath(href) === currentAbsPath) {
390                 isActive = true;
391               }
392             }
393             if (isActive) {
394               $(e).addClass(active);
395             }
396           }
397         });
398         renderNavbar();
399       });
400     }
401   }
402
403   function renderSidebar() {
404     var sidetoc = $('#sidetoggle .sidetoc')[0];
405     if (typeof (sidetoc) === 'undefined') {
406       loadToc();
407     } else {
408       registerTocEvents();
409       if ($('footer').is(':visible')) {
410         $('.sidetoc').addClass('shiftup');
411       }
412
413       // Scroll to active item
414       var top = 0;
415       $('#toc a.active').parents('li').each(function (i, e) {
416         $(e).addClass(active).addClass(expanded);
417         $(e).children('a').addClass(active);
418         top += $(e).position().top;
419       })
420       $('.sidetoc').scrollTop(top - 50);
421
422       if ($('footer').is(':visible')) {
423         $('.sidetoc').addClass('shiftup');
424       }
425
426       renderBreadcrumb();
427     }
428
429     function registerTocEvents() {
430       $('.toc .nav > li > .expand-stub').click(function (e) {
431         $(e.target).parent().toggleClass(expanded);
432       });
433       $('.toc .nav > li > .expand-stub + a:not([href])').click(function (e) {
434         $(e.target).parent().toggleClass(expanded);
435       });
436       $('#toc_filter_input').on('input', function (e) {
437         var val = this.value;
438         if (val === '') {
439           // Clear 'filtered' class
440           $('#toc li').removeClass(filtered).removeClass(hide);
441           return;
442         }
443
444         // Get leaf nodes
445         $('#toc li>a').filter(function (i, e) {
446           return $(e).siblings().length === 0
447         }).each(function (i, anchor) {
448           var text = $(anchor).attr('title');
449           var parent = $(anchor).parent();
450           var parentNodes = parent.parents('ul>li');
451           for (var i = 0; i < parentNodes.length; i++) {
452             var parentText = $(parentNodes[i]).children('a').attr('title');
453             if (parentText) text = parentText + '.' + text;
454           };
455           if (filterNavItem(text, val)) {
456             parent.addClass(show);
457             parent.removeClass(hide);
458           } else {
459             parent.addClass(hide);
460             parent.removeClass(show);
461           }
462         });
463         $('#toc li>a').filter(function (i, e) {
464           return $(e).siblings().length > 0
465         }).each(function (i, anchor) {
466           var parent = $(anchor).parent();
467           if (parent.find('li.show').length > 0) {
468             parent.addClass(show);
469             parent.addClass(filtered);
470             parent.removeClass(hide);
471           } else {
472             parent.addClass(hide);
473             parent.removeClass(show);
474             parent.removeClass(filtered);
475           }
476         })
477
478         function filterNavItem(name, text) {
479           if (!text) return true;
480           if (name && name.toLowerCase().indexOf(text.toLowerCase()) > -1) return true;
481           return false;
482         }
483       });
484     }
485
486     function loadToc() {
487       var tocPath = $("meta[property='docfx\\:tocrel']").attr("content");
488       if (!tocPath) {
489         return;
490       }
491       tocPath = tocPath.replace(/\\/g, '/');
492       $('#sidetoc').load(tocPath + " #sidetoggle > div", function () {
493         var index = tocPath.lastIndexOf('/');
494         var tocrel = '';
495         if (index > -1) {
496           tocrel = tocPath.substr(0, index + 1);
497         }
498         var currentHref = util.getAbsolutePath(window.location.pathname);
499         $('#sidetoc').find('a[href]').each(function (i, e) {
500           var href = $(e).attr("href");
501           if (util.isRelativePath(href)) {
502             href = tocrel + href;
503             $(e).attr("href", href);
504           }
505
506           if (util.getAbsolutePath(e.href) === currentHref) {
507             $(e).addClass(active);
508           }
509
510           $(e).breakWord();
511         });
512
513         renderSidebar();
514       });
515     }
516   }
517
518   function renderBreadcrumb() {
519     var breadcrumb = [];
520     $('#navbar a.active').each(function (i, e) {
521       breadcrumb.push({
522         href: e.href,
523         name: e.innerHTML
524       });
525     })
526     $('#toc a.active').each(function (i, e) {
527       breadcrumb.push({
528         href: e.href,
529         name: e.innerHTML
530       });
531     })
532
533     var html = util.formList(breadcrumb, 'breadcrumb');
534     $('#breadcrumb').html(html);
535   }
536
537   //Setup Affix
538   function renderAffix() {
539     var hierarchy = getHierarchy();
540     if (hierarchy && hierarchy.length > 0) {
541       var html = '<h5 class="title">In This Article</h5>'
542       html += util.formList(hierarchy, ['nav', 'bs-docs-sidenav']);
543       $("#affix").empty().append(html);
544       if ($('footer').is(':visible')) {
545         $(".sideaffix").css("bottom", "70px");
546       }
547       $('#affix a').click(function() {
548         var scrollspy = $('[data-spy="scroll"]').data()['bs.scrollspy'];
549         var target = e.target.hash;
550         if (scrollspy && target) {
551           scrollspy.activate(target);
552         }
553       });
554     }
555
556     function getHierarchy() {
557       // supported headers are h1, h2, h3, and h4
558       var $headers = $($.map(['h1', 'h2', 'h3', 'h4'], function (h) { return ".article article " + h; }).join(", "));
559
560       // a stack of hierarchy items that are currently being built
561       var stack = [];
562       $headers.each(function (i, e) {
563         if (!e.id) {
564           return;
565         }
566
567         var item = {
568           name: htmlEncode($(e).text()),
569           href: "#" + e.id,
570           items: []
571         };
572
573         if (!stack.length) {
574           stack.push({ type: e.tagName, siblings: [item] });
575           return;
576         }
577
578         var frame = stack[stack.length - 1];
579         if (e.tagName === frame.type) {
580           frame.siblings.push(item);
581         } else if (e.tagName[1] > frame.type[1]) {
582           // we are looking at a child of the last element of frame.siblings.
583           // push a frame onto the stack. After we've finished building this item's children,
584           // we'll attach it as a child of the last element
585           stack.push({ type: e.tagName, siblings: [item] });
586         } else {  // e.tagName[1] < frame.type[1]
587           // we are looking at a sibling of an ancestor of the current item.
588           // pop frames from the stack, building items as we go, until we reach the correct level at which to attach this item.
589           while (e.tagName[1] < stack[stack.length - 1].type[1]) {
590             buildParent();
591           }
592           if (e.tagName === stack[stack.length - 1].type) {
593             stack[stack.length - 1].siblings.push(item);
594           } else {
595             stack.push({ type: e.tagName, siblings: [item] });
596           }
597         }
598       });
599       while (stack.length > 1) {
600         buildParent();
601       }
602
603       function buildParent() {
604         var childrenToAttach = stack.pop();
605         var parentFrame = stack[stack.length - 1];
606         var parent = parentFrame.siblings[parentFrame.siblings.length - 1];
607         $.each(childrenToAttach.siblings, function (i, child) {
608           parent.items.push(child);
609         });
610       }
611       if (stack.length > 0) {
612
613         var topLevel = stack.pop().siblings;
614         if (topLevel.length === 1) {  // if there's only one topmost header, dump it
615           return topLevel[0].items;
616         }
617         return topLevel;
618       }
619       return undefined;
620     }
621
622     function htmlEncode(str) {
623       if (!str) return str;
624       return str
625         .replace(/&/g, '&amp;')
626         .replace(/"/g, '&quot;')
627         .replace(/'/g, '&#39;')
628         .replace(/</g, '&lt;')
629         .replace(/>/g, '&gt;');
630     }
631
632     function htmlDecode(value) {
633       if (!str) return str;
634       return value
635         .replace(/&quot;/g, '"')
636         .replace(/&#39;/g, "'")
637         .replace(/&lt;/g, '<')
638         .replace(/&gt;/g, '>')
639         .replace(/&amp;/g, '&');
640     }
641
642     function cssEscape(str) {
643       // see: http://stackoverflow.com/questions/2786538/need-to-escape-a-special-character-in-a-jquery-selector-string#answer-2837646
644       if (!str) return str;
645       return str
646         .replace(/[!"#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]/g, "\\$&");
647     }
648   }
649
650   // Show footer
651   function renderFooter() {
652     initFooter();
653     $(window).on("scroll", showFooterCore);
654
655     function initFooter() {
656       if (needFooter()) {
657         shiftUpBottomCss();
658         $("footer").show();
659       } else {
660         resetBottomCss();
661         $("footer").hide();
662       }
663     }
664
665     function showFooterCore() {
666       if (needFooter()) {
667         shiftUpBottomCss();
668         $("footer").fadeIn();
669       } else {
670         resetBottomCss();
671         $("footer").fadeOut();
672       }
673     }
674
675     function needFooter() {
676       var scrollHeight = $(document).height();
677       var scrollPosition = $(window).height() + $(window).scrollTop();
678       return (scrollHeight - scrollPosition) < 1;
679     }
680
681     function resetBottomCss() {
682       $(".sidetoc").removeClass("shiftup");
683       $(".sideaffix").removeClass("shiftup");
684     }
685
686     function shiftUpBottomCss() {
687       $(".sidetoc").addClass("shiftup");
688       $(".sideaffix").addClass("shiftup");
689     }
690   }
691
692   function renderLogo() {
693     // For LOGO SVG
694     // Replace SVG with inline SVG
695     // http://stackoverflow.com/questions/11978995/how-to-change-color-of-svg-image-using-css-jquery-svg-image-replacement
696     jQuery('img.svg').each(function () {
697       var $img = jQuery(this);
698       var imgID = $img.attr('id');
699       var imgClass = $img.attr('class');
700       var imgURL = $img.attr('src');
701
702       jQuery.get(imgURL, function (data) {
703         // Get the SVG tag, ignore the rest
704         var $svg = jQuery(data).find('svg');
705
706         // Add replaced image's ID to the new SVG
707         if (typeof imgID !== 'undefined') {
708           $svg = $svg.attr('id', imgID);
709         }
710         // Add replaced image's classes to the new SVG
711         if (typeof imgClass !== 'undefined') {
712           $svg = $svg.attr('class', imgClass + ' replaced-svg');
713         }
714
715         // Remove any invalid XML tags as per http://validator.w3.org
716         $svg = $svg.removeAttr('xmlns:a');
717
718         // Replace image with new SVG
719         $img.replaceWith($svg);
720
721       }, 'xml');
722     });
723   }
724
725   function renderTabs() {
726     var contentAttrs = {
727       id: 'data-bi-id',
728       name: 'data-bi-name',
729       type: 'data-bi-type'
730     };
731
732     var Tab = (function () {
733       function Tab(li, a, section) {
734         this.li = li;
735         this.a = a;
736         this.section = section;
737       }
738       Object.defineProperty(Tab.prototype, "tabIds", {
739         get: function () { return this.a.getAttribute('data-tab').split(' '); },
740         enumerable: true,
741         configurable: true
742       });
743       Object.defineProperty(Tab.prototype, "condition", {
744         get: function () { return this.a.getAttribute('data-condition'); },
745         enumerable: true,
746         configurable: true
747       });
748       Object.defineProperty(Tab.prototype, "visible", {
749         get: function () { return !this.li.hasAttribute('hidden'); },
750         set: function (value) {
751           if (value) {
752             this.li.removeAttribute('hidden');
753             this.li.removeAttribute('aria-hidden');
754           }
755           else {
756             this.li.setAttribute('hidden', 'hidden');
757             this.li.setAttribute('aria-hidden', 'true');
758           }
759         },
760         enumerable: true,
761         configurable: true
762       });
763       Object.defineProperty(Tab.prototype, "selected", {
764         get: function () { return !this.section.hasAttribute('hidden'); },
765         set: function (value) {
766           if (value) {
767             this.a.setAttribute('aria-selected', 'true');
768             this.a.tabIndex = 0;
769             this.section.removeAttribute('hidden');
770             this.section.removeAttribute('aria-hidden');
771           }
772           else {
773             this.a.setAttribute('aria-selected', 'false');
774             this.a.tabIndex = -1;
775             this.section.setAttribute('hidden', 'hidden');
776             this.section.setAttribute('aria-hidden', 'true');
777           }
778         },
779         enumerable: true,
780         configurable: true
781       });
782       Tab.prototype.focus = function () {
783         this.a.focus();
784       };
785       return Tab;
786     }());
787
788     initTabs(document.body);
789
790     function initTabs(container) {
791       var queryStringTabs = readTabsQueryStringParam();
792       var elements = container.querySelectorAll('.tabGroup');
793       var state = { groups: [], selectedTabs: [] };
794       for (var i = 0; i < elements.length; i++) {
795         var group = initTabGroup(elements.item(i));
796         if (!group.independent) {
797           updateVisibilityAndSelection(group, state);
798           state.groups.push(group);
799         }
800       }
801       container.addEventListener('click', function (event) { return handleClick(event, state); });
802       if (state.groups.length === 0) {
803         return state;
804       }
805       selectTabs(queryStringTabs, container);
806       updateTabsQueryStringParam(state);
807       notifyContentUpdated();
808       return state;
809     }
810
811     function initTabGroup(element) {
812       var group = {
813         independent: element.hasAttribute('data-tab-group-independent'),
814         tabs: []
815       };
816       var li = element.firstElementChild.firstElementChild;
817       while (li) {
818         var a = li.firstElementChild;
819         a.setAttribute(contentAttrs.name, 'tab');
820         var dataTab = a.getAttribute('data-tab').replace(/\+/g, ' ');
821         a.setAttribute('data-tab', dataTab);
822         var section = element.querySelector("[id=\"" + a.getAttribute('aria-controls') + "\"]");
823         var tab = new Tab(li, a, section);
824         group.tabs.push(tab);
825         li = li.nextElementSibling;
826       }
827       element.setAttribute(contentAttrs.name, 'tab-group');
828       element.tabGroup = group;
829       return group;
830     }
831
832     function updateVisibilityAndSelection(group, state) {
833       var anySelected = false;
834       var firstVisibleTab;
835       for (var _i = 0, _a = group.tabs; _i < _a.length; _i++) {
836         var tab = _a[_i];
837         tab.visible = tab.condition === null || state.selectedTabs.indexOf(tab.condition) !== -1;
838         if (tab.visible) {
839           if (!firstVisibleTab) {
840             firstVisibleTab = tab;
841           }
842         }
843         tab.selected = tab.visible && arraysIntersect(state.selectedTabs, tab.tabIds);
844         anySelected = anySelected || tab.selected;
845       }
846       if (!anySelected) {
847         for (var _b = 0, _c = group.tabs; _b < _c.length; _b++) {
848           var tabIds = _c[_b].tabIds;
849           for (var _d = 0, tabIds_1 = tabIds; _d < tabIds_1.length; _d++) {
850             var tabId = tabIds_1[_d];
851             var index = state.selectedTabs.indexOf(tabId);
852             if (index === -1) {
853               continue;
854             }
855             state.selectedTabs.splice(index, 1);
856           }
857         }
858         var tab = firstVisibleTab;
859         tab.selected = true;
860         state.selectedTabs.push(tab.tabIds[0]);
861       }
862     }
863
864     function getTabInfoFromEvent(event) {
865       if (!(event.target instanceof HTMLElement)) {
866         return null;
867       }
868       var anchor = event.target.closest('a[data-tab]');
869       if (anchor === null) {
870         return null;
871       }
872       var tabIds = anchor.getAttribute('data-tab').split(' ');
873       var group = anchor.parentElement.parentElement.parentElement.tabGroup;
874       if (group === undefined) {
875         return null;
876       }
877       return { tabIds: tabIds, group: group, anchor: anchor };
878     }
879
880     function handleClick(event, state) {
881       var info = getTabInfoFromEvent(event);
882       if (info === null) {
883         return;
884       }
885       event.preventDefault();
886       info.anchor.href = 'javascript:';
887       setTimeout(function () { return info.anchor.href = '#' + info.anchor.getAttribute('aria-controls'); });
888       var tabIds = info.tabIds, group = info.group;
889       var originalTop = info.anchor.getBoundingClientRect().top;
890       if (group.independent) {
891         for (var _i = 0, _a = group.tabs; _i < _a.length; _i++) {
892           var tab = _a[_i];
893           tab.selected = arraysIntersect(tab.tabIds, tabIds);
894         }
895       }
896       else {
897         if (arraysIntersect(state.selectedTabs, tabIds)) {
898           return;
899         }
900         var previousTabId = group.tabs.filter(function (t) { return t.selected; })[0].tabIds[0];
901         state.selectedTabs.splice(state.selectedTabs.indexOf(previousTabId), 1, tabIds[0]);
902         for (var _b = 0, _c = state.groups; _b < _c.length; _b++) {
903           var group_1 = _c[_b];
904           updateVisibilityAndSelection(group_1, state);
905         }
906         updateTabsQueryStringParam(state);
907       }
908       notifyContentUpdated();
909       var top = info.anchor.getBoundingClientRect().top;
910       if (top !== originalTop && event instanceof MouseEvent) {
911         window.scrollTo(0, window.pageYOffset + top - originalTop);
912       }
913     }
914
915     function selectTabs(tabIds) {
916       for (var _i = 0, tabIds_1 = tabIds; _i < tabIds_1.length; _i++) {
917         var tabId = tabIds_1[_i];
918         var a = document.querySelector(".tabGroup > ul > li > a[data-tab=\"" + tabId + "\"]:not([hidden])");
919         if (a === null) {
920           return;
921         }
922         a.dispatchEvent(new CustomEvent('click', { bubbles: true }));
923       }
924     }
925
926     function readTabsQueryStringParam() {
927       var qs = parseQueryString();
928       var t = qs.tabs;
929       if (t === undefined || t === '') {
930         return [];
931       }
932       return t.split(',');
933     }
934
935     function updateTabsQueryStringParam(state) {
936       var qs = parseQueryString();
937       qs.tabs = state.selectedTabs.join();
938       var url = location.protocol + "//" + location.host + location.pathname + "?" + toQueryString(qs) + location.hash;
939       if (location.href === url) {
940         return;
941       }
942       history.replaceState({}, document.title, url);
943     }
944
945     function toQueryString(args) {
946       var parts = [];
947       for (var name_1 in args) {
948         if (args.hasOwnProperty(name_1) && args[name_1] !== '' && args[name_1] !== null && args[name_1] !== undefined) {
949           parts.push(encodeURIComponent(name_1) + '=' + encodeURIComponent(args[name_1]));
950         }
951       }
952       return parts.join('&');
953     }
954
955     function parseQueryString(queryString) {
956       var match;
957       var pl = /\+/g;
958       var search = /([^&=]+)=?([^&]*)/g;
959       var decode = function (s) { return decodeURIComponent(s.replace(pl, ' ')); };
960       if (queryString === undefined) {
961         queryString = '';
962       }
963       queryString = queryString.substring(1);
964       var urlParams = {};
965       while (match = search.exec(queryString)) {
966         urlParams[decode(match[1])] = decode(match[2]);
967       }
968       return urlParams;
969     }
970
971     function arraysIntersect(a, b) {
972       for (var _i = 0, a_1 = a; _i < a_1.length; _i++) {
973         var itemA = a_1[_i];
974         for (var _a = 0, b_1 = b; _a < b_1.length; _a++) {
975           var itemB = b_1[_a];
976           if (itemA === itemB) {
977             return true;
978           }
979         }
980       }
981       return false;
982     }
983
984     function notifyContentUpdated() {
985       // Dispatch this event when needed
986       // window.dispatchEvent(new CustomEvent('content-update'));
987     }
988   }
989
990   function utility() {
991     this.getAbsolutePath = getAbsolutePath;
992     this.isRelativePath = isRelativePath;
993     this.isAbsolutePath = isAbsolutePath;
994     this.getDirectory = getDirectory;
995     this.formList = formList;
996
997     function getAbsolutePath(href) {
998       // Use anchor to normalize href
999       var anchor = $('<a href="' + href + '"></a>')[0];
1000       // Ignore protocal, remove search and query
1001       return anchor.host + anchor.pathname;
1002     }
1003
1004     function isRelativePath(href) {
1005       if (href === undefined || href === '' || href[0] === '/') {
1006         return false;
1007       }
1008       return !isAbsolutePath(href);
1009     }
1010
1011     function isAbsolutePath(href) {
1012       return (/^(?:[a-z]+:)?\/\//i).test(href);
1013     }
1014
1015     function getDirectory(href) {
1016       if (!href) return '';
1017       var index = href.lastIndexOf('/');
1018       if (index == -1) return '';
1019       if (index > -1) {
1020         return href.substr(0, index);
1021       }
1022     }
1023
1024     function formList(item, classes) {
1025       var level = 1;
1026       var model = {
1027         items: item
1028       };
1029       var cls = [].concat(classes).join(" ");
1030       return getList(model, cls);
1031
1032       function getList(model, cls) {
1033         if (!model || !model.items) return null;
1034         var l = model.items.length;
1035         if (l === 0) return null;
1036         var html = '<ul class="level' + level + ' ' + (cls || '') + '">';
1037         level++;
1038         for (var i = 0; i < l; i++) {
1039           var item = model.items[i];
1040           var href = item.href;
1041           var name = item.name;
1042           if (!name) continue;
1043           html += href ? '<li><a href="' + href + '">' + name + '</a>' : '<li>' + name;
1044           html += getList(item, cls) || '';
1045           html += '</li>';
1046         }
1047         html += '</ul>';
1048         return html;
1049       }
1050     }
1051
1052     /**
1053      * Add <wbr> into long word.
1054      * @param {String} text - The word to break. It should be in plain text without HTML tags.
1055      */
1056     function breakPlainText(text) {
1057       if (!text) return text;
1058       return text.replace(/([a-z])([A-Z])|(\.)(\w)/g, '$1$3<wbr>$2$4')
1059     }
1060
1061     /**
1062      * Add <wbr> into long word. The jQuery element should contain no html tags.
1063      * If the jQuery element contains tags, this function will not change the element.
1064      */
1065     $.fn.breakWord = function () {
1066       if (this.html() == this.text()) {
1067         this.html(function (index, text) {
1068           return breakPlainText(text);
1069         })
1070       }
1071       return this;
1072     }
1073   }
1074
1075   // adjusted from https://stackoverflow.com/a/13067009/1523776
1076   function workAroundFixedHeaderForAnchors() {
1077     var HISTORY_SUPPORT = !!(history && history.pushState);
1078     var ANCHOR_REGEX = /^#[^ ]+$/;
1079
1080     function getFixedOffset() {
1081       return $('header').first().height();
1082     }
1083
1084     /**
1085      * If the provided href is an anchor which resolves to an element on the
1086      * page, scroll to it.
1087      * @param  {String} href
1088      * @return {Boolean} - Was the href an anchor.
1089      */
1090     function scrollIfAnchor(href, pushToHistory) {
1091       var match, rect, anchorOffset;
1092
1093       if (!ANCHOR_REGEX.test(href)) {
1094         return false;
1095       }
1096
1097       match = document.getElementById(href.slice(1));
1098
1099       if (match) {
1100         rect = match.getBoundingClientRect();
1101         anchorOffset = window.pageYOffset + rect.top - getFixedOffset();
1102         window.scrollTo(window.pageXOffset, anchorOffset);
1103
1104         // Add the state to history as-per normal anchor links
1105         if (HISTORY_SUPPORT && pushToHistory) {
1106           history.pushState({}, document.title, location.pathname + href);
1107         }
1108       }
1109
1110       return !!match;
1111     }
1112
1113     /**
1114      * Attempt to scroll to the current location's hash.
1115      */
1116     function scrollToCurrent() {
1117       scrollIfAnchor(window.location.hash);
1118     }
1119
1120     /**
1121      * If the click event's target was an anchor, fix the scroll position.
1122      */
1123     function delegateAnchors(e) {
1124       var elem = e.target;
1125
1126       if (scrollIfAnchor(elem.getAttribute('href'), true)) {
1127         e.preventDefault();
1128       }
1129     }
1130
1131     $(window).on('hashchange', scrollToCurrent);
1132     // Exclude tabbed content case
1133     $('a:not([data-tab])').click(delegateAnchors);
1134     scrollToCurrent();
1135   }
1136 });