PageSourceSearch

https://www.resilio.com/connect_api/main.js?

js resilio.com collected 2026-10-01 09:07:50 UTC 56,110 bytes, 1,641 lines download raw bytes

1var connectApiBasePath = window.__connectApiAssetBasePath || '/connect_api/';
2var connectApiDataBasePath = window.__connectApiBasePath || connectApiBasePath;
3
4if (connectApiBasePath.slice(-1) !== '/') {
5    connectApiBasePath += '/';
6}
7
8if (connectApiDataBasePath.slice(-1) !== '/') {
9    connectApiDataBasePath += '/';
10}
11
12function connectApiAsset(path) {
13    return connectApiBasePath + path;
14}
15
16function connectApiDataAsset(path) {
17    return connectApiDataBasePath + path;
18}
19
20require.config({
21    paths: {
22        bootstrap: connectApiAsset('vendor/bootstrap.min'),
23        diffMatchPatch: connectApiAsset('vendor/diff_match_patch.min'),
24        handlebars: connectApiAsset('vendor/handlebars.min'),
25        handlebarsExtended: connectApiAsset('utils/handlebars_helper'),
26        jquery: connectApiAsset('vendor/jquery.min'),
27        locales: connectApiAsset('locales/locale'),
28        lodash: connectApiAsset('vendor/lodash.custom.min'),
29        pathToRegexp: connectApiAsset('vendor/path-to-regexp/index'),
30        prettify: connectApiAsset('vendor/prettify/prettify'),
31        semver: connectApiAsset('vendor/semver.min'),
32        utilsSampleRequest: connectApiAsset('utils/send_sample_request'),
33        webfontloader: connectApiAsset('vendor/webfontloader'),
34        list: connectApiAsset('vendor/list.min')
35    },
36    shim: {
37        bootstrap: {
38            deps: ['jquery']
39        },
40        diffMatchPatch: {
41            exports: 'diff_match_patch'
42        },
43        handlebars: {
44            exports: 'Handlebars'
45        },
46        handlebarsExtended: {
47            deps: ['jquery', 'handlebars'],
48            exports: 'Handlebars'
49        },
50        prettify: {
51            exports: 'prettyPrint'
52        }
53    },
54    urlArgs: window.__apiDocsAssetVersion || 'v=20260714-api-docs-v5',
55    waitSeconds: 15
56});
57
58require([
59    'jquery',
60    'lodash',
61    'locales',
62    'handlebarsExtended',
63    connectApiDataAsset('api_project.js'),
64    connectApiDataAsset('api_data.js'),
65    'prettify',
66    'utilsSampleRequest',
67    'semver',
68    'webfontloader',
69    'bootstrap',
70    'pathToRegexp',
71    'list'
72], function($, _, locale, Handlebars, apiProject, apiData, prettyPrint, sampleRequest, semver, WebFont) {
73
74    var initialScrollLock = window.__apiDocsInitialScrollLock;
75    var setApiDocsLoadingProgress = window.__apiDocsSetLoadingProgress || function() {};
76    var initialAnchorHash = window.location.hash || window.__apiDocsInitialHash || '';
77    var shouldResetInitialScroll = !!document.querySelector('.api-docs-page') && (!initialAnchorHash || initialAnchorHash === '#top');
78    var initialScrollResetStartedAt = Date.now();
79    var initialScrollResetDuration = 3000;
80    var initialScrollResetReleased = false;
81    var sampleRequestInitQueued = false;
82
83    function debounceApiDocs(fn, wait) {
84        var timeout;
85
86        return function() {
87            var context = this;
88            var args = arguments;
89
90            window.clearTimeout(timeout);
91            timeout = window.setTimeout(function() {
92                fn.apply(context, args);
93            }, wait);
94        };
95    }
96    var exampleTabsResizeBound = false;
97
98    setApiDocsLoadingProgress(68);
99
100    if (shouldResetInitialScroll && 'scrollRestoration' in window.history) {
101        window.history.scrollRestoration = 'manual';
102    }
103
104    if (shouldResetInitialScroll) {
105        ['wheel', 'touchstart', 'mousedown', 'keydown'].forEach(function(eventName) {
106            window.addEventListener(eventName, function() {
107                initialScrollResetReleased = true;
108            }, { once: true, passive: true });
109        });
110    }
111
112    function resetInitialScrollPosition() {
113        if (
114            shouldResetInitialScroll &&
115            !initialScrollResetReleased &&
116            Date.now() - initialScrollResetStartedAt < initialScrollResetDuration
117        ) {
118            window.scrollTo(0, 0);
119            if (initialScrollLock && typeof initialScrollLock.reset === 'function') {
120                initialScrollLock.reset();
121            }
122        }
123    }
124
125    function holdInitialScrollPosition() {
126        resetInitialScrollPosition();
127
128        if (
129            shouldResetInitialScroll &&
130            !initialScrollResetReleased &&
131            Date.now() - initialScrollResetStartedAt < initialScrollResetDuration
132        ) {
133            window.requestAnimationFrame(holdInitialScrollPosition);
134        }
135    }
136
137    if (shouldResetInitialScroll) {
138        window.addEventListener('load', function() {
139            window.setTimeout(resetInitialScrollPosition, 0);
140            window.setTimeout(resetInitialScrollPosition, 500);
141            window.setTimeout(resetInitialScrollPosition, 1200);
142        });
143        window.addEventListener('pageshow', function() {
144            window.setTimeout(resetInitialScrollPosition, 0);
145            window.setTimeout(resetInitialScrollPosition, 500);
146        });
147    }
148
149    holdInitialScrollPosition();
150
151    // load google web fonts
152    loadGoogleFontCss();
153
154    function schedulePostRenderWork(callback, timeout) {
155        window.setTimeout(function() {
156            if (window.requestIdleCallback) {
157                window.requestIdleCallback(callback, { timeout: timeout || 1200 });
158            } else {
159                window.setTimeout(callback, 0);
160            }
161        }, 0);
162    }
163
164    function scheduleSampleRequestInit() {
165        if (sampleRequestInitQueued) {
166            return;
167        }
168
169        sampleRequestInitQueued = true;
170        schedulePostRenderWork(function() {
171            sampleRequestInitQueued = false;
172            sampleRequest.initDynamic();
173        }, 1000);
174    }
175
176    function revealApiDocsPage() {
177        var reveal = function() {
178            setApiDocsLoadingProgress(100);
179            settleCurrentHashPosition(0);
180            window.setTimeout(function() {
181                settleCurrentHashPosition(0);
182            }, 150);
183            window.setTimeout(function() {
184                settleCurrentHashPosition(0);
185                $('.api-docs-page').removeClass('api-docs-is-loading');
186            }, 300);
187        };
188
189        if (window.requestAnimationFrame) {
190            window.requestAnimationFrame(function() {
191                window.requestAnimationFrame(reveal);
192            });
193        } else {
194            window.setTimeout(reveal, 0);
195        }
196    }
197
198    var api = apiData.api;
199
200    //
201    // Templates
202    //
203    var templateHeader         = Handlebars.compile( $('#template-header').html() );
204    var templateFooter         = Handlebars.compile( $('#template-footer').html() );
205    var templateArticle        = Handlebars.compile( $('#template-article').html() );
206    var templateCompareArticle = Handlebars.compile( $('#template-compare-article').html() );
207    var templateGenerator      = Handlebars.compile( $('#template-generator').html() );
208    var templateProject        = Handlebars.compile( $('#template-project').html() );
209    var templateSections       = Handlebars.compile( $('#template-sections').html() );
210    var templateSidenav        = Handlebars.compile( $('#template-sidenav').html() );
211
212    //
213    // apiProject defaults
214    //
215    if ( ! apiProject.template)
216        apiProject.template = {};
217
218    if (apiProject.template.withCompare == null)
219        apiProject.template.withCompare = true;
220
221    if (window.__apiDocsReleaseVersions)
222        apiProject.template.withCompare = false;
223
224    if (apiProject.template.withGenerator == null)
225        apiProject.template.withGenerator = true;
226
227    if (apiProject.template.forceLanguage)
228        locale.setLanguage(apiProject.template.forceLanguage);
229
230    // Setup jQuery Ajax
231    $.ajaxSetup(apiProject.template.jQueryAjaxSetup);
232
233    //
234    // Data transform
235    //
236    // grouped by group
237    var apiByGroup = _.groupBy(api, function(entry) {
238        return entry.group;
239    });
240
241    // grouped by group and name
242    var apiByGroupAndName = {};
243    $.each(apiByGroup, function(index, entries) {
244        apiByGroupAndName[index] = _.groupBy(entries, function(entry) {
245            return entry.name;
246        });
247    });
248
249    //
250    // sort api within a group by title ASC and custom order
251    //
252    var newList = [];
253    var umlauts = { 'ä': 'ae', 'ü': 'ue', 'ö': 'oe', 'ß': 'ss' }; // TODO: remove in version 1.0
254    $.each (apiByGroupAndName, function(index, groupEntries) {
255        // get titles from the first entry of group[].name[] (name has versioning)
256        var titles = [];
257        $.each (groupEntries, function(titleName, entries) {
258            var title = entries[0].title;
259            if(title !== undefined) {
260                title.toLowerCase().replace(/[äöüß]/g, function($0) { return umlauts[$0]; });
261                titles.push(title + '#~#' + titleName); // '#~#' keep reference to titleName after sorting
262            }
263        });
264        // sort by name ASC
265        titles.sort();
266
267        // custom order
268        if (apiProject.order)
269            titles = sortByOrder(titles, apiProject.order, '#~#');
270
271        // add single elements to the new list
272        titles.forEach(function(name) {
273            var values = name.split('#~#');
274            var key = values[1];
275            groupEntries[key].forEach(function(entry) {
276                newList.push(entry);
277            });
278        });
279    });
280    // api overwrite with ordered list
281    api = newList;
282
283    //
284    // Group- and Versionlists
285    //
286    var apiGroups = {};
287    var apiGroupTitles = {};
288    var apiVersions = {};
289    apiVersions[apiProject.version] = 1;
290
291    $.each(api, function(index, entry) {
292        apiGroups[entry.group] = 1;
293        apiGroupTitles[entry.group] = entry.groupTitle || entry.group;
294        apiVersions[entry.version] = 1;
295    });
296
297    // sort groups
298    apiGroups = Object.keys(apiGroups);
299    apiGroups.sort();
300
301    // custom order
302    if (apiProject.order)
303        apiGroups = sortByOrder(apiGroups, apiProject.order);
304
305    // sort versions DESC
306    apiVersions = Object.keys(apiVersions);
307    apiVersions.sort(semver.compare);
308    apiVersions.reverse();
309
310    //
311    // create Navigationlist
312    //
313    var nav = [];
314    apiGroups.forEach(function(group) {
315        // Mainmenu entry
316        nav.push({
317            group: group,
318            isHeader: true,
319            title: apiGroupTitles[group]
320        });
321
322        // Submenu
323        var oldName = '';
324        api.forEach(function(entry) {
325            if (entry.group === group) {
326                if (oldName !== entry.name) {
327                    nav.push({
328                        title: entry.title,
329                        group: group,
330                        name: entry.name,
331                        type: entry.type,
332                        version: entry.version
333                    });
334                } else {
335                    nav.push({
336                        title: entry.title,
337                        group: group,
338                        hidden: true,
339                        name: entry.name,
340                        type: entry.type,
341                        version: entry.version
342                    });
343                }
344                oldName = entry.name;
345            }
346        });
347    });
348
349    /**
350     * Add navigation items by analyzing the HTML content and searching for h1 and h2 tags
351     * @param nav Object the navigation array
352     * @param content string the compiled HTML content
353     * @param index where to insert items
354     * @return boolean true if any good-looking (i.e. with a group identifier) <h1> tag was found
355     */
356    function add_nav(nav, content, index) {
357        var found_level1 = false;
358        if ( ! content) {
359          return found_level1;
360        }
361        var topics = content.match(/<h(1|2).*?>(.+?)<\/h(1|2)>/gi);
362        if ( topics ) {
363          topics.forEach(function(entry) {
364              var level = entry.substring(2,3);
365              var title = entry.replace(/<.+?>/g, '');    // Remove all HTML tags for the title
366              var entry_tags = entry.match(/id="api-([^\-]+)(?:-(.+))?"/);    // Find the group and name in the id property
367              var group = (entry_tags ? entry_tags[1] : null);
368              var name = (entry_tags ? entry_tags[2] : null);
369              if (level==1 && title && group)  {
370                  nav.splice(index, 0, {
371                      group: group,
372                      isHeader: true,
373                      title: title,
374                      isFixed: true
375                  });
376                  index++;
377                  found_level1 = true;
378              }
379              if (level==2 && title && group && name)    {
380                  nav.splice(index, 0, {
381                      group: group,
382                      name: name,
383                      isHeader: false,
384                      title: title,
385                      isFixed: false,
386                      version: '1.0'
387                  });
388                  index++;
389              }
390          });
391        }
392        return found_level1;
393    }
394
395    // Mainmenu Header entry
396    if (apiProject.header) {
397        var found_level1 = add_nav(nav, apiProject.header.content, 0); // Add level 1 and 2 titles
398        if (!found_level1) {    // If no Level 1 tags were found, make a title
399            nav.unshift({
400                group: '_',
401                isHeader: true,
402                title: (apiProject.header.title == null) ? locale.__('General') : apiProject.header.title,
403                isFixed: true
404            });
405        }
406    }
407
408    // Mainmenu Footer entry
409    if (apiProject.footer) {
410        var last_nav_index = nav.length;
411        var found_level1 = add_nav(nav, apiProject.footer.content, nav.length); // Add level 1 and 2 titles
412        if (!found_level1 && apiProject.footer.title != null) {    // If no Level 1 tags were found, make a title
413            nav.splice(last_nav_index, 0, {
414                group: '_footer',
415                isHeader: true,
416                title: apiProject.footer.title,
417                isFixed: true
418            });
419        }
420    }
421
422    // render pagetitle
423    var title = apiProject.title ? apiProject.title : 'apiDoc: ' + apiProject.name + ' - ' + apiProject.version;
424    if (!$('.api-docs-page').length) {
425        $(document).attr('title', title);
426    }
427
428    // remove loader
429    $('#loader').remove();
430
431    // render sidenav
432    var fields = {
433        nav: nav
434    };
435    $('#sidenav').append( templateSidenav(fields) );
436    setApiDocsLoadingProgress(72);
437
438    // render Generator
439    $('#generator').append( templateGenerator(apiProject) );
440
441    // render Project
442    _.extend(apiProject, { versions: apiVersions});
443    $('#project').append( templateProject(apiProject) );
444
445    // render apiDoc, header/footer documentation
446    if (apiProject.header)
447        $('#header').append( templateHeader(apiProject.header) );
448
449    if (apiProject.footer)
450        $('#footer').append( templateFooter(apiProject.footer) );
451
452    //
453    // Render Sections and Articles
454    //
455    var articleVersions = {};
456    var content = '';
457    apiGroups.forEach(function(groupEntry) {
458        var articles = [];
459        var oldName = '';
460        var fields = {};
461        var title = groupEntry;
462        var description = '';
463        articleVersions[groupEntry] = {};
464
465        // render all articles of a group
466        api.forEach(function(entry) {
467            if(groupEntry === entry.group) {
468                if (oldName !== entry.name) {
469                    // determine versions
470                    api.forEach(function(versionEntry) {
471                        if (groupEntry === versionEntry.group && entry.name === versionEntry.name) {
472                            if ( ! articleVersions[entry.group].hasOwnProperty(entry.name) ) {
473                                articleVersions[entry.group][entry.name] = [];
474                            }
475                            articleVersions[entry.group][entry.name].push(versionEntry.version);
476                        }
477                    });
478                    fields = {
479                        article: entry,
480                        versions: articleVersions[entry.group][entry.name]
481                    };
482                } else {
483                    fields = {
484                        article: entry,
485                        hidden: true,
486                        versions: articleVersions[entry.group][entry.name]
487                    };
488                }
489
490                // add prefix URL for endpoint
491                if (apiProject.url)
492                    fields.article.url = apiProject.url + fields.article.url;
493
494                addArticleSettings(fields, entry);
495
496                if (entry.groupTitle)
497                    title = entry.groupTitle;
498
499                // TODO: make groupDescription compareable with older versions (not important for the moment)
500                if (entry.groupDescription)
501                    description = entry.groupDescription;
502
503                articles.push({
504                    article: templateArticle(fields),
505                    group: entry.group,
506                    name: entry.name
507                });
508                oldName = entry.name;
509            }
510        });
511
512        // render Section with Articles
513        var fields = {
514            group: groupEntry,
515            title: title,
516            description: description,
517            articles: articles
518        };
519        content += templateSections(fields);
520    });
521    $('#sections').append( content );
522    setApiDocsLoadingProgress(84);
523    resetInitialScrollPosition();
524
525    // Account for the fixed site header plus a small cushion so linked headings stay visible.
526    function getTopOverlayBottom() {
527        var bottom = 0;
528        var selectors = [
529            '#resilio-news-bar',
530            '[data-resilio-header-2026-v1].s-navbar-block',
531            '[data-resilio-main-header="1"]',
532            '.s-navbar-block',
533            'header',
534            '[role="banner"]'
535        ];
536
537        selectors.forEach(function(selector) {
538            $(selector).each(function() {
539                var styles = window.getComputedStyle ? window.getComputedStyle(this) : null;
540                var position = styles ? styles.position : '';
541                var rect = this.getBoundingClientRect ? this.getBoundingClientRect() : null;
542                var isPinnedToTop;
543
544                if (!rect || rect.height <= 0) {
545                    return;
546                }
547
548                if (styles && (styles.display === 'none' || styles.visibility === 'hidden')) {
549                    return;
550                }
551
552                isPinnedToTop = rect.top <= 4 && rect.bottom > 0 && rect.bottom < window.innerHeight * 0.6;
553
554                if (position === 'fixed' || position === 'sticky' || position === '-webkit-sticky' || isPinnedToTop) {
555                    bottom = Math.max(bottom, rect.bottom);
556                }
557            });
558        });
559
560        return bottom;
561    }
562
563    function getAnchorOffset() {
564        var rootStyles = window.getComputedStyle ? window.getComputedStyle(document.documentElement) : null;
565        var headerHeight = rootStyles ? parseFloat(rootStyles.getPropertyValue('--resilio-header-h')) : 0;
566        var newsbarOffset = rootStyles ? parseFloat(rootStyles.getPropertyValue('--resilio-newsbar-visible-offset')) : 0;
567        var cssOffset = (headerHeight || 72) + (newsbarOffset || 0);
568        return Math.ceil(Math.max(cssOffset, getTopOverlayBottom())) + 25;
569    }
570
571    function getAnchorTarget(id) {
572        var targetId = id.charAt(0) === '#' ? id.slice(1) : id;
573        var element = document.getElementById(targetId);
574
575        return element ? $(element) : $();
576    }
577
578    function getAnchorTargetTop(id) {
579        var $target = getAnchorTarget(id);
580
581        if ($target.length === 0) {
582            return 0;
583        }
584
585        var $heading = $target.is('h1,h2,h3,h4') ? $target : $target.find('> h1, > article:visible h1:first, article:visible h1:first, h1:first').first();
586
587        return $heading.length > 0 ? $heading.offset().top : $target.offset().top;
588    }
589
590    function getAnchorScrollTop(id) {
591        return Math.max(0, parseInt(getAnchorTargetTop(id), 10) - getAnchorOffset());
592    }
593
594    function getPreCopyText(pre) {
595        return $(pre).text();
596    }
597
598    function copyTextWithTextarea(text) {
599        var textarea = document.createElement('textarea');
600        textarea.value = text;
601        textarea.setAttribute('readonly', '');
602        textarea.style.position = 'fixed';
603        textarea.style.top = '-9999px';
604        textarea.style.left = '-9999px';
605        document.body.appendChild(textarea);
606        textarea.focus();
607        textarea.select();
608        textarea.setSelectionRange(0, textarea.value.length);
609
610        try {
611            document.execCommand('copy');
612        } finally {
613            document.body.removeChild(textarea);
614        }
615
616        return $.Deferred().resolve().promise();
617    }
618
619    function copyTextToClipboard(text) {
620        if (navigator.clipboard && navigator.clipboard.writeText) {
621            var clipboardResult = navigator.clipboard.writeText(text);
622
623            if (clipboardResult && clipboardResult.then) {
624                return clipboardResult.catch(function() {
625                    return copyTextWithTextarea(text);
626                });
627            }
628
629            return $.Deferred().resolve().promise();
630        }
631
632        return copyTextWithTextarea(text);
633    }
634
635    function enhancePreCopyButtons($root) {
636        $root.find('pre').each(function() {
637            var $pre = $(this);
638            var $wrapper;
639
640            $pre.find('> .api-copy-button').remove();
641
642            if ($pre.parent('.api-pre-wrapper').length > 0) {
643                return;
644            }
645
646            $pre.wrap('<div class="api-pre-wrapper"></div>');
647            $wrapper = $pre.parent();
648            $wrapper.append('<button type="button" class="api-copy-button" aria-label="Copy code" title="Copy code"><img src="/images/copy-01.svg" alt="" aria-hidden="true"></button><span class="api-copy-message" aria-live="polite"></span>');
649        });
650    }
651
652    var anchorScrollAnimation;
653
654    function easeInOutGentle(progress) {
655        return progress < 0.5 ?
656            16 * Math.pow(progress, 5) :
657            1 - Math.pow(-2 * progress + 2, 6) / 2;
658    }
659
660    function getScrollDuration(distance, duration) {
661        if (typeof duration === 'number') {
662            return duration;
663        }
664
665        return Math.min(980, Math.max(400, distance * 0.38));
666    }
667
668    function scrollToTop(endTop, duration, getFinalTop, onComplete) {
669        var startTop = window.pageYOffset || document.documentElement.scrollTop || 0;
670        var distance = Math.abs(endTop - startTop);
671        var scrollDuration = getScrollDuration(distance, duration);
672        var prefersReducedMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
673        var getResolvedFinalTop = typeof getFinalTop === 'function' ? getFinalTop : function() {
674            return endTop;
675        };
676
677        if (anchorScrollAnimation) {
678            window.cancelAnimationFrame(anchorScrollAnimation);
679        }
680
681        if (scrollDuration === 0 || prefersReducedMotion || distance < 2) {
682            window.scrollTo(0, endTop);
683            window.setTimeout(function() {
684                window.scrollTo(0, getResolvedFinalTop());
685                if (typeof onComplete === 'function') {
686                    onComplete();
687                }
688            }, 0);
689
690            return;
691        }
692
693        var startTime = window.performance && window.performance.now ? window.performance.now() : Date.now();
694
695        function step(timestamp) {
696            var elapsed = timestamp - startTime;
697            var progress = Math.min(elapsed / scrollDuration, 1);
698            var easedProgress = easeInOutGentle(progress);
699
700            window.scrollTo(0, startTop + (endTop - startTop) * easedProgress);
701
702            if (progress < 1) {
703                anchorScrollAnimation = window.requestAnimationFrame(step);
704            } else {
705                anchorScrollAnimation = null;
706                window.setTimeout(function() {
707                    window.scrollTo(0, getResolvedFinalTop());
708                    if (typeof onComplete === 'function') {
709                        onComplete();
710                    }
711                }, 120);
712            }
713        }
714
715        anchorScrollAnimation = window.requestAnimationFrame(step);
716    }
717
718    function scrollToAnchor(id, duration, onComplete) {
719        scrollToTop(getAnchorScrollTop(id), duration, function() {
720            return getAnchorScrollTop(id);
721        }, onComplete);
722    }
723
724    window.__apiDocsScrollToAnchor = scrollToAnchor;
725
726    function revealInitialAnchorLoad() {
727        document.documentElement.classList.remove('api-docs-anchor-loading');
728    }
729
730    function settleInitialHashPosition(onComplete) {
731        settleCurrentHashPosition(0, onComplete);
732    }
733
734    function settleCurrentHashPosition(duration, onComplete) {
735        var id = window.location.hash || window.__apiDocsInitialHash;
736
737        if (!id) {
738            if (typeof onComplete === 'function') {
739                onComplete();
740            }
741            return;
742        }
743
744        if (id === '#top') {
745            scrollToTop(0, duration, function() {
746                return 0;
747            }, function() {
748                if (typeof onComplete === 'function') {
749                    onComplete();
750                }
751            });
752            return;
753        }
754
755        if (getAnchorTarget(id).length > 0) {
756            scrollToAnchor(id, duration, function() {
757                if (!window.location.hash && window.history && window.history.replaceState) {
758                    window.history.replaceState(null, '', window.location.pathname + window.location.search + id);
759                }
760                if (typeof onComplete === 'function') {
761                    onComplete();
762                }
763            });
764            return;
765        }
766
767        if (typeof onComplete === 'function') {
768            onComplete();
769        }
770    }
771
772    window.__apiDocsSettleCurrentHash = settleCurrentHashPosition;
773
774    function getElementScrollTop($element, extraOffset) {
775        if ($element.length === 0) {
776            return 0;
777        }
778
779        return Math.max(0, parseInt($element.offset().top, 10) - getAnchorOffset() - (extraOffset || 0));
780    }
781
782    function scrollToElement($element, duration, extraOffset) {
783        if ($element.length === 0) {
784            return;
785        }
786
787        scrollToTop(getElementScrollTop($element, extraOffset), duration, function() {
788            return getElementScrollTop($element, extraOffset);
789        });
790    }
791
792    function scrollTabIntoView($tabs, tab) {
793        if ($tabs.length === 0 || !tab) {
794            return;
795        }
796
797        var tabs = $tabs[0];
798        var tabRect = tab.getBoundingClientRect();
799        var tabsRect = tabs.getBoundingClientRect();
800        var buffer = 20;
801
802        if (tabRect.left < tabsRect.left + buffer) {
803            tabs.scrollLeft -= tabsRect.left + buffer - tabRect.left;
804        } else if (tabRect.right > tabsRect.right - buffer) {
805            tabs.scrollLeft += tabRect.right - (tabsRect.right - buffer);
806        }
807
808        updateExampleTabsOverflow($tabs.closest('.api-tabs-overflow'));
809    }
810
811    function updateExampleTabsOverflow($wrap) {
812        var tabs = $wrap.find('> .nav-tabs-examples')[0];
813        var contentWidth = 0;
814        var hasOverflow;
815        var maxScroll;
816        var scrollLeft;
817
818        if (!tabs) {
819            return;
820        }
821
822        $wrap.find('> .nav-tabs-examples > li').each(function() {
823            contentWidth += $(this).outerWidth(true);
824        });
825        hasOverflow = contentWidth + 20 > $wrap.width() + 2;
826        $wrap.toggleClass('has-overflow', hasOverflow);
827
828        maxScroll = Math.max(0, tabs.scrollWidth - tabs.clientWidth);
829        scrollLeft = Math.max(0, Math.min(tabs.scrollLeft, maxScroll));
830
831        $wrap
832            .toggleClass('can-scroll-left', hasOverflow && scrollLeft > 2)
833            .toggleClass('can-scroll-right', hasOverflow && scrollLeft < maxScroll - 2);
834    }
835
836    function scrollExampleTabs($wrap, direction) {
837        var tabs = $wrap.find('> .nav-tabs-examples')[0];
838        var distance;
839
840        if (!tabs) {
841            return;
842        }
843
844        distance = Math.max(160, tabs.clientWidth * 0.75) * direction;
845
846        if (tabs.scrollBy) {
847            tabs.scrollBy({ left: distance, behavior: 'smooth' });
848        } else {
849            tabs.scrollLeft += distance;
850        }
851
852        window.setTimeout(function() {
853            updateExampleTabsOverflow($wrap);
854        }, 260);
855    }
856
857    function enhanceExampleTabs($root) {
858        $root.find('.nav-tabs-examples').each(function() {
859            var $tabs = $(this);
860            var $wrap = $tabs.closest('.api-tabs-overflow');
861
862            if ($wrap.length === 0) {
863                $tabs.wrap('<div class="api-tabs-overflow"></div>');
864                $wrap = $tabs.parent();
865                $wrap.prepend('<button type="button" class="api-tabs-arrow api-tabs-arrow--left" aria-label="Scroll tabs left"><img src="/images/square-arrow-left-02-sharp.svg" alt="" aria-hidden="true"></button>');
866                $wrap.append('<button type="button" class="api-tabs-arrow api-tabs-arrow--right" aria-label="Scroll tabs right"><img src="/images/square-arrow-right-02-sharp.svg" alt="" aria-hidden="true"></button>');
867                $tabs.on('scroll.apiTabsOverflow', function() {
868                    updateExampleTabsOverflow($wrap);
869                });
870                $wrap.on('click.apiTabsOverflow', '.api-tabs-arrow', function(e) {
871                    e.preventDefault();
872                    scrollExampleTabs($wrap, $(this).hasClass('api-tabs-arrow--left') ? -1 : 1);
873                });
874            }
875
876            updateExampleTabsOverflow($wrap);
877        });
878
879        if (!exampleTabsResizeBound) {
880            exampleTabsResizeBound = true;
881            $(window).on('resize.apiTabsOverflow', debounceApiDocs(function() {
882                $('.api-tabs-overflow').each(function() {
883                    updateExampleTabsOverflow($(this));
884                });
885            }, 120));
886        }
887    }
888
889    // Bootstrap Scrollspy
890    $(this).scrollspy({ target: '#scrollingNav', offset: getAnchorOffset() });
891
892    function handleApiAnchorClick(e) {
893        e.preventDefault();
894
895        var id = this.hash || $(this).attr('href');
896
897        if (id.indexOf('#api-') !== 0) {
898            return true;
899        }
900
901        if (getAnchorTarget(id).length > 0) {
902            scrollToAnchor(id);
903            if (window.history && window.history.pushState) {
904                window.history.pushState(null, '', id);
905            }
906        }
907
908        return false;
909    }
910
911    // Content-Scroll on API anchor click.
912    $('#scrollingNav').on('click', '.sidenav a[href^="#"]', handleApiAnchorClick);
913    $('#connectApiContainer').on('click', 'a[href^="#api-"]', handleApiAnchorClick);
914
915    // Quickjump on page load, including hashes preserved while async docs render.
916    if(initialAnchorHash) {
917        settleInitialHashPosition(revealInitialAnchorLoad);
918    } else {
919        revealInitialAnchorLoad();
920    }
921
922    /**
923     * Check if Parameter (sub) List has a type Field.
924     * Example: @apiSuccess          varname1 No type.
925     *          @apiSuccess {String} varname2 With type.
926     *
927     * @param {Object} fields
928     */
929    function _hasTypeInFields(fields) {
930        var result = false;
931        $.each(fields, function(name) {
932            result = result || _.some(fields[name], function(item) { return item.type; });
933        });
934        return result;
935    }
936
937    /**
938     * On Template changes, recall plugins.
939     */
940    function activateFirstExampleTabs($root) {
941        $root.find('.nav-tabs-examples').each(function() {
942            var $tabs = $(this);
943            var hasActiveTab = $tabs.find('li.active, a.active').length > 0;
944
945            if (!hasActiveTab)
946                $tabs.find('a:first').tab('show');
947        });
948    }
949
950    function initDynamic() {
951        setApiDocsLoadingProgress(88);
952        // Bootstrap popover
953        $('button[data-toggle="popover"]').popover().click(function(e) {
954            e.preventDefault();
955        });
956
957        var version = $('#version strong').html();
958        $('#sidenav li').removeClass('is-new');
959        if (apiProject.template.withCompare) {
960            $('#sidenav li[data-version=\'' + version + '\']').each(function(){
961                var group = $(this).data('group');
962                var name = $(this).data('name');
963                var length = $('#sidenav li[data-group=\'' + group + '\'][data-name=\'' + name + '\']').length;
964                var index  = $('#sidenav li[data-group=\'' + group + '\'][data-name=\'' + name + '\']').index($(this));
965                if (length === 1 || index === (length - 1))
966                    $(this).addClass('is-new');
967            });
968        }
969
970        // tabs
971        activateFirstExampleTabs($('#connectMCApi'));
972        enhanceExampleTabs($('#connectMCApi'));
973
974        // sample request switch
975        $('.sample-request-switch').click(function (e) {
976            var name = '.' + $(this).attr('name') + '-fields';
977            $(name).addClass('hide');
978            $(this).parent().next(name).removeClass('hide');
979        });
980
981        // call scrollspy refresh method
982        $(window).scrollspy('refresh');
983
984        // init non-critical sample request handlers after the initial docs render path.
985        scheduleSampleRequestInit();
986    }
987    initDynamic();
988    resetInitialScrollPosition();
989
990    function runPostRenderEnhancements() {
991        // Run layout-affecting code-block enhancements behind the loader to avoid visible shifts.
992        prettyPrint();
993        enhancePreCopyButtons($('#connectMCApi'));
994        $(window).scrollspy('refresh');
995        setApiDocsLoadingProgress(96);
996    }
997
998    runPostRenderEnhancements();
999    settleInitialHashPosition(function() {
1000        revealInitialAnchorLoad();
1001        revealApiDocsPage();
1002    });
1003
1004    window.setTimeout(resetInitialScrollPosition, 0);
1005    window.setTimeout(resetInitialScrollPosition, 250);
1006    window.setTimeout(resetInitialScrollPosition, 750);
1007    window.setTimeout(resetInitialScrollPosition, 1200);
1008    window.setTimeout(resetInitialScrollPosition, 2500);
1009
1010    $('#connectMCApi').on('click', '.nav-tabs-examples a[href^="#"]', function(e) {
1011        var $tab = $(this);
1012        var $tabs = $tab.closest('.nav-tabs-examples');
1013        var wasActive = $tab.parent('li').hasClass('active');
1014        var scrollToTabs = function() {
1015            scrollTabIntoView($tabs, $tab[0]);
1016            scrollToElement($tabs.length > 0 ? $tabs : $tab);
1017        };
1018
1019        e.preventDefault();
1020        e.stopImmediatePropagation();
1021
1022        if (!wasActive)
1023            $tab.one('shown.bs.tab', scrollToTabs);
1024
1025        $tab.tab('show');
1026
1027        if (wasActive)
1028            window.setTimeout(scrollToTabs, 0);
1029    });
1030
1031    $('#connectMCApi').on('click', '.api-copy-button', function(e) {
1032        e.preventDefault();
1033        e.stopPropagation();
1034
1035        var button = this;
1036        var $button = $(button);
1037        var $wrapper = $button.closest('.api-pre-wrapper');
1038        var $message = $wrapper.find('> .api-copy-message');
1039        var text = getPreCopyText($wrapper.find('> pre')[0]);
1040        var resetCopyButton = function() {
1041            $button.removeClass('is-copied');
1042            $message.removeClass('is-visible').text('');
1043            button.setAttribute('aria-label', 'Copy code');
1044            button.setAttribute('title', 'Copy code');
1045        };
1046        var activeTimeout = $button.data('copyTimeout');
1047
1048        if (activeTimeout) {
1049            window.clearTimeout(activeTimeout);
1050        }
1051
1052        $button.addClass('is-copied');
1053        $message.text('Copied!').addClass('is-visible');
1054        button.setAttribute('aria-label', 'Copied');
1055        button.setAttribute('title', 'Copied');
1056
1057        copyTextToClipboard(text);
1058
1059        $button.data('copyTimeout', window.setTimeout(resetCopyButton, 3000));
1060    });
1061
1062    //
1063    // HTML-Template specific jQuery-Functions
1064    //
1065    // Change Main Version
1066    $('#versions li.version a').on('click', function(e) {
1067        e.preventDefault();
1068
1069        var selectedVersion = $(this).html();
1070        $('#version strong').html(selectedVersion);
1071
1072        // hide all
1073        $('article').addClass('hide');
1074        $('#sidenav li:not(.nav-fixed)').addClass('hide');
1075
1076        // show 1st equal or lower Version of each entry
1077        $('article[data-version]').each(function(index) {
1078            var group = $(this).data('group');
1079            var name = $(this).data('name');
1080            var version = $(this).data('version');
1081
1082            if (semver.lte(version, selectedVersion)) {
1083                if ($('article[data-group=\'' + group + '\'][data-name=\'' + name + '\']:visible').length === 0) {
1084                    // enable Article
1085                    $('article[data-group=\'' + group + '\'][data-name=\'' + name + '\'][data-version=\'' + version + '\']').removeClass('hide');
1086                    // enable Navigation
1087                    $('#sidenav li[data-group=\'' + group + '\'][data-name=\'' + name + '\'][data-version=\'' + version + '\']').removeClass('hide');
1088                    $('#sidenav li.nav-header[data-group=\'' + group + '\']').removeClass('hide');
1089                }
1090            }
1091        });
1092
1093        // show 1st equal or lower Version of each entry
1094        $('article[data-version]').each(function(index) {
1095            var group = $(this).data('group');
1096            $('section#api-' + group).removeClass('hide');
1097            if ($('section#api-' + group + ' article:visible').length === 0) {
1098                $('section#api-' + group).addClass('hide');
1099            } else {
1100                $('section#api-' + group).removeClass('hide');
1101            }
1102        });
1103
1104        initDynamic();
1105        return;
1106    });
1107
1108    // compare all article with their predecessor
1109    $('#compareAllWithPredecessor').on('click', changeAllVersionCompareTo);
1110
1111    // change version of an article
1112    $('article .versions li.version a').on('click', changeVersionCompareTo);
1113
1114    // compare url-parameter
1115    $.urlParam = function(name) {
1116        var results = new RegExp('[\\?&amp;]' + name + '=([^&amp;#]*)').exec(window.location.href);
1117        return (results && results[1]) ? results[1] : null;
1118    };
1119
1120    if ($.urlParam('compare')) {
1121        // URL Paramter ?compare=1 is set
1122        $('#compareAllWithPredecessor').trigger('click');
1123
1124        if (window.location.hash) {
1125            var id = window.location.hash;
1126            scrollToAnchor(id, 0);
1127        }
1128    }
1129
1130    /**
1131     * Initialize search
1132     */
1133    var options = {
1134      valueNames: [ 'nav-list-item' ]
1135    };
1136    var endpointsList = new List('scrollingNav', options);
1137    var $sidebarSearch = $('#scrollingNav .sidenav-search input.search');
1138    var $searchReset = $('#scrollingNav span.search-reset');
1139    var $sidebarList = $('#scrollingNav .sidenav');
1140    var keyboardResultIndex = -1;
1141
1142    $sidebarList.attr('id', 'api-docs-sidenav-results');
1143    $sidebarSearch.attr({
1144      'role': 'combobox',
1145      'aria-autocomplete': 'list',
1146      'aria-controls': 'api-docs-sidenav-results',
1147      'aria-expanded': 'true'
1148    });
1149
1150    $searchReset
1151      .empty()
1152      .append('<img src="/images/cancel-circle.svg" alt="" aria-hidden="true">')
1153      .attr({
1154        'aria-label': 'Clear filter',
1155        'role': 'button',
1156        'tabindex': '0',
1157        'title': 'Clear filter'
1158      });
1159
1160    var $sidebarEmptyState = $('<li class="api-docs-empty-state" aria-live="polite"><div class="api-docs-empty-state__title">No items found for the current filters.</div><div class="api-docs-empty-state__subhead">Consider changing your filter options to see more results.</div></li>');
1161
1162    function getVisibleSidebarResults() {
1163      return $sidebarList.children('li:visible').not('.api-docs-empty-state').children('a:visible');
1164    }
1165
1166    function clearKeyboardResult() {
1167      keyboardResultIndex = -1;
1168      $sidebarList.children('li').removeClass('api-docs-keyboard-active');
1169      $sidebarSearch.removeAttr('aria-activedescendant');
1170    }
1171
1172    function focusSidebarSearchWithoutScrolling() {
1173      var input = $sidebarSearch.get(0);
1174
1175      if (!input) {
1176        return;
1177      }
1178
1179      try {
1180        input.focus({ preventScroll: true });
1181      } catch (error) {
1182        input.focus();
1183      }
1184    }
1185
1186    function keepKeyboardResultVisible(link) {
1187      var list = $sidebarList.get(0);
1188      var linkRect;
1189      var listRect;
1190
1191      if (!list || !link) {
1192        return;
1193      }
1194
1195      linkRect = link.getBoundingClientRect();
1196      listRect = list.getBoundingClientRect();
1197
1198      if (linkRect.top < listRect.top) {
1199        list.scrollTop -= listRect.top - linkRect.top;
1200      } else if (linkRect.bottom > listRect.bottom) {
1201        list.scrollTop += linkRect.bottom - listRect.bottom;
1202      }
1203    }
1204
1205    function selectKeyboardResult(index) {
1206      var $results = getVisibleSidebarResults();
1207      var resultCount = $results.length;
1208      var $result;
1209      var resultId;
1210
1211      if (!resultCount) {
1212        clearKeyboardResult();
1213        return;
1214      }
1215
1216      keyboardResultIndex = (index + resultCount) % resultCount;
1217      $sidebarList.children('li').removeClass('api-docs-keyboard-active');
1218      $result = $results.eq(keyboardResultIndex);
1219      $result.parent().addClass('api-docs-keyboard-active');
1220      resultId = $result.attr('id') || 'api-docs-filter-result-' + String($result.attr('href') || keyboardResultIndex).replace(/[^a-z0-9_-]+/gi, '-');
1221      $result.attr('id', resultId);
1222      $sidebarSearch.attr('aria-activedescendant', resultId);
1223      keepKeyboardResultVisible($result.get(0));
1224    }
1225
1226    document.getElementById('scrollingNav').addEventListener('keydown', function(e) {
1227      var link = e.target.closest('.sidenav a[href^="#"]');
1228      var $results;
1229      var linkIndex;
1230      var selectedLink;
1231
1232      if (!link) {
1233        return;
1234      }
1235
1236      if (e.key === 'Enter' || e.keyCode === 13) {
1237        if (link.parentElement.classList.contains('api-docs-keyboard-active')) {
1238          e.preventDefault();
1239          link.click();
1240          $sidebarList.children('li').removeClass('api-docs-keyboard-active');
1241          $sidebarSearch.removeAttr('aria-activedescendant');
1242        }
1243        return;
1244      }
1245
1246      if (e.key !== 'ArrowDown' && e.key !== 'ArrowUp' && e.keyCode !== 40 && e.keyCode !== 38) {
1247        return;
1248      }
1249
1250      $results = getVisibleSidebarResults();
1251      linkIndex = $results.index(link);
1252
1253      if (linkIndex < 0) {
1254        return;
1255      }
1256
1257      e.preventDefault();
1258      keyboardResultIndex = linkIndex;
1259      selectKeyboardResult(linkIndex + ((e.key === 'ArrowUp' || e.keyCode === 38) ? -1 : 1));
1260      selectedLink = getVisibleSidebarResults().eq(keyboardResultIndex).get(0);
1261
1262      if (selectedLink) {
1263        try {
1264          selectedLink.focus({ preventScroll: true });
1265        } catch (error) {
1266          selectedLink.focus();
1267        }
1268      }
1269    }, true);
1270
1271    function updateSidebarEmptyState() {
1272      var hasQuery = $.trim($sidebarSearch.val()).length > 0;
1273      var hasMatches = endpointsList.matchingItems.length > 0;
1274      var shouldShowEmptyState = hasQuery && !hasMatches;
1275
1276      if (!$sidebarEmptyState.parent().length) {
1277        $sidebarList.append($sidebarEmptyState);
1278      }
1279
1280      $sidebarEmptyState
1281        .toggleClass('is-visible', shouldShowEmptyState)
1282        .toggle(shouldShowEmptyState);
1283      $sidebarSearch.attr('aria-expanded', hasMatches ? 'true' : 'false');
1284    }
1285
1286    if (endpointsList.on) {
1287      endpointsList.on('updated', function() {
1288        updateSidebarEmptyState();
1289      });
1290    }
1291
1292    /**
1293     * Detect ESC key to reset search
1294     */
1295    $(document).keyup(function(e) {
1296      if (e.keyCode === 27) $searchReset.click();
1297    });
1298
1299    $sidebarSearch.on('input', clearKeyboardResult);
1300
1301    $sidebarSearch.on('input keyup', function() {
1302      window.setTimeout(updateSidebarEmptyState, 0);
1303    });
1304
1305    $sidebarSearch.on('keydown', function(e) {
1306      var $results;
1307
1308      if (e.key === 'ArrowDown' || e.keyCode === 40) {
1309        e.preventDefault();
1310        selectKeyboardResult(keyboardResultIndex + 1);
1311        return;
1312      }
1313
1314      if (e.key === 'ArrowUp' || e.keyCode === 38) {
1315        e.preventDefault();
1316        $results = getVisibleSidebarResults();
1317        selectKeyboardResult(keyboardResultIndex < 0 ? $results.length - 1 : keyboardResultIndex - 1);
1318        return;
1319      }
1320
1321      if (e.key === 'Enter' || e.keyCode === 13) {
1322        $results = getVisibleSidebarResults();
1323
1324        if (!$results.length) {
1325          return;
1326        }
1327
1328        e.preventDefault();
1329        if (keyboardResultIndex < 0) {
1330          selectKeyboardResult(0);
1331          $results = getVisibleSidebarResults();
1332        }
1333        $results.eq(keyboardResultIndex).trigger('click');
1334        $sidebarList.children('li').removeClass('api-docs-keyboard-active');
1335        $sidebarSearch.removeAttr('aria-activedescendant');
1336        focusSidebarSearchWithoutScrolling();
1337      }
1338    });
1339
1340    /**
1341     * Search reset
1342     */
1343    $searchReset.on('click', function() {
1344      $sidebarSearch
1345        .val("")
1346        .focus()
1347      ;
1348      endpointsList.search();
1349      updateSidebarEmptyState();
1350      clearKeyboardResult();
1351    });
1352
1353    $searchReset.on('keydown', function(e) {
1354      if (e.keyCode === 13 || e.keyCode === 32) {
1355        e.preventDefault();
1356        $searchReset.click();
1357      }
1358    });
1359
1360    /**
1361     * Change version of an article to compare it to an other version.
1362     */
1363    function changeVersionCompareTo(e) {
1364        e.preventDefault();
1365
1366        var $root = $(this).parents('article');
1367        var selectedVersion = $(this).html();
1368        var $button = $root.find('.version');
1369        var currentVersion = $button.find('strong').html();
1370        $button.find('strong').html(selectedVersion);
1371
1372        var group = $root.data('group');
1373        var name = $root.data('name');
1374        var version = $root.data('version');
1375
1376        var compareVersion = $root.data('compare-version');
1377
1378        if (compareVersion === selectedVersion)
1379            return;
1380
1381        if ( ! compareVersion && version == selectedVersion)
1382            return;
1383
1384        if (compareVersion && articleVersions[group][name][0] === selectedVersion || version === selectedVersion) {
1385            // the version of the entry is set to the highest version (reset)
1386            resetArticle(group, name, version);
1387        } else {
1388            var $compareToArticle = $('article[data-group=\'' + group + '\'][data-name=\'' + name + '\'][data-version=\'' + selectedVersion + '\']');
1389
1390            var sourceEntry = {};
1391            var compareEntry = {};
1392            $.each(apiByGroupAndName[group][name], function(index, entry) {
1393                if (entry.version === version)
1394                    sourceEntry = entry;
1395                if (entry.version === selectedVersion)
1396                    compareEntry = entry;
1397            });
1398
1399            var fields = {
1400                article: sourceEntry,
1401                compare: compareEntry,
1402                versions: articleVersions[group][name]
1403            };
1404
1405            // add unique id
1406            // TODO: replace all group-name-version in template with id.
1407            fields.article.id = fields.article.group + '-' + fields.article.name + '-' + fields.article.version;
1408            fields.article.id = fields.article.id.replace(/\./g, '_');
1409
1410            fields.compare.id = fields.compare.group + '-' + fields.compare.name + '-' + fields.compare.version;
1411            fields.compare.id = fields.compare.id.replace(/\./g, '_');
1412
1413            var entry = sourceEntry;
1414            if (entry.parameter && entry.parameter.fields)
1415                fields._hasTypeInParameterFields = _hasTypeInFields(entry.parameter.fields);
1416
1417            if (entry.error && entry.error.fields)
1418                fields._hasTypeInErrorFields = _hasTypeInFields(entry.error.fields);
1419
1420            if (entry.success && entry.success.fields)
1421                fields._hasTypeInSuccessFields = _hasTypeInFields(entry.success.fields);
1422
1423            if (entry.info && entry.info.fields)
1424                fields._hasTypeInInfoFields = _hasTypeInFields(entry.info.fields);
1425
1426            var entry = compareEntry;
1427            if (fields._hasTypeInParameterFields !== true && entry.parameter && entry.parameter.fields)
1428                fields._hasTypeInParameterFields = _hasTypeInFields(entry.parameter.fields);
1429
1430            if (fields._hasTypeInErrorFields !== true && entry.error && entry.error.fields)
1431                fields._hasTypeInErrorFields = _hasTypeInFields(entry.error.fields);
1432
1433            if (fields._hasTypeInSuccessFields !== true && entry.success && entry.success.fields)
1434                fields._hasTypeInSuccessFields = _hasTypeInFields(entry.success.fields);
1435
1436            if (fields._hasTypeInInfoFields !== true && entry.info && entry.info.fields)
1437                fields._hasTypeInInfoFields = _hasTypeInFields(entry.info.fields);
1438
1439            var content = templateCompareArticle(fields);
1440            $root.after(content);
1441            var $content = $root.next();
1442
1443            // Event on.click re-assign
1444            $content.find('.versions li.version a').on('click', changeVersionCompareTo);
1445
1446            // select navigation
1447            $('#sidenav li[data-group=\'' + group + '\'][data-name=\'' + name + '\'][data-version=\'' + currentVersion + '\']').addClass('has-modifications');
1448
1449            $root.remove();
1450            // TODO: on change main version or select the highest version re-render
1451        }
1452
1453        initDynamic();
1454    }
1455
1456    /**
1457     * Compare all currently selected Versions with their predecessor.
1458     */
1459    function changeAllVersionCompareTo(e) {
1460        e.preventDefault();
1461        $('article:visible .versions').each(function(){
1462            var $root = $(this).parents('article');
1463            var currentVersion = $root.data('version');
1464            var $foundElement = null;
1465            $(this).find('li.version a').each(function() {
1466                var selectVersion = $(this).html();
1467                if (selectVersion < currentVersion && ! $foundElement)
1468                    $foundElement = $(this);
1469            });
1470
1471            if($foundElement)
1472                $foundElement.trigger('click');
1473        });
1474        initDynamic();
1475    }
1476
1477    /**
1478     * Sort the fields.
1479     */
1480    function sortFields(fields_object) {
1481        $.each(fields_object, function (key, fields) {
1482
1483            var reversed = fields.slice().reverse()
1484
1485            var max_dot_count = Math.max.apply(null, reversed.map(function (item) {
1486                return item.field.split(".").length - 1;
1487            }))
1488
1489            for (var dot_count = 1; dot_count <= max_dot_count; dot_count++) {
1490                reversed.forEach(function (item, index) {
1491                    var parts = item.field.split(".");
1492                    if (parts.length - 1 == dot_count) {
1493                        var fields_names = fields.map(function (item) { return item.field; });
1494                        if (parts.slice(1).length  >= 1) {
1495                            var prefix = parts.slice(0, parts.length - 1).join(".");
1496                            var prefix_index = fields_names.indexOf(prefix);
1497                            if (prefix_index > -1) {
1498                                fields.splice(fields_names.indexOf(item.field), 1);
1499                                fields.splice(prefix_index + 1, 0, item);
1500                            }
1501                        }
1502                    }
1503                });
1504            }
1505        });
1506    }
1507
1508    /**
1509     * Add article settings.
1510     */
1511    function addArticleSettings(fields, entry) {
1512        // add unique id
1513        // TODO: replace all group-name-version in template with id.
1514        fields.id = fields.article.group + '-' + fields.article.name + '-' + fields.article.version;
1515        fields.id = fields.id.replace(/\./g, '_');
1516
1517        if (entry.header && entry.header.fields) {
1518            sortFields(entry.header.fields);
1519            fields._hasTypeInHeaderFields = _hasTypeInFields(entry.header.fields);
1520        }
1521
1522        if (entry.parameter && entry.parameter.fields) {
1523            sortFields(entry.parameter.fields);
1524            fields._hasTypeInParameterFields = _hasTypeInFields(entry.parameter.fields);
1525        }
1526
1527        if (entry.error && entry.error.fields) {
1528            sortFields(entry.error.fields);
1529            fields._hasTypeInErrorFields = _hasTypeInFields(entry.error.fields);
1530        }
1531
1532        if (entry.success && entry.success.fields) {
1533            sortFields(entry.success.fields);
1534            fields._hasTypeInSuccessFields = _hasTypeInFields(entry.success.fields);
1535        }
1536
1537        if (entry.info && entry.info.fields) {
1538            sortFields(entry.info.fields);
1539            fields._hasTypeInInfoFields = _hasTypeInFields(entry.info.fields);
1540        }
1541
1542        // add template settings
1543        fields.template = apiProject.template;
1544    }
1545
1546    /**
1547     * Render Article.
1548     */
1549    function renderArticle(group, name, version) {
1550        var entry = {};
1551        $.each(apiByGroupAndName[group][name], function(index, currentEntry) {
1552            if (currentEntry.version === version)
1553                entry = currentEntry;
1554        });
1555        var fields = {
1556            article: entry,
1557            versions: articleVersions[group][name]
1558        };
1559
1560        addArticleSettings(fields, entry);
1561
1562        return templateArticle(fields);
1563    }
1564
1565    /**
1566     * Render original Article and remove the current visible Article.
1567     */
1568    function resetArticle(group, name, version) {
1569        var $root = $('article[data-group=\'' + group + '\'][data-name=\'' + name + '\']:visible');
1570        var content = renderArticle(group, name, version);
1571
1572        $root.after(content);
1573        var $content = $root.next();
1574
1575        // Event on.click muss neu zugewiesen werden (sollte eigentlich mit on automatisch funktionieren... sollte)
1576        $content.find('.versions li.version a').on('click', changeVersionCompareTo);
1577        enhancePreCopyButtons($content);
1578        activateFirstExampleTabs($content);
1579        scheduleSampleRequestInit();
1580
1581        $('#sidenav li[data-group=\'' + group + '\'][data-name=\'' + name + '\'][data-version=\'' + version + '\']').removeClass('has-modifications');
1582
1583        $root.remove();
1584        return;
1585    }
1586
1587    /**
1588     * Load google fonts.
1589     */
1590    function loadGoogleFontCss() {
1591        WebFont.load({
1592            active: function() {
1593                // Update scrollspy
1594                $(window).scrollspy('refresh')
1595            },
1596            google: {
1597                families: ['Source Code Pro', 'Source Sans Pro:n4,n6,n7']
1598            }
1599        });
1600    }
1601
1602    /**
1603     * Return ordered entries by custom order and append not defined entries to the end.
1604     * @param  {String[]} elements
1605     * @param  {String[]} order
1606     * @param  {String}   splitBy
1607     * @return {String[]} Custom ordered list.
1608     */
1609    function sortByOrder(elements, order, splitBy) {
1610        var results = [];
1611        order.forEach (function(name) {
1612            if (splitBy)
1613                elements.forEach (function(element) {
1614                    var parts = element.split(splitBy);
1615                    var key = parts[1]; // reference keep for sorting
1616                    if (key == name)
1617                        results.push(element);
1618                });
1619            else
1620                elements.forEach (function(key) {
1621                    if (key == name)
1622                        results.push(name);
1623                });
1624        });
1625        // Append all other entries that ar not defined in order
1626        elements.forEach(function(element) {
1627            if (results.indexOf(element) === -1)
1628                results.push(element);
1629        });
1630        return results;
1631    }
1632
1633}, function(error) {
1634    // Let the host page recover when a selected release does not publish API-doc assets.
1635    if (typeof window.__apiDocsHandleLoadError === 'function') {
1636        window.__apiDocsHandleLoadError(error);
1637        return;
1638    }
1639
1640    throw error;
1641});

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.