xref: /OpenGrok/opengrok-web/src/main/webapp/js/utils-0.0.45.js (revision d7d00a41c3c13c165bccbd6a05b3c7531cc912e0)
1*d7d00a41SKrystof Tulinger/*
2*d7d00a41SKrystof Tulinger * CDDL HEADER START
3*d7d00a41SKrystof Tulinger *
4*d7d00a41SKrystof Tulinger * The contents of this file are subject to the terms of the
5*d7d00a41SKrystof Tulinger * Common Development and Distribution License (the "License").
6*d7d00a41SKrystof Tulinger * You may not use this file except in compliance with the License.
7*d7d00a41SKrystof Tulinger *
8*d7d00a41SKrystof Tulinger * See LICENSE.txt included in this distribution for the specific
9*d7d00a41SKrystof Tulinger * language governing permissions and limitations under the License.
10*d7d00a41SKrystof Tulinger *
11*d7d00a41SKrystof Tulinger * When distributing Covered Code, include this CDDL HEADER in each
12*d7d00a41SKrystof Tulinger * file and include the License file at LICENSE.txt.
13*d7d00a41SKrystof Tulinger * If applicable, add the following below this CDDL HEADER, with the
14*d7d00a41SKrystof Tulinger * fields enclosed by brackets "[]" replaced with your own identifying
15*d7d00a41SKrystof Tulinger * information: Portions Copyright [yyyy] [name of copyright owner]
16*d7d00a41SKrystof Tulinger *
17*d7d00a41SKrystof Tulinger * CDDL HEADER END
18*d7d00a41SKrystof Tulinger */
19*d7d00a41SKrystof Tulinger
20*d7d00a41SKrystof Tulinger/*
21*d7d00a41SKrystof Tulinger * Copyright (c) 2009, 2021, Oracle and/or its affiliates. All rights reserved.
22*d7d00a41SKrystof Tulinger * Portions Copyright 2011 Jens Elkner.
23*d7d00a41SKrystof Tulinger * Portions Copyright (c) 2017, 2020, Chris Fraire <cfraire@me.com>.
24*d7d00a41SKrystof Tulinger */
25*d7d00a41SKrystof Tulinger
26*d7d00a41SKrystof Tulinger/**
27*d7d00a41SKrystof Tulinger * Spaces plugin.
28*d7d00a41SKrystof Tulinger *
29*d7d00a41SKrystof Tulinger * Inserts a dummy space between line number and the text so that on copy-paste
30*d7d00a41SKrystof Tulinger * the white space is preserved.
31*d7d00a41SKrystof Tulinger *
32*d7d00a41SKrystof Tulinger * Internally listens on scroll events and autofills the spaces only for the visible
33*d7d00a41SKrystof Tulinger * elements.
34*d7d00a41SKrystof Tulinger *
35*d7d00a41SKrystof Tulinger * IMPORTANT: This plugin is strictly dependent on ascending order of lines
36*d7d00a41SKrystof Tulinger * and on their attribute "name". It performs a binary search which boosts performance
37*d7d00a41SKrystof Tulinger * of this plugin for really long files.
38*d7d00a41SKrystof Tulinger *
39*d7d00a41SKrystof Tulinger * @author Krystof Tulinger
40*d7d00a41SKrystof Tulinger */
41*d7d00a41SKrystof Tulinger(function (w, $) {
42*d7d00a41SKrystof Tulinger    const spaces = function () {
43*d7d00a41SKrystof Tulinger        const inner = {
44*d7d00a41SKrystof Tulinger            defaults: {
45*d7d00a41SKrystof Tulinger                interval: 750,
46*d7d00a41SKrystof Tulinger                selector: "a.l, a.hl",
47*d7d00a41SKrystof Tulinger                $parent: null,
48*d7d00a41SKrystof Tulinger                callback: function () {
49*d7d00a41SKrystof Tulinger                    if (!$(this).hasClass("selected")) {
50*d7d00a41SKrystof Tulinger                        $(this).addClass("selected");
51*d7d00a41SKrystof Tulinger                        $(this).text($(this).text() + " ");
52*d7d00a41SKrystof Tulinger                    }
53*d7d00a41SKrystof Tulinger                }
54*d7d00a41SKrystof Tulinger            },
55*d7d00a41SKrystof Tulinger            options: {},
56*d7d00a41SKrystof Tulinger            $collection: $(),
57*d7d00a41SKrystof Tulinger            initialized: false,
58*d7d00a41SKrystof Tulinger            lock: false,
59*d7d00a41SKrystof Tulinger            binarySearch: function (array, key, compare) {
60*d7d00a41SKrystof Tulinger                let lo = 0;
61*d7d00a41SKrystof Tulinger                let hi = array.length - 1;
62*d7d00a41SKrystof Tulinger                while (lo <= hi) {
63*d7d00a41SKrystof Tulinger                    const mid = ((lo + hi) >> 1);
64*d7d00a41SKrystof Tulinger                    const cmp = compare(array[mid], key);
65*d7d00a41SKrystof Tulinger                    if (cmp === 0) {
66*d7d00a41SKrystof Tulinger                        return mid;
67*d7d00a41SKrystof Tulinger                    } else if (cmp < 0) {
68*d7d00a41SKrystof Tulinger                        lo = mid + 1;
69*d7d00a41SKrystof Tulinger                    } else {
70*d7d00a41SKrystof Tulinger                        hi = mid - 1;
71*d7d00a41SKrystof Tulinger                    }
72*d7d00a41SKrystof Tulinger                }
73*d7d00a41SKrystof Tulinger                return -1;
74*d7d00a41SKrystof Tulinger            },
75*d7d00a41SKrystof Tulinger            handleScrollEvent: function () {
76*d7d00a41SKrystof Tulinger                inner.lock = false;
77*d7d00a41SKrystof Tulinger
78*d7d00a41SKrystof Tulinger                const myOffset = inner.$collection.first().offset() ? inner.$collection.first().offset().top : 0;
79*d7d00a41SKrystof Tulinger                const myHeight = inner.$collection.first().height() || 0;
80*d7d00a41SKrystof Tulinger                const parentOffset = inner.options.$parent.offset() ? inner.options.$parent.offset().top : 0;
81*d7d00a41SKrystof Tulinger                const parentHeight = inner.options.$parent.height() || 0;
82*d7d00a41SKrystof Tulinger
83*d7d00a41SKrystof Tulinger                const expectations = {
84*d7d00a41SKrystof Tulinger                    // the first element in viewport
85*d7d00a41SKrystof Tulinger                    start: Math.floor(Math.abs(Math.min(myOffset - parentOffset, 0)) / myHeight),
86*d7d00a41SKrystof Tulinger                    // the last element in viewport
87*d7d00a41SKrystof Tulinger                    end: Math.ceil((Math.abs(Math.min(myOffset - parentOffset, 0)) + parentHeight) / myHeight)
88*d7d00a41SKrystof Tulinger                };
89*d7d00a41SKrystof Tulinger
90*d7d00a41SKrystof Tulinger                const indices = {
91*d7d00a41SKrystof Tulinger                    start: 0,
92*d7d00a41SKrystof Tulinger                    end: inner.$collection.length
93*d7d00a41SKrystof Tulinger                };
94*d7d00a41SKrystof Tulinger
95*d7d00a41SKrystof Tulinger                const cmp = function (a, key) {
96*d7d00a41SKrystof Tulinger                    return $(a).attr("name") - key; // comparing the "name" attribute with the desired value
97*d7d00a41SKrystof Tulinger                };
98*d7d00a41SKrystof Tulinger
99*d7d00a41SKrystof Tulinger
100*d7d00a41SKrystof Tulinger                indices.start = inner.binarySearch(inner.$collection, expectations.start, cmp);
101*d7d00a41SKrystof Tulinger                indices.end = inner.binarySearch(inner.$collection, expectations.end, cmp);
102*d7d00a41SKrystof Tulinger
103*d7d00a41SKrystof Tulinger                /** cutoffs */
104*d7d00a41SKrystof Tulinger                indices.start = Math.max(0, indices.start);
105*d7d00a41SKrystof Tulinger                indices.start = Math.min(inner.$collection.length - 1, indices.start);
106*d7d00a41SKrystof Tulinger
107*d7d00a41SKrystof Tulinger                if (indices.end === -1) {
108*d7d00a41SKrystof Tulinger                    indices.end = inner.$collection.length - 1;
109*d7d00a41SKrystof Tulinger                }
110*d7d00a41SKrystof Tulinger                indices.end = Math.min(inner.$collection.length - 1, indices.end);
111*d7d00a41SKrystof Tulinger
112*d7d00a41SKrystof Tulinger                /** calling callback for every element in the viewport */
113*d7d00a41SKrystof Tulinger                for (var i = indices.start; i <= indices.end; i++) {
114*d7d00a41SKrystof Tulinger                    inner.options.callback.apply(inner.$collection[i]);
115*d7d00a41SKrystof Tulinger                }
116*d7d00a41SKrystof Tulinger            },
117*d7d00a41SKrystof Tulinger            init: function () {
118*d7d00a41SKrystof Tulinger
119*d7d00a41SKrystof Tulinger                if (inner.initialized) {
120*d7d00a41SKrystof Tulinger                    return;
121*d7d00a41SKrystof Tulinger                }
122*d7d00a41SKrystof Tulinger
123*d7d00a41SKrystof Tulinger                inner.$collection = inner.options.$parent.find(inner.options.selector);
124*d7d00a41SKrystof Tulinger
125*d7d00a41SKrystof Tulinger                if (inner.$collection.length <= 0) {
126*d7d00a41SKrystof Tulinger                    return;
127*d7d00a41SKrystof Tulinger                }
128*d7d00a41SKrystof Tulinger
129*d7d00a41SKrystof Tulinger                const scrollHandler = function (e) {
130*d7d00a41SKrystof Tulinger                    if (inner.lock) {
131*d7d00a41SKrystof Tulinger                        return;
132*d7d00a41SKrystof Tulinger                    }
133*d7d00a41SKrystof Tulinger                    inner.lock = true;
134*d7d00a41SKrystof Tulinger                    setTimeout(inner.handleScrollEvent, inner.options.interval);
135*d7d00a41SKrystof Tulinger                };
136*d7d00a41SKrystof Tulinger                // fire the event if user has not scrolled
137*d7d00a41SKrystof Tulinger                inner.options.$parent.scroll(scrollHandler).resize(scrollHandler).scroll();
138*d7d00a41SKrystof Tulinger                inner.initialized = true;
139*d7d00a41SKrystof Tulinger            }
140*d7d00a41SKrystof Tulinger        };
141*d7d00a41SKrystof Tulinger
142*d7d00a41SKrystof Tulinger        this.init = function (options) {
143*d7d00a41SKrystof Tulinger            inner.options = $.extend({}, inner.defaults, {$parent: $("#content")}, options);
144*d7d00a41SKrystof Tulinger            inner.init();
145*d7d00a41SKrystof Tulinger            return this;
146*d7d00a41SKrystof Tulinger        };
147*d7d00a41SKrystof Tulinger    };
148*d7d00a41SKrystof Tulinger
149*d7d00a41SKrystof Tulinger    $.spaces = new ($.extend(spaces, $.spaces ? $.spaces : {}))();
150*d7d00a41SKrystof Tulinger})(window, window.jQuery);
151*d7d00a41SKrystof Tulinger
152*d7d00a41SKrystof Tulinger/**
153*d7d00a41SKrystof Tulinger * Offseting the target anchors by the height of the fixed header.
154*d7d00a41SKrystof Tulinger * Code taken from http://jsfiddle.net/ianclark001/rkocah23/.
155*d7d00a41SKrystof Tulinger *
156*d7d00a41SKrystof Tulinger * If this is not used, clicking on a anchor
157*d7d00a41SKrystof Tulinger * with a hash target (href="#some-id") will
158*d7d00a41SKrystof Tulinger * lead to incorrect positioning at the top of the page.
159*d7d00a41SKrystof Tulinger */
160*d7d00a41SKrystof Tulinger(function (document, history, location) {
161*d7d00a41SKrystof Tulinger    const HISTORY_SUPPORT = !!(history && history.pushState);
162*d7d00a41SKrystof Tulinger
163*d7d00a41SKrystof Tulinger    const anchorScrolls = {
164*d7d00a41SKrystof Tulinger        ANCHOR_REGEX: /^#[^ ]+$/,
165*d7d00a41SKrystof Tulinger        OFFSET_HEIGHT_PX: 90,
166*d7d00a41SKrystof Tulinger
167*d7d00a41SKrystof Tulinger        /**
168*d7d00a41SKrystof Tulinger         * Establish events, and fix initial scroll position if a hash is provided.
169*d7d00a41SKrystof Tulinger         */
170*d7d00a41SKrystof Tulinger        init: function () {
171*d7d00a41SKrystof Tulinger            this.scrollToCurrent();
172*d7d00a41SKrystof Tulinger            $(window).on('hashchange', $.proxy(this, 'scrollToCurrent'));
173*d7d00a41SKrystof Tulinger            $('body').on('click', 'a', $.proxy(this, 'delegateAnchors'));
174*d7d00a41SKrystof Tulinger        },
175*d7d00a41SKrystof Tulinger
176*d7d00a41SKrystof Tulinger        /**
177*d7d00a41SKrystof Tulinger         * Return the offset amount to deduct from the normal scroll position.
178*d7d00a41SKrystof Tulinger         * Modify as appropriate to allow for dynamic calculations
179*d7d00a41SKrystof Tulinger         */
180*d7d00a41SKrystof Tulinger        getFixedOffset: function () {
181*d7d00a41SKrystof Tulinger            return this.OFFSET_HEIGHT_PX;
182*d7d00a41SKrystof Tulinger        },
183*d7d00a41SKrystof Tulinger
184*d7d00a41SKrystof Tulinger        /**
185*d7d00a41SKrystof Tulinger         * If the provided href is an anchor which resolves to an element on the
186*d7d00a41SKrystof Tulinger         * page, scroll to it.
187*d7d00a41SKrystof Tulinger         * @param  {String} href
188*d7d00a41SKrystof Tulinger         * @return {Boolean} - Was the href an anchor.
189*d7d00a41SKrystof Tulinger         */
190*d7d00a41SKrystof Tulinger        scrollIfAnchor: function (href, pushToHistory) {
191*d7d00a41SKrystof Tulinger            if (!this.ANCHOR_REGEX.test(href)) {
192*d7d00a41SKrystof Tulinger                return false;
193*d7d00a41SKrystof Tulinger            }
194*d7d00a41SKrystof Tulinger
195*d7d00a41SKrystof Tulinger            let match = document.getElementById(href.slice(1));
196*d7d00a41SKrystof Tulinger            if (!match) {
197*d7d00a41SKrystof Tulinger                /**
198*d7d00a41SKrystof Tulinger                 * Match the elements with name="href", take the first match
199*d7d00a41SKrystof Tulinger                 */
200*d7d00a41SKrystof Tulinger                match = document.getElementsByName(href.slice(1));
201*d7d00a41SKrystof Tulinger                match = match.length > 0 ? match[0] : null;
202*d7d00a41SKrystof Tulinger            }
203*d7d00a41SKrystof Tulinger
204*d7d00a41SKrystof Tulinger            if (match) {
205*d7d00a41SKrystof Tulinger                const anchorOffset = $(match.nextElementSibling).offset().top - this.getFixedOffset();
206*d7d00a41SKrystof Tulinger                $('html, body').animate({scrollTop: anchorOffset});
207*d7d00a41SKrystof Tulinger
208*d7d00a41SKrystof Tulinger                location.hash = href;
209*d7d00a41SKrystof Tulinger
210*d7d00a41SKrystof Tulinger                // Add the state to history as-per normal anchor links
211*d7d00a41SKrystof Tulinger                if (HISTORY_SUPPORT && pushToHistory) {
212*d7d00a41SKrystof Tulinger                    history.pushState({}, document.title, location.pathname + location.search + href);
213*d7d00a41SKrystof Tulinger                }
214*d7d00a41SKrystof Tulinger            }
215*d7d00a41SKrystof Tulinger
216*d7d00a41SKrystof Tulinger            return !!match;
217*d7d00a41SKrystof Tulinger        },
218*d7d00a41SKrystof Tulinger
219*d7d00a41SKrystof Tulinger        /**
220*d7d00a41SKrystof Tulinger         * Attempt to scroll to the current location's hash.
221*d7d00a41SKrystof Tulinger         */
222*d7d00a41SKrystof Tulinger        scrollToCurrent: function (e) {
223*d7d00a41SKrystof Tulinger            if (this.scrollIfAnchor(window.location.hash) && e) {
224*d7d00a41SKrystof Tulinger                e.preventDefault();
225*d7d00a41SKrystof Tulinger            }
226*d7d00a41SKrystof Tulinger        },
227*d7d00a41SKrystof Tulinger
228*d7d00a41SKrystof Tulinger        /**
229*d7d00a41SKrystof Tulinger         * If the click event's target was an anchor, fix the scroll position.
230*d7d00a41SKrystof Tulinger         */
231*d7d00a41SKrystof Tulinger        delegateAnchors: function (e) {
232*d7d00a41SKrystof Tulinger            const elem = e.target;
233*d7d00a41SKrystof Tulinger
234*d7d00a41SKrystof Tulinger            if (this.scrollIfAnchor(elem.getAttribute('href'), true)) {
235*d7d00a41SKrystof Tulinger                e.preventDefault();
236*d7d00a41SKrystof Tulinger            }
237*d7d00a41SKrystof Tulinger        }
238*d7d00a41SKrystof Tulinger    };
239*d7d00a41SKrystof Tulinger
240*d7d00a41SKrystof Tulinger
241*d7d00a41SKrystof Tulinger    $(document).ready($.proxy(anchorScrolls, 'init'));
242*d7d00a41SKrystof Tulinger})(window.document, window.history, window.location);
243*d7d00a41SKrystof Tulinger
244*d7d00a41SKrystof Tulinger(function(window, $) {
245*d7d00a41SKrystof Tulinger    const hash = function () {
246*d7d00a41SKrystof Tulinger        const inner = {
247*d7d00a41SKrystof Tulinger            self: this,
248*d7d00a41SKrystof Tulinger            initialized: false,
249*d7d00a41SKrystof Tulinger            highlighted: [],
250*d7d00a41SKrystof Tulinger            defaults: {
251*d7d00a41SKrystof Tulinger              highlightedClass: 'target',
252*d7d00a41SKrystof Tulinger              linkSelectorTemplate: '{parent} a[name={n}]',
253*d7d00a41SKrystof Tulinger              clickSelector: '{parent} a.l, {parent} a.hl',
254*d7d00a41SKrystof Tulinger              parent: 'div#src',
255*d7d00a41SKrystof Tulinger              autoScroll: true,
256*d7d00a41SKrystof Tulinger              autoScrollDuration: 500
257*d7d00a41SKrystof Tulinger            },
258*d7d00a41SKrystof Tulinger            options: {},
259*d7d00a41SKrystof Tulinger            bindClickHandler: function() {
260*d7d00a41SKrystof Tulinger                $(inner.format(inner.options.clickSelector, {parent: inner.options.parent})).click(function (e) {
261*d7d00a41SKrystof Tulinger                    if(e.shiftKey) {
262*d7d00a41SKrystof Tulinger                        // shift pressed
263*d7d00a41SKrystof Tulinger                        const val = inner.toInt($(this).attr("name"));
264*d7d00a41SKrystof Tulinger                        if (!val) {
265*d7d00a41SKrystof Tulinger                            return false;
266*d7d00a41SKrystof Tulinger                        }
267*d7d00a41SKrystof Tulinger
268*d7d00a41SKrystof Tulinger                        const l = inner.getLinesParts(window.location.hash);
269*d7d00a41SKrystof Tulinger
270*d7d00a41SKrystof Tulinger                        if (l.length == 2) {
271*d7d00a41SKrystof Tulinger                            window.location.hash = "#" + Math.min(l[0], val) + "-" + Math.max(val, l[1]);
272*d7d00a41SKrystof Tulinger                        } else if (l.length == 1) {
273*d7d00a41SKrystof Tulinger                            window.location.hash = "#" + Math.min(l[0], val) + "-" + Math.max(l[0], val);
274*d7d00a41SKrystof Tulinger                        }
275*d7d00a41SKrystof Tulinger                        return false;
276*d7d00a41SKrystof Tulinger                    }
277*d7d00a41SKrystof Tulinger                    return true;
278*d7d00a41SKrystof Tulinger                });
279*d7d00a41SKrystof Tulinger            },
280*d7d00a41SKrystof Tulinger
281*d7d00a41SKrystof Tulinger            getHashParts: function (hash) {
282*d7d00a41SKrystof Tulinger                if (!hash || hash === "") {
283*d7d00a41SKrystof Tulinger                    return hash;
284*d7d00a41SKrystof Tulinger                }
285*d7d00a41SKrystof Tulinger                return (hash = hash.split("#")).length > 1 ? hash[1] : "";
286*d7d00a41SKrystof Tulinger            },
287*d7d00a41SKrystof Tulinger
288*d7d00a41SKrystof Tulinger            getLinesParts: function ( hashPart ) {
289*d7d00a41SKrystof Tulinger              hashPart = inner.getHashParts(hashPart);
290*d7d00a41SKrystof Tulinger              if (!hashPart || hashPart === "") {
291*d7d00a41SKrystof Tulinger                  return hashPart;
292*d7d00a41SKrystof Tulinger              }
293*d7d00a41SKrystof Tulinger              const s = hashPart.split("-");
294*d7d00a41SKrystof Tulinger              if (s.length > 1 && inner.toInt(s[0]) && inner.toInt(s[1])) {
295*d7d00a41SKrystof Tulinger                  return [inner.toInt(s[0]), inner.toInt(s[1])];
296*d7d00a41SKrystof Tulinger              }
297*d7d00a41SKrystof Tulinger              if (s.length > 0 && inner.toInt(s[0])) {
298*d7d00a41SKrystof Tulinger                  return [inner.toInt(s[0])];
299*d7d00a41SKrystof Tulinger              }
300*d7d00a41SKrystof Tulinger              return [];
301*d7d00a41SKrystof Tulinger            },
302*d7d00a41SKrystof Tulinger
303*d7d00a41SKrystof Tulinger            lines: function (urlPart) {
304*d7d00a41SKrystof Tulinger                const p = inner.getLinesParts(urlPart);
305*d7d00a41SKrystof Tulinger                if (p.length == 2) {
306*d7d00a41SKrystof Tulinger                    let l = [];
307*d7d00a41SKrystof Tulinger                    for (let i = Math.min(p[0],p[1]); i <= Math.max(p[0], p[1]); i++) {
308*d7d00a41SKrystof Tulinger                        l.push(i);
309*d7d00a41SKrystof Tulinger                    }
310*d7d00a41SKrystof Tulinger                    return l;
311*d7d00a41SKrystof Tulinger                } else if (p.length == 1){
312*d7d00a41SKrystof Tulinger                    return [p[0]];
313*d7d00a41SKrystof Tulinger                }
314*d7d00a41SKrystof Tulinger                return [];
315*d7d00a41SKrystof Tulinger            },
316*d7d00a41SKrystof Tulinger
317*d7d00a41SKrystof Tulinger            reload: function(e){
318*d7d00a41SKrystof Tulinger                for (let i = 0; i < inner.highlighted.length; i++) {
319*d7d00a41SKrystof Tulinger                    // remove color
320*d7d00a41SKrystof Tulinger                    inner.highlighted[i].removeClass(inner.options.highlightedClass);
321*d7d00a41SKrystof Tulinger                }
322*d7d00a41SKrystof Tulinger                inner.highlighted = [];
323*d7d00a41SKrystof Tulinger
324*d7d00a41SKrystof Tulinger                const lines = inner.lines(window.location.hash);
325*d7d00a41SKrystof Tulinger
326*d7d00a41SKrystof Tulinger                if (lines.length < 1) {
327*d7d00a41SKrystof Tulinger                    // not a case of line highlighting
328*d7d00a41SKrystof Tulinger                    return;
329*d7d00a41SKrystof Tulinger                }
330*d7d00a41SKrystof Tulinger                for (let j = 0; j < lines.length; j++) {
331*d7d00a41SKrystof Tulinger                    // color
332*d7d00a41SKrystof Tulinger                    const slc = inner.format(inner.options.linkSelectorTemplate, { "parent": inner.options.parent,
333*d7d00a41SKrystof Tulinger                                                                                  "n": lines[j] } );
334*d7d00a41SKrystof Tulinger                    const el = $(slc).addClass(inner.options.highlightedClass);
335*d7d00a41SKrystof Tulinger                    inner.highlighted.push(el);
336*d7d00a41SKrystof Tulinger                }
337*d7d00a41SKrystof Tulinger            },
338*d7d00a41SKrystof Tulinger            format: function(format) {
339*d7d00a41SKrystof Tulinger                let args = Array.prototype.slice.call(arguments, 1);
340*d7d00a41SKrystof Tulinger                args = args.length > 0 ? typeof args[0] === "object" ? args[0] : args : args;
341*d7d00a41SKrystof Tulinger                return format.replace(/{([a-zA-Z0-9_-]+)}/g, function(match, number) {
342*d7d00a41SKrystof Tulinger                  return typeof args[number] != 'undefined' ? args[number] : match;
343*d7d00a41SKrystof Tulinger                });
344*d7d00a41SKrystof Tulinger            },
345*d7d00a41SKrystof Tulinger            toInt: function (string) {
346*d7d00a41SKrystof Tulinger                return parseInt(string, 10);
347*d7d00a41SKrystof Tulinger            },
348*d7d00a41SKrystof Tulinger            scroll: function (){
349*d7d00a41SKrystof Tulinger                if (!inner.options.autoScroll) {
350*d7d00a41SKrystof Tulinger                    return;
351*d7d00a41SKrystof Tulinger                }
352*d7d00a41SKrystof Tulinger                const lines = inner.getLinesParts(window.location.hash);
353*d7d00a41SKrystof Tulinger                if (lines.length > 0) {
354*d7d00a41SKrystof Tulinger                    const line = lines[0]; // first line
355*d7d00a41SKrystof Tulinger                    const $line = $(inner.format(inner.options.linkSelectorTemplate, {
356*d7d00a41SKrystof Tulinger                        parent: inner.options.parent,
357*d7d00a41SKrystof Tulinger                        n: line
358*d7d00a41SKrystof Tulinger                    }));
359*d7d00a41SKrystof Tulinger                    if ($line.length > 0) {
360*d7d00a41SKrystof Tulinger                        // if there is such element identified with the line number
361*d7d00a41SKrystof Tulinger                        // we can scroll to it
362*d7d00a41SKrystof Tulinger                        $('html, body').animate({
363*d7d00a41SKrystof Tulinger                            scrollTop: $(inner.format(inner.options.linkSelectorTemplate, {
364*d7d00a41SKrystof Tulinger                                parent: inner.options.parent,
365*d7d00a41SKrystof Tulinger                                n: line
366*d7d00a41SKrystof Tulinger                            })).offset().top - $(inner.options.parent).offset().top
367*d7d00a41SKrystof Tulinger                        }, inner.options.autoScrollDuration);
368*d7d00a41SKrystof Tulinger                    }
369*d7d00a41SKrystof Tulinger                }
370*d7d00a41SKrystof Tulinger            }
371*d7d00a41SKrystof Tulinger        }; // inner
372*d7d00a41SKrystof Tulinger
373*d7d00a41SKrystof Tulinger        this.init = function (options) {
374*d7d00a41SKrystof Tulinger            if (inner.initialized) {
375*d7d00a41SKrystof Tulinger                return this;
376*d7d00a41SKrystof Tulinger            }
377*d7d00a41SKrystof Tulinger            inner.options = $.extend(inner.defaults, options, {});
378*d7d00a41SKrystof Tulinger
379*d7d00a41SKrystof Tulinger            $(window).on("hashchange", inner.reload);
380*d7d00a41SKrystof Tulinger            inner.reload();
381*d7d00a41SKrystof Tulinger            inner.bindClickHandler();
382*d7d00a41SKrystof Tulinger            inner.scroll();
383*d7d00a41SKrystof Tulinger            inner.initialized = true;
384*d7d00a41SKrystof Tulinger            return this;
385*d7d00a41SKrystof Tulinger        };
386*d7d00a41SKrystof Tulinger    };
387*d7d00a41SKrystof Tulinger    $.hash = new ($.extend(hash, $.hash ? $.hash : {}))();
388*d7d00a41SKrystof Tulinger}) (window, window.jQuery);
389*d7d00a41SKrystof Tulinger
390*d7d00a41SKrystof Tulinger/**
391*d7d00a41SKrystof Tulinger * General on-demand script downloader
392*d7d00a41SKrystof Tulinger */
393*d7d00a41SKrystof Tulinger(function (window, document, $) {
394*d7d00a41SKrystof Tulinger    const script = function () {
395*d7d00a41SKrystof Tulinger        this.scriptsDownloaded = {};
396*d7d00a41SKrystof Tulinger        this.defaults = {
397*d7d00a41SKrystof Tulinger            contextPath: window.contextPath
398*d7d00a41SKrystof Tulinger        };
399*d7d00a41SKrystof Tulinger
400*d7d00a41SKrystof Tulinger        this.options = $.extend(this.defaults, {});
401*d7d00a41SKrystof Tulinger
402*d7d00a41SKrystof Tulinger        this.loadScript = function (url) {
403*d7d00a41SKrystof Tulinger            if (!/^[a-z]{3,5}:\/\//.test(url)) { // dummy test for remote prefix
404*d7d00a41SKrystof Tulinger                url = this.options.contextPath + '/' + url;
405*d7d00a41SKrystof Tulinger            }
406*d7d00a41SKrystof Tulinger            if (url in this.scriptsDownloaded) {
407*d7d00a41SKrystof Tulinger                return this.scriptsDownloaded[url];
408*d7d00a41SKrystof Tulinger            }
409*d7d00a41SKrystof Tulinger            this.scriptsDownloaded[url] = $.ajax({
410*d7d00a41SKrystof Tulinger                url: url,
411*d7d00a41SKrystof Tulinger                dataType: 'script',
412*d7d00a41SKrystof Tulinger                cache: true,
413*d7d00a41SKrystof Tulinger                timeout: 10000
414*d7d00a41SKrystof Tulinger            }).fail(function () {
415*d7d00a41SKrystof Tulinger                console.debug('Failed to download "' + url + '" module');
416*d7d00a41SKrystof Tulinger            });
417*d7d00a41SKrystof Tulinger            return this.scriptsDownloaded[url];
418*d7d00a41SKrystof Tulinger        };
419*d7d00a41SKrystof Tulinger    };
420*d7d00a41SKrystof Tulinger    $.script = new ($.extend(script, $.script ? $.script : {}))();
421*d7d00a41SKrystof Tulinger})(window, document, jQuery);
422*d7d00a41SKrystof Tulinger
423*d7d00a41SKrystof Tulinger/**
424*d7d00a41SKrystof Tulinger * General window plugin
425*d7d00a41SKrystof Tulinger *
426*d7d00a41SKrystof Tulinger * This plugin allows you to create a new window inside the browser. The main
427*d7d00a41SKrystof Tulinger * interface is create function.
428*d7d00a41SKrystof Tulinger *
429*d7d00a41SKrystof Tulinger * Usage:
430*d7d00a41SKrystof Tulinger * $myWindow = $.window.create({
431*d7d00a41SKrystof Tulinger *  // default options (later available via this.options)
432*d7d00a41SKrystof Tulinger *  project: 'abcd', // not existing in window options and will be filled
433*d7d00a41SKrystof Tulinger *  draggable: false, // override the window defaults
434*d7d00a41SKrystof Tulinger *  // callbacks for events
435*d7d00a41SKrystof Tulinger *  init: function ($window) {
436*d7d00a41SKrystof Tulinger *      // called when creating the new window
437*d7d00a41SKrystof Tulinger *      // you can modify the new window object - it's jquery object
438*d7d00a41SKrystof Tulinger *      // you must return the modified window object
439*d7d00a41SKrystof Tulinger *  },
440*d7d00a41SKrystof Tulinger *  load: function ($window) {
441*d7d00a41SKrystof Tulinger *      // called when the page is successfully loaded
442*d7d00a41SKrystof Tulinger *      // you can attach some handlers and fill some options with DOM values
443*d7d00a41SKrystof Tulinger *  },
444*d7d00a41SKrystof Tulinger *  update: function(data) {
445*d7d00a41SKrystof Tulinger *      // called when update is called on your window bypassing the data param
446*d7d00a41SKrystof Tulinger *      // you can modify the window content or other DOM content
447*d7d00a41SKrystof Tulinger *  }
448*d7d00a41SKrystof Tulinger * }, {
449*d7d00a41SKrystof Tulinger *      // context object - can contain other helper variables and functions
450*d7d00a41SKrystof Tulinger *      // it's available in the callbacks as the 'this' variable
451*d7d00a41SKrystof Tulinger *      // the window itself is available as this.$window
452*d7d00a41SKrystof Tulinger *      modified: false,
453*d7d00a41SKrystof Tulinger *      modify: function () {
454*d7d00a41SKrystof Tulinger *          this.modified = true;
455*d7d00a41SKrystof Tulinger *      },
456*d7d00a41SKrystof Tulinger * })
457*d7d00a41SKrystof Tulinger *
458*d7d00a41SKrystof Tulinger * The new $myWindow object is jQuery object - you can call jQuery functions on it.
459*d7d00a41SKrystof Tulinger * It doesn't really make sense to call all of the jQuery functions however
460*d7d00a41SKrystof Tulinger * some of them might be useful: toggle, hide, show and so on.
461*d7d00a41SKrystof Tulinger *
462*d7d00a41SKrystof Tulinger * The window object also provides some useful functions like
463*d7d00a41SKrystof Tulinger * $myWindow.error(message) to display an error in this.$errors element or
464*d7d00a41SKrystof Tulinger * $myWindow.update(data) to trigger your update callback with given data or
465*d7d00a41SKrystof Tulinger * $myWindow.move(position) to move the window to the given position
466*d7d00a41SKrystof Tulinger *    if no position is given it may be determined from the mouse position
467*d7d00a41SKrystof Tulinger *
468*d7d00a41SKrystof Tulinger * For custom content in the window you can use body() function:
469*d7d00a41SKrystof Tulinger * $myWindow.body().append($('<div>').addClass('important-div'))
470*d7d00a41SKrystof Tulinger *
471*d7d00a41SKrystof Tulinger * @author Kryštof Tulinger
472*d7d00a41SKrystof Tulinger */
473*d7d00a41SKrystof Tulinger(function (browserWindow, document, $, $script) {
474*d7d00a41SKrystof Tulinger    const window = function () {
475*d7d00a41SKrystof Tulinger        const Inner = function (options, context) {
476*d7d00a41SKrystof Tulinger            const self = this;
477*d7d00a41SKrystof Tulinger            // private
478*d7d00a41SKrystof Tulinger            this.context = context;
479*d7d00a41SKrystof Tulinger            this.callbacks = {
480*d7d00a41SKrystof Tulinger                init: [],
481*d7d00a41SKrystof Tulinger                load: [],
482*d7d00a41SKrystof Tulinger                update: []
483*d7d00a41SKrystof Tulinger            };
484*d7d00a41SKrystof Tulinger            this.$window = undefined;
485*d7d00a41SKrystof Tulinger            this.$errors = undefined;
486*d7d00a41SKrystof Tulinger            this.clientX = 0;
487*d7d00a41SKrystof Tulinger            this.clientY = 0;
488*d7d00a41SKrystof Tulinger            this.pendingUpdates = [];
489*d7d00a41SKrystof Tulinger
490*d7d00a41SKrystof Tulinger            /**
491*d7d00a41SKrystof Tulinger             * Default values for the window options.
492*d7d00a41SKrystof Tulinger             */
493*d7d00a41SKrystof Tulinger            this.defaults = {
494*d7d00a41SKrystof Tulinger                title: 'Window',
495*d7d00a41SKrystof Tulinger                appendDraggable: '#content',
496*d7d00a41SKrystof Tulinger                draggable: true,
497*d7d00a41SKrystof Tulinger                draggableScript: 'js/jquery-ui-1.12.1-draggable.min.js', // relative to context
498*d7d00a41SKrystof Tulinger                contextPath: browserWindow.contextPath,
499*d7d00a41SKrystof Tulinger                parent: undefined,
500*d7d00a41SKrystof Tulinger                load: undefined,
501*d7d00a41SKrystof Tulinger                init: undefined,
502*d7d00a41SKrystof Tulinger                handlers: undefined
503*d7d00a41SKrystof Tulinger            };
504*d7d00a41SKrystof Tulinger
505*d7d00a41SKrystof Tulinger            this.options = $.extend({}, this.defaults, options);
506*d7d00a41SKrystof Tulinger
507*d7d00a41SKrystof Tulinger            this.addCallback = function (name, callback, context) {
508*d7d00a41SKrystof Tulinger                context = context || this.getSelfContext;
509*d7d00a41SKrystof Tulinger                if (!this.callbacks || !$.isArray(this.callbacks[name])) {
510*d7d00a41SKrystof Tulinger                    this.callbacks[name] = [];
511*d7d00a41SKrystof Tulinger                }
512*d7d00a41SKrystof Tulinger                this.callbacks[name].push({
513*d7d00a41SKrystof Tulinger                    'callback': callback,
514*d7d00a41SKrystof Tulinger                    'context': context
515*d7d00a41SKrystof Tulinger                });
516*d7d00a41SKrystof Tulinger            };
517*d7d00a41SKrystof Tulinger
518*d7d00a41SKrystof Tulinger            this.fire = function (name, args) {
519*d7d00a41SKrystof Tulinger                if (!this.callbacks || !$.isArray(this.callbacks[name])) {
520*d7d00a41SKrystof Tulinger                    return;
521*d7d00a41SKrystof Tulinger                }
522*d7d00a41SKrystof Tulinger
523*d7d00a41SKrystof Tulinger                for (let i = 0; i < this.callbacks[name].length; i++) {
524*d7d00a41SKrystof Tulinger                    this.$window = (this.callbacks[name][i].callback.apply(
525*d7d00a41SKrystof Tulinger                            this.callbacks[name][i].context.call(this),
526*d7d00a41SKrystof Tulinger                            args || [this.$window])) || this.$window;
527*d7d00a41SKrystof Tulinger                }
528*d7d00a41SKrystof Tulinger            };
529*d7d00a41SKrystof Tulinger
530*d7d00a41SKrystof Tulinger            this.getContext = function () {
531*d7d00a41SKrystof Tulinger                return $.extend(this.context, {options: this.options});
532*d7d00a41SKrystof Tulinger            };
533*d7d00a41SKrystof Tulinger
534*d7d00a41SKrystof Tulinger            this.getSelfContext = function () {
535*d7d00a41SKrystof Tulinger                return this;
536*d7d00a41SKrystof Tulinger            };
537*d7d00a41SKrystof Tulinger
538*d7d00a41SKrystof Tulinger            // private
539*d7d00a41SKrystof Tulinger            this.cropPosition = function($w, position) {
540*d7d00a41SKrystof Tulinger                const w = {
541*d7d00a41SKrystof Tulinger                    height: $w.outerHeight(true),
542*d7d00a41SKrystof Tulinger                    width: $w.outerWidth(true)
543*d7d00a41SKrystof Tulinger                };
544*d7d00a41SKrystof Tulinger                const bw = {
545*d7d00a41SKrystof Tulinger                    height: $(browserWindow).outerHeight(true),
546*d7d00a41SKrystof Tulinger                    width: $(browserWindow).outerWidth(true),
547*d7d00a41SKrystof Tulinger                    yOffset: 0,
548*d7d00a41SKrystof Tulinger                    xOffset: 0
549*d7d00a41SKrystof Tulinger                };
550*d7d00a41SKrystof Tulinger                position.top -= Math.max(0, position.top + w.height - bw.yOffset - bw.height + 20);
551*d7d00a41SKrystof Tulinger                position.left -= Math.max(0, position.left + w.width - bw.xOffset - bw.width + 20);
552*d7d00a41SKrystof Tulinger                return position;
553*d7d00a41SKrystof Tulinger            };
554*d7d00a41SKrystof Tulinger
555*d7d00a41SKrystof Tulinger            this.determinePosition = function () {
556*d7d00a41SKrystof Tulinger                const position = {
557*d7d00a41SKrystof Tulinger                    top: this.clientY,
558*d7d00a41SKrystof Tulinger                    left: this.clientX
559*d7d00a41SKrystof Tulinger                };
560*d7d00a41SKrystof Tulinger                return this.cropPosition(this.$window, position);
561*d7d00a41SKrystof Tulinger            };
562*d7d00a41SKrystof Tulinger
563*d7d00a41SKrystof Tulinger            this.makeMeDraggable = function () {
564*d7d00a41SKrystof Tulinger                if (!$script || typeof $script.loadScript !== 'function') {
565*d7d00a41SKrystof Tulinger                    console.log("The window plugin requires $.script plugin when draggable option is 'true'");
566*d7d00a41SKrystof Tulinger                    return;
567*d7d00a41SKrystof Tulinger                }
568*d7d00a41SKrystof Tulinger
569*d7d00a41SKrystof Tulinger                $script.loadScript(this.options.draggableScript).done(function () {
570*d7d00a41SKrystof Tulinger                    self.$window.draggable({
571*d7d00a41SKrystof Tulinger                        appendTo: self.options.draggableAppendTo || $('body'),
572*d7d00a41SKrystof Tulinger                        helper: 'clone',
573*d7d00a41SKrystof Tulinger                        start: function () {
574*d7d00a41SKrystof Tulinger                            $(this).hide();
575*d7d00a41SKrystof Tulinger                        },
576*d7d00a41SKrystof Tulinger                        stop: function (e, ui) {
577*d7d00a41SKrystof Tulinger                            $(this).show().offset(ui.offset).css('position', 'fixed');
578*d7d00a41SKrystof Tulinger                        },
579*d7d00a41SKrystof Tulinger                        create: function (e, ui) {
580*d7d00a41SKrystof Tulinger                            $(this).css('position', 'fixed');
581*d7d00a41SKrystof Tulinger                        }
582*d7d00a41SKrystof Tulinger                    });
583*d7d00a41SKrystof Tulinger                });
584*d7d00a41SKrystof Tulinger            };
585*d7d00a41SKrystof Tulinger
586*d7d00a41SKrystof Tulinger            this.addCallback('init', function ($window) {
587*d7d00a41SKrystof Tulinger                let $close;
588*d7d00a41SKrystof Tulinger                const $top = $("<div>").addClass('clearfix').
589*d7d00a41SKrystof Tulinger                        append($("<div>").addClass("pull-left").append($("<b>").text(this.options.title || "Window"))).
590*d7d00a41SKrystof Tulinger                        append($("<div>").addClass('pull-right').append($close = $('<a href="#" class="minimize">x</a>')));
591*d7d00a41SKrystof Tulinger
592*d7d00a41SKrystof Tulinger                const $header = $("<div>").addClass('window-header').append($top);
593*d7d00a41SKrystof Tulinger
594*d7d00a41SKrystof Tulinger                const $body = $("<div>").addClass("window-body").append(self.$errors = $('<div>').css('text-align', 'center'));
595*d7d00a41SKrystof Tulinger
596*d7d00a41SKrystof Tulinger                $window = $("<div>").
597*d7d00a41SKrystof Tulinger                        addClass('window').
598*d7d00a41SKrystof Tulinger                        addClass('diff_navigation_style').
599*d7d00a41SKrystof Tulinger                        css('z-index', 15000).
600*d7d00a41SKrystof Tulinger                        hide().
601*d7d00a41SKrystof Tulinger                        append($header).
602*d7d00a41SKrystof Tulinger                        append($body);
603*d7d00a41SKrystof Tulinger
604*d7d00a41SKrystof Tulinger                $close.click(function () {
605*d7d00a41SKrystof Tulinger                    $window.hide();
606*d7d00a41SKrystof Tulinger                    return false;
607*d7d00a41SKrystof Tulinger                });
608*d7d00a41SKrystof Tulinger
609*d7d00a41SKrystof Tulinger                /**
610*d7d00a41SKrystof Tulinger                 * Get the element for the window body.
611*d7d00a41SKrystof Tulinger                 * This should be used to place your desired content.
612*d7d00a41SKrystof Tulinger                 *
613*d7d00a41SKrystof Tulinger                 * The returned object has a method {@code window()}
614*d7d00a41SKrystof Tulinger                 * which returns the whole window.
615*d7d00a41SKrystof Tulinger                 *
616*d7d00a41SKrystof Tulinger                 * {@code $window} is equivalent to {@code $window.body().window()}
617*d7d00a41SKrystof Tulinger                 *
618*d7d00a41SKrystof Tulinger                 * @returns jQuery object of the window body
619*d7d00a41SKrystof Tulinger                 */
620*d7d00a41SKrystof Tulinger                $window.body = function () {
621*d7d00a41SKrystof Tulinger                    $body.window = function () {
622*d7d00a41SKrystof Tulinger                        return $window;
623*d7d00a41SKrystof Tulinger                    };
624*d7d00a41SKrystof Tulinger                    return $body;
625*d7d00a41SKrystof Tulinger                };
626*d7d00a41SKrystof Tulinger
627*d7d00a41SKrystof Tulinger                /**
628*d7d00a41SKrystof Tulinger                 * Display custom error message in the window
629*d7d00a41SKrystof Tulinger                 * @param {string} msg message
630*d7d00a41SKrystof Tulinger                 * @returns self
631*d7d00a41SKrystof Tulinger                 */
632*d7d00a41SKrystof Tulinger                $window.error = function (msg) {
633*d7d00a41SKrystof Tulinger                    const $span = $("<p class='error'>" + msg + "</p>").
634*d7d00a41SKrystof Tulinger                            animate({opacity: "0.2"}, 3000);
635*d7d00a41SKrystof Tulinger                    $span.hide('slow', function () {
636*d7d00a41SKrystof Tulinger                        $span.remove();
637*d7d00a41SKrystof Tulinger                    });
638*d7d00a41SKrystof Tulinger                    self.$errors.html($span);
639*d7d00a41SKrystof Tulinger                    return this;
640*d7d00a41SKrystof Tulinger                };
641*d7d00a41SKrystof Tulinger
642*d7d00a41SKrystof Tulinger                /**
643*d7d00a41SKrystof Tulinger                 * Move the window to the position. If no position is given
644*d7d00a41SKrystof Tulinger                 * it may be determined from the mouse position.
645*d7d00a41SKrystof Tulinger                 *
646*d7d00a41SKrystof Tulinger                 * @param {object} position object with top and left attributes
647*d7d00a41SKrystof Tulinger                 * @returns self
648*d7d00a41SKrystof Tulinger                 */
649*d7d00a41SKrystof Tulinger                $window.move = function (position) {
650*d7d00a41SKrystof Tulinger                    position = position || self.determinePosition();
651*d7d00a41SKrystof Tulinger                    return this.css(position);
652*d7d00a41SKrystof Tulinger                };
653*d7d00a41SKrystof Tulinger
654*d7d00a41SKrystof Tulinger                /**
655*d7d00a41SKrystof Tulinger                 * Display or hide the window.
656*d7d00a41SKrystof Tulinger                 *
657*d7d00a41SKrystof Tulinger                 * We override this method from jquery to manually
658*d7d00a41SKrystof Tulinger                 * trigger the hide() and show() methods
659*d7d00a41SKrystof Tulinger                 * which may be used in the descendants.
660*d7d00a41SKrystof Tulinger                 *
661*d7d00a41SKrystof Tulinger                 * @returns self
662*d7d00a41SKrystof Tulinger                 */
663*d7d00a41SKrystof Tulinger                $window.toggle = function() {
664*d7d00a41SKrystof Tulinger                    const action = this.is(':visible') ? this.hide : this.show;
665*d7d00a41SKrystof Tulinger                    return action.apply(this, arguments);
666*d7d00a41SKrystof Tulinger                };
667*d7d00a41SKrystof Tulinger
668*d7d00a41SKrystof Tulinger                /**
669*d7d00a41SKrystof Tulinger                 * Toggle and move the window to the current mouse position
670*d7d00a41SKrystof Tulinger                 *
671*d7d00a41SKrystof Tulinger                 * @returns self
672*d7d00a41SKrystof Tulinger                 */
673*d7d00a41SKrystof Tulinger                $window.toggleAndMove = function () {
674*d7d00a41SKrystof Tulinger                    return $window.toggle().move();
675*d7d00a41SKrystof Tulinger                };
676*d7d00a41SKrystof Tulinger
677*d7d00a41SKrystof Tulinger                /**
678*d7d00a41SKrystof Tulinger                 * Update the window with given data.
679*d7d00a41SKrystof Tulinger                 *
680*d7d00a41SKrystof Tulinger                 * @param {mixed} data
681*d7d00a41SKrystof Tulinger                 * @returns {undefined}
682*d7d00a41SKrystof Tulinger                 */
683*d7d00a41SKrystof Tulinger                $window.update = function (data) {
684*d7d00a41SKrystof Tulinger                    if (this.loaded) {
685*d7d00a41SKrystof Tulinger                        self.fire('update', [data]);
686*d7d00a41SKrystof Tulinger                    } else {
687*d7d00a41SKrystof Tulinger                        self.pendingUpdates.push({data: data});
688*d7d00a41SKrystof Tulinger                    }
689*d7d00a41SKrystof Tulinger                    return this;
690*d7d00a41SKrystof Tulinger                };
691*d7d00a41SKrystof Tulinger
692*d7d00a41SKrystof Tulinger                // insert window into context
693*d7d00a41SKrystof Tulinger                this.context = $.extend(this.context, {$window: $window});
694*d7d00a41SKrystof Tulinger
695*d7d00a41SKrystof Tulinger                // set us as initialized
696*d7d00a41SKrystof Tulinger                $window.initialized = true;
697*d7d00a41SKrystof Tulinger                // set us as not loaded
698*d7d00a41SKrystof Tulinger                $window.loaded = false;
699*d7d00a41SKrystof Tulinger
700*d7d00a41SKrystof Tulinger                return $window;
701*d7d00a41SKrystof Tulinger            });
702*d7d00a41SKrystof Tulinger
703*d7d00a41SKrystof Tulinger            this.addCallback('load', function ($window) {
704*d7d00a41SKrystof Tulinger                const that = this;
705*d7d00a41SKrystof Tulinger                $(document).mousemove(function (e) {
706*d7d00a41SKrystof Tulinger                    that.clientX = e.clientX;
707*d7d00a41SKrystof Tulinger                    that.clientY = e.clientY;
708*d7d00a41SKrystof Tulinger                });
709*d7d00a41SKrystof Tulinger                $(document).keyup(function (e) {
710*d7d00a41SKrystof Tulinger                    var key = e.keyCode;
711*d7d00a41SKrystof Tulinger                    if (key === 27) { // esc
712*d7d00a41SKrystof Tulinger                        that.$window.hide();
713*d7d00a41SKrystof Tulinger                    }
714*d7d00a41SKrystof Tulinger                    return true;
715*d7d00a41SKrystof Tulinger                });
716*d7d00a41SKrystof Tulinger            });
717*d7d00a41SKrystof Tulinger
718*d7d00a41SKrystof Tulinger            if (this.options.draggable) {
719*d7d00a41SKrystof Tulinger                this.addCallback('load', this.makeMeDraggable);
720*d7d00a41SKrystof Tulinger            }
721*d7d00a41SKrystof Tulinger
722*d7d00a41SKrystof Tulinger            this.addCallback('load', function ($window) {
723*d7d00a41SKrystof Tulinger                this.$window.appendTo(this.options.parent ? $(this.options.parent) : $("body"));
724*d7d00a41SKrystof Tulinger            });
725*d7d00a41SKrystof Tulinger
726*d7d00a41SKrystof Tulinger            if (this.options.init && typeof this.options.init === 'function') {
727*d7d00a41SKrystof Tulinger                this.addCallback('init', this.options.init, this.getContext);
728*d7d00a41SKrystof Tulinger            }
729*d7d00a41SKrystof Tulinger
730*d7d00a41SKrystof Tulinger            if (self.options.load && typeof self.options.load === 'function') {
731*d7d00a41SKrystof Tulinger                this.addCallback('load', this.options.load, this.getContext);
732*d7d00a41SKrystof Tulinger            }
733*d7d00a41SKrystof Tulinger
734*d7d00a41SKrystof Tulinger            if (self.options.update && typeof self.options.update === 'function') {
735*d7d00a41SKrystof Tulinger                this.addCallback('update', this.options.update, this.getContext);
736*d7d00a41SKrystof Tulinger            }
737*d7d00a41SKrystof Tulinger
738*d7d00a41SKrystof Tulinger            $(function () {
739*d7d00a41SKrystof Tulinger                self.fire('load');
740*d7d00a41SKrystof Tulinger                self.$window.loaded = true;
741*d7d00a41SKrystof Tulinger
742*d7d00a41SKrystof Tulinger                for (var i = 0; i < self.pendingUpdates.length; i++) {
743*d7d00a41SKrystof Tulinger                    self.fire('update', [self.pendingUpdates[i].data]);
744*d7d00a41SKrystof Tulinger                }
745*d7d00a41SKrystof Tulinger                self.pendingUpdates = [];
746*d7d00a41SKrystof Tulinger            });
747*d7d00a41SKrystof Tulinger
748*d7d00a41SKrystof Tulinger            this.fire('init');
749*d7d00a41SKrystof Tulinger
750*d7d00a41SKrystof Tulinger            return this.$window;
751*d7d00a41SKrystof Tulinger        };
752*d7d00a41SKrystof Tulinger
753*d7d00a41SKrystof Tulinger        /**
754*d7d00a41SKrystof Tulinger         * Create a window.
755*d7d00a41SKrystof Tulinger         *
756*d7d00a41SKrystof Tulinger         * @param {hash} options containing default options and callbacks
757*d7d00a41SKrystof Tulinger         * @param {hash} context other helper variables and functions
758*d7d00a41SKrystof Tulinger         * @returns new window object
759*d7d00a41SKrystof Tulinger         */
760*d7d00a41SKrystof Tulinger        this.create = function (options, context) {
761*d7d00a41SKrystof Tulinger            return new Inner(options, context);
762*d7d00a41SKrystof Tulinger        };
763*d7d00a41SKrystof Tulinger    };
764*d7d00a41SKrystof Tulinger    $.window = new ($.extend(window, $.window ? $.window : {}))();
765*d7d00a41SKrystof Tulinger})(window, document, jQuery, jQuery.script);
766*d7d00a41SKrystof Tulinger
767*d7d00a41SKrystof Tulinger/**
768*d7d00a41SKrystof Tulinger * Intelligence window plugin.
769*d7d00a41SKrystof Tulinger *
770*d7d00a41SKrystof Tulinger * Reworked to use Jquery in 2016
771*d7d00a41SKrystof Tulinger */
772*d7d00a41SKrystof Tulinger(function (browserWindow, document, $, $window) {
773*d7d00a41SKrystof Tulinger    if (!$window || typeof $window.create !== 'function') {
774*d7d00a41SKrystof Tulinger        console.log("The intelligenceWindow plugin requires $.window plugin");
775*d7d00a41SKrystof Tulinger        return;
776*d7d00a41SKrystof Tulinger    }
777*d7d00a41SKrystof Tulinger
778*d7d00a41SKrystof Tulinger    var intelliWindow = function () {
779*d7d00a41SKrystof Tulinger        this.initialised = false;
780*d7d00a41SKrystof Tulinger        this.init = function (options, context) {
781*d7d00a41SKrystof Tulinger            $.intelliWindow = $window.create($.extend({
782*d7d00a41SKrystof Tulinger                title: 'Intelligence window',
783*d7d00a41SKrystof Tulinger                selector: 'a.intelliWindow-symbol',
784*d7d00a41SKrystof Tulinger                google_url: 'https://www.google.com/search?q=',
785*d7d00a41SKrystof Tulinger                project: undefined,
786*d7d00a41SKrystof Tulinger                init: function ($window) {
787*d7d00a41SKrystof Tulinger                    let $highlight, $unhighlight, $unhighlightAll, $prev, $next;
788*d7d00a41SKrystof Tulinger
789*d7d00a41SKrystof Tulinger                    let $firstList = $("<ul>").
790*d7d00a41SKrystof Tulinger                            append($("<li>").append(
791*d7d00a41SKrystof Tulinger                                    $highlight = $('<a href="#" title="Highlight">' +
792*d7d00a41SKrystof Tulinger                                            '<span>Highlight</span> <b class="symbol-name"></b></a>'))).
793*d7d00a41SKrystof Tulinger                            append($("<li>").append(
794*d7d00a41SKrystof Tulinger                                    $unhighlight = $('<a href="#" title="Unhighlight">' +
795*d7d00a41SKrystof Tulinger                                            '<span>Unhighlight</span> <b class="symbol-name"></b></a>'))).
796*d7d00a41SKrystof Tulinger                            append($("<li>").append(
797*d7d00a41SKrystof Tulinger                                    $unhighlightAll = $('<a href="#" title="Unhighlight all">' +
798*d7d00a41SKrystof Tulinger                                            '<span>Unhighlight all</span></a>')));
799*d7d00a41SKrystof Tulinger
800*d7d00a41SKrystof Tulinger                    this.bindOnClick($highlight, this.highlight);
801*d7d00a41SKrystof Tulinger                    this.bindOnClick($unhighlight, this.unhighlight);
802*d7d00a41SKrystof Tulinger                    this.bindOnClick($unhighlightAll, this.unhighlightAll);
803*d7d00a41SKrystof Tulinger
804*d7d00a41SKrystof Tulinger                    let $secondList = $("<ul>").
805*d7d00a41SKrystof Tulinger                            append($("<li>").append(
806*d7d00a41SKrystof Tulinger                                    $('<a class="search-defs" href="#" target="_blank">' +
807*d7d00a41SKrystof Tulinger                                            '<span>Search for definitions of</span> <b class="symbol-name"></b></a>'))).
808*d7d00a41SKrystof Tulinger                            append($("<li>").append(
809*d7d00a41SKrystof Tulinger                                    $('<a class="search-refs" href="#" target="_blank">' +
810*d7d00a41SKrystof Tulinger                                            '<span>Search for references of</span> <b class="symbol-name"></b></a>'))).
811*d7d00a41SKrystof Tulinger                            append($("<li>").append(
812*d7d00a41SKrystof Tulinger                                    $('<a class="search-full" href="#" target="_blank">' +
813*d7d00a41SKrystof Tulinger                                            '<span>Do a full search with</span> <b class="symbol-name"></b></a>'))).
814*d7d00a41SKrystof Tulinger                            append($("<li>").append(
815*d7d00a41SKrystof Tulinger                                    $('<a class="search-files" href="#" target="_blank">' +
816*d7d00a41SKrystof Tulinger                                            '<span>Search for file names that contain</span> <b class="symbol-name"></b></a>')));
817*d7d00a41SKrystof Tulinger
818*d7d00a41SKrystof Tulinger                    let $thirdList = $("<ul>").
819*d7d00a41SKrystof Tulinger                            append($("<li>").append(
820*d7d00a41SKrystof Tulinger                                    $('<a class="search-google" href="#" target="_blank">' +
821*d7d00a41SKrystof Tulinger                                            '<span>Google</span> <b class="symbol-name"></b></a>')));
822*d7d00a41SKrystof Tulinger
823*d7d00a41SKrystof Tulinger                    let $controls = $('<div class="pull-right">').
824*d7d00a41SKrystof Tulinger                            append($next = $('<a href="#" title="next" class="pull-right">Next >></a>')).
825*d7d00a41SKrystof Tulinger                            append('<span class="pull-right"> | </span>').
826*d7d00a41SKrystof Tulinger                            append($prev = $('<a href="#" title="prev" class="pull-right"><< Prev </a>')).
827*d7d00a41SKrystof Tulinger                            append($('<div class="clearfix">')).
828*d7d00a41SKrystof Tulinger                            append(this.$errors = $('<span class="clearfix">'));
829*d7d00a41SKrystof Tulinger
830*d7d00a41SKrystof Tulinger                    this.bindOnClick($next, this.scrollToNextElement, 1);
831*d7d00a41SKrystof Tulinger                    this.bindOnClick($prev, this.scrollToNextElement, -1);
832*d7d00a41SKrystof Tulinger
833*d7d00a41SKrystof Tulinger                    return $window.
834*d7d00a41SKrystof Tulinger                            attr('id', 'intelli_win').
835*d7d00a41SKrystof Tulinger                            addClass('intelli-window').
836*d7d00a41SKrystof Tulinger                            body().
837*d7d00a41SKrystof Tulinger                            append($controls).
838*d7d00a41SKrystof Tulinger                            append($("<h2>").addClass('symbol-name')).
839*d7d00a41SKrystof Tulinger                            append($("<span>").addClass('symbol-description')).
840*d7d00a41SKrystof Tulinger                            append($("<hr>")).
841*d7d00a41SKrystof Tulinger                            append($("<h5>").text("In current file")).
842*d7d00a41SKrystof Tulinger                            append($firstList).
843*d7d00a41SKrystof Tulinger                            append($("<h5>").text('In project "' + this.project + '"')).
844*d7d00a41SKrystof Tulinger                            append($secondList).
845*d7d00a41SKrystof Tulinger                            append($("<h5>").text("On Google")).
846*d7d00a41SKrystof Tulinger                            append($thirdList).
847*d7d00a41SKrystof Tulinger                            window();
848*d7d00a41SKrystof Tulinger                },
849*d7d00a41SKrystof Tulinger                load: function ($window) {
850*d7d00a41SKrystof Tulinger                    const that = this;
851*d7d00a41SKrystof Tulinger                    $(document).keypress(function (e) {
852*d7d00a41SKrystof Tulinger                        if (textInputHasFocus()) {
853*d7d00a41SKrystof Tulinger                            return true;
854*d7d00a41SKrystof Tulinger                        }
855*d7d00a41SKrystof Tulinger                        const key = e.which;
856*d7d00a41SKrystof Tulinger                        switch (key) {
857*d7d00a41SKrystof Tulinger                            case 49: // 1
858*d7d00a41SKrystof Tulinger                                if (that.symbol) {
859*d7d00a41SKrystof Tulinger                                    that.$window.toggleAndMove();
860*d7d00a41SKrystof Tulinger                                }
861*d7d00a41SKrystof Tulinger                                break;
862*d7d00a41SKrystof Tulinger                            case 50: // 2
863*d7d00a41SKrystof Tulinger                                if (that.symbol && that.unhighlight(that.symbol).length === 0) {
864*d7d00a41SKrystof Tulinger                                     that.highlight(that.symbol, 1);
865*d7d00a41SKrystof Tulinger                                }
866*d7d00a41SKrystof Tulinger                                break;
867*d7d00a41SKrystof Tulinger                            case 51: // 3
868*d7d00a41SKrystof Tulinger                                if (that.symbol && that.unhighlight(that.symbol).length === 0) {
869*d7d00a41SKrystof Tulinger                                     that.highlight(that.symbol, 2);
870*d7d00a41SKrystof Tulinger                                }
871*d7d00a41SKrystof Tulinger                                break;
872*d7d00a41SKrystof Tulinger                            case 52: // 4
873*d7d00a41SKrystof Tulinger                                if (that.symbol && that.unhighlight(that.symbol).length === 0) {
874*d7d00a41SKrystof Tulinger                                     that.highlight(that.symbol, 3);
875*d7d00a41SKrystof Tulinger                                }
876*d7d00a41SKrystof Tulinger                                break;
877*d7d00a41SKrystof Tulinger                            case 53: // 5
878*d7d00a41SKrystof Tulinger                                if (that.symbol && that.unhighlight(that.symbol).length === 0) {
879*d7d00a41SKrystof Tulinger                                    that.highlight(that.symbol, 4);
880*d7d00a41SKrystof Tulinger                                }
881*d7d00a41SKrystof Tulinger                                break;
882*d7d00a41SKrystof Tulinger                            case 54: // 6
883*d7d00a41SKrystof Tulinger                                if (that.symbol && that.unhighlight(that.symbol).length === 0) {
884*d7d00a41SKrystof Tulinger                                    that.highlight(that.symbol, 5);
885*d7d00a41SKrystof Tulinger                                }
886*d7d00a41SKrystof Tulinger                                break;
887*d7d00a41SKrystof Tulinger                            case 55: // 5
888*d7d00a41SKrystof Tulinger                                if (that.symbol && that.unhighlight(that.symbol).length === 0) {
889*d7d00a41SKrystof Tulinger                                    that.highlight(that.symbol, 6);
890*d7d00a41SKrystof Tulinger                                }
891*d7d00a41SKrystof Tulinger                                break;
892*d7d00a41SKrystof Tulinger                            case 56: // 7
893*d7d00a41SKrystof Tulinger                                that.unhighlightAll();
894*d7d00a41SKrystof Tulinger                                break;
895*d7d00a41SKrystof Tulinger                            case 110: // n
896*d7d00a41SKrystof Tulinger                                that.scrollToNextElement(1);
897*d7d00a41SKrystof Tulinger                                break;
898*d7d00a41SKrystof Tulinger                            case 98: // b
899*d7d00a41SKrystof Tulinger                                that.scrollToNextElement(-1);
900*d7d00a41SKrystof Tulinger                                break;
901*d7d00a41SKrystof Tulinger                            default:
902*d7d00a41SKrystof Tulinger                        }
903*d7d00a41SKrystof Tulinger                        return true;
904*d7d00a41SKrystof Tulinger                    });
905*d7d00a41SKrystof Tulinger                    this.getSymbols().mouseover(function () {
906*d7d00a41SKrystof Tulinger                        that.changeSymbol($(this));
907*d7d00a41SKrystof Tulinger                    });
908*d7d00a41SKrystof Tulinger                    this.project = this.options.project || $("input[name='project']").val();
909*d7d00a41SKrystof Tulinger                    this.contextPath = browserWindow.contextPath;
910*d7d00a41SKrystof Tulinger                }
911*d7d00a41SKrystof Tulinger            }, options || {}), $.extend({
912*d7d00a41SKrystof Tulinger                symbol: undefined,
913*d7d00a41SKrystof Tulinger                project: undefined,
914*d7d00a41SKrystof Tulinger                $symbols: undefined,
915*d7d00a41SKrystof Tulinger                $current: undefined,
916*d7d00a41SKrystof Tulinger                $last_highlighted_current: $(),
917*d7d00a41SKrystof Tulinger                $search_defs: undefined,
918*d7d00a41SKrystof Tulinger                $search_refs: undefined,
919*d7d00a41SKrystof Tulinger                $search_full: undefined,
920*d7d00a41SKrystof Tulinger                $search_files: undefined,
921*d7d00a41SKrystof Tulinger                $search_google: undefined,
922*d7d00a41SKrystof Tulinger                changeSymbol: function ($el) {
923*d7d00a41SKrystof Tulinger                    this.$current = $el;
924*d7d00a41SKrystof Tulinger                    this.$last_highlighted_current = $el.hasClass("symbol-highlighted") ? $el : this.$last_highlighted_current;
925*d7d00a41SKrystof Tulinger                    this.symbol = $el.text();
926*d7d00a41SKrystof Tulinger                    this.place = $el.data("definition-place");
927*d7d00a41SKrystof Tulinger                    this.$window.find('.hidden-on-start').show();
928*d7d00a41SKrystof Tulinger                    this.$window.find(".symbol-name").text(this.symbol);
929*d7d00a41SKrystof Tulinger                    this.$window.find(".symbol-description").text(this.getSymbolDescription(this.place));
930*d7d00a41SKrystof Tulinger                    this.modifyLinks();
931*d7d00a41SKrystof Tulinger                },
932*d7d00a41SKrystof Tulinger                modifyLinks: function () {
933*d7d00a41SKrystof Tulinger                    this.$search_defs = this.$search_defs || this.$window.find('.search-defs');
934*d7d00a41SKrystof Tulinger                    this.$search_refs = this.$search_refs || this.$window.find('.search-refs');
935*d7d00a41SKrystof Tulinger                    this.$search_full = this.$search_full || this.$window.find('.search-full');
936*d7d00a41SKrystof Tulinger                    this.$search_files = this.$search_files || this.$window.find('.search-files');
937*d7d00a41SKrystof Tulinger                    this.$search_google = this.$search_google || this.$window.find('.search-google');
938*d7d00a41SKrystof Tulinger
939*d7d00a41SKrystof Tulinger                    this.$search_defs.attr('href', this.getSearchLink('defs'));
940*d7d00a41SKrystof Tulinger                    this.$search_refs.attr('href', this.getSearchLink('refs'));
941*d7d00a41SKrystof Tulinger                    this.$search_full.attr('href', this.getSearchLink('full'));
942*d7d00a41SKrystof Tulinger                    this.$search_files.attr('href', this.getSearchLink('path'));
943*d7d00a41SKrystof Tulinger                    this.$search_google.attr('href', this.options.google_url + this.symbol);
944*d7d00a41SKrystof Tulinger                },
945*d7d00a41SKrystof Tulinger                getSearchLink: function (query) {
946*d7d00a41SKrystof Tulinger                    return this.options.contextPath + '/search?' + query + '=' + this.symbol + '&project=' + this.project;
947*d7d00a41SKrystof Tulinger                },
948*d7d00a41SKrystof Tulinger                getSymbolDescription: function (place) {
949*d7d00a41SKrystof Tulinger                    switch (place) {
950*d7d00a41SKrystof Tulinger                        case "def":
951*d7d00a41SKrystof Tulinger                            return "A declaration or definition.";
952*d7d00a41SKrystof Tulinger                        case "defined-in-file":
953*d7d00a41SKrystof Tulinger                            return "A symbol declared or defined in this file.";
954*d7d00a41SKrystof Tulinger                        case "undefined-in-file":
955*d7d00a41SKrystof Tulinger                            return "A symbol declared or defined elsewhere.";
956*d7d00a41SKrystof Tulinger                        default:
957*d7d00a41SKrystof Tulinger                            // should not happen
958*d7d00a41SKrystof Tulinger                            return "Something I have no idea about.";
959*d7d00a41SKrystof Tulinger                    }
960*d7d00a41SKrystof Tulinger                },
961*d7d00a41SKrystof Tulinger                getSymbols: function () {
962*d7d00a41SKrystof Tulinger                    return (this.$symbols = this.$symbols || $(this.options.selector));
963*d7d00a41SKrystof Tulinger                },
964*d7d00a41SKrystof Tulinger                highlight: function (symbol, color) {
965*d7d00a41SKrystof Tulinger                    if (this.$current.text() === symbol) {
966*d7d00a41SKrystof Tulinger                        this.$last_highlighted_current = this.$current;
967*d7d00a41SKrystof Tulinger                    }
968*d7d00a41SKrystof Tulinger                    return this.getSymbols().filter(function () {
969*d7d00a41SKrystof Tulinger                        return $(this).text() === symbol;
970*d7d00a41SKrystof Tulinger                    }).addClass('symbol-highlighted').addClass('hightlight-color-' + (color || 1));
971*d7d00a41SKrystof Tulinger                },
972*d7d00a41SKrystof Tulinger                unhighlight: function (symbol) {
973*d7d00a41SKrystof Tulinger                    if (this.$last_highlighted_current &&
974*d7d00a41SKrystof Tulinger                            this.$last_highlighted_current.text() === symbol &&
975*d7d00a41SKrystof Tulinger                            this.$last_highlighted_current.hasClass('symbol-highlighted')) {
976*d7d00a41SKrystof Tulinger                        const i = this.getSymbols().index(this.$last_highlighted_current);
977*d7d00a41SKrystof Tulinger                        this.$last_highlighted_jump = this.getSymbols().slice(0, i).filter('.symbol-highlighted').last();
978*d7d00a41SKrystof Tulinger                    }
979*d7d00a41SKrystof Tulinger                    return this.getSymbols().filter(".symbol-highlighted").filter(function () {
980*d7d00a41SKrystof Tulinger                        return $(this).text() === symbol;
981*d7d00a41SKrystof Tulinger                    }).removeClass('symbol-highlighted')
982*d7d00a41SKrystof Tulinger                        .removeClass(function (index, className) {
983*d7d00a41SKrystof Tulinger                            return (className.match(/(^|\s)hightlight-color-\S+/g) || []).join(' ');
984*d7d00a41SKrystof Tulinger                        });
985*d7d00a41SKrystof Tulinger                },
986*d7d00a41SKrystof Tulinger                unhighlightAll: function () {
987*d7d00a41SKrystof Tulinger                    this.$last_highlighted_current = undefined;
988*d7d00a41SKrystof Tulinger                    return this.getSymbols().filter(".symbol-highlighted").
989*d7d00a41SKrystof Tulinger                            removeClass("symbol-highlighted").
990*d7d00a41SKrystof Tulinger                            removeClass(function (index, className) {
991*d7d00a41SKrystof Tulinger                                return (className.match (/(^|\s)hightlight-color-\S+/g) || []).join(' ');
992*d7d00a41SKrystof Tulinger                            });
993*d7d00a41SKrystof Tulinger                },
994*d7d00a41SKrystof Tulinger                scrollTop: function ($el) {
995*d7d00a41SKrystof Tulinger                    if (this.options.scrollTop) {
996*d7d00a41SKrystof Tulinger                        this.options.scrollTop($el);
997*d7d00a41SKrystof Tulinger                    } else {
998*d7d00a41SKrystof Tulinger                        $('html, body').stop().animate({
999*d7d00a41SKrystof Tulinger                            scrollTop: $el.offset().top - $("#src").offset().top
1000*d7d00a41SKrystof Tulinger                        }, 500);
1001*d7d00a41SKrystof Tulinger                    }
1002*d7d00a41SKrystof Tulinger                },
1003*d7d00a41SKrystof Tulinger                scrollToNextElement: function (direction) {
1004*d7d00a41SKrystof Tulinger                    const UP = -1;
1005*d7d00a41SKrystof Tulinger                    const DOWN = 1;
1006*d7d00a41SKrystof Tulinger                    const $highlighted = this.getSymbols().filter(".symbol-highlighted");
1007*d7d00a41SKrystof Tulinger                    let $el = $highlighted.length && this.$last_highlighted_current ? this.$last_highlighted_current : this.$current;
1008*d7d00a41SKrystof Tulinger                    const indexOfCurrent = this.getSymbols().index($el);
1009*d7d00a41SKrystof Tulinger
1010*d7d00a41SKrystof Tulinger                    switch (direction) {
1011*d7d00a41SKrystof Tulinger                        case DOWN:
1012*d7d00a41SKrystof Tulinger                            $el = this.getSymbols().slice(indexOfCurrent + 1);
1013*d7d00a41SKrystof Tulinger                            if ($highlighted.length) {
1014*d7d00a41SKrystof Tulinger                                $el = $el.filter('.symbol-highlighted');
1015*d7d00a41SKrystof Tulinger                            }
1016*d7d00a41SKrystof Tulinger                            if (!$el.length) {
1017*d7d00a41SKrystof Tulinger                                this.$window.error("This is the last occurence!");
1018*d7d00a41SKrystof Tulinger                                return;
1019*d7d00a41SKrystof Tulinger                            }
1020*d7d00a41SKrystof Tulinger                            $el = $el.first();
1021*d7d00a41SKrystof Tulinger                            break;
1022*d7d00a41SKrystof Tulinger                        case UP:
1023*d7d00a41SKrystof Tulinger                            $el = this.getSymbols().slice(0, indexOfCurrent);
1024*d7d00a41SKrystof Tulinger                            if ($highlighted.length) {
1025*d7d00a41SKrystof Tulinger                                $el = $el.filter('.symbol-highlighted');
1026*d7d00a41SKrystof Tulinger                            }
1027*d7d00a41SKrystof Tulinger                            if (!$el.length) {
1028*d7d00a41SKrystof Tulinger                                this.$window.error("This is the first occurence!");
1029*d7d00a41SKrystof Tulinger                                return;
1030*d7d00a41SKrystof Tulinger                            }
1031*d7d00a41SKrystof Tulinger                            $el = $el.last();
1032*d7d00a41SKrystof Tulinger                            break;
1033*d7d00a41SKrystof Tulinger                        default:
1034*d7d00a41SKrystof Tulinger                            this.$window.error("Unknown direction");
1035*d7d00a41SKrystof Tulinger                            return;
1036*d7d00a41SKrystof Tulinger                    }
1037*d7d00a41SKrystof Tulinger
1038*d7d00a41SKrystof Tulinger                    this.scrollTop($el);
1039*d7d00a41SKrystof Tulinger                    this.changeSymbol($el);
1040*d7d00a41SKrystof Tulinger                },
1041*d7d00a41SKrystof Tulinger                bindOnClick: function ($el, callback, param) {
1042*d7d00a41SKrystof Tulinger                    const that = this;
1043*d7d00a41SKrystof Tulinger                    $el.click(function (e) {
1044*d7d00a41SKrystof Tulinger                        e.preventDefault();
1045*d7d00a41SKrystof Tulinger                        callback.call(that, param || that.symbol);
1046*d7d00a41SKrystof Tulinger                        return false;
1047*d7d00a41SKrystof Tulinger                    });
1048*d7d00a41SKrystof Tulinger                }
1049*d7d00a41SKrystof Tulinger            }, context || {}));
1050*d7d00a41SKrystof Tulinger            return $.intelliWindow;
1051*d7d00a41SKrystof Tulinger        };
1052*d7d00a41SKrystof Tulinger    };
1053*d7d00a41SKrystof Tulinger    $.intelliWindow = new ($.extend(intelliWindow, $.intelliWindow ? $.intelliWindow : {}))();
1054*d7d00a41SKrystof Tulinger})(window, document, jQuery, jQuery.window);
1055*d7d00a41SKrystof Tulinger
1056*d7d00a41SKrystof Tulinger/**
1057*d7d00a41SKrystof Tulinger * Messages window plugin.
1058*d7d00a41SKrystof Tulinger *
1059*d7d00a41SKrystof Tulinger * @author Kryštof Tulinger
1060*d7d00a41SKrystof Tulinger */
1061*d7d00a41SKrystof Tulinger(function (browserWindow, document, $, $window) {
1062*d7d00a41SKrystof Tulinger    if (!$window || typeof $window.create !== 'function') {
1063*d7d00a41SKrystof Tulinger        console.log("The messagesWindow plugin requires $.window plugin");
1064*d7d00a41SKrystof Tulinger        return;
1065*d7d00a41SKrystof Tulinger    }
1066*d7d00a41SKrystof Tulinger
1067*d7d00a41SKrystof Tulinger    const messagesWindow = function () {
1068*d7d00a41SKrystof Tulinger        this.init = function (options, context) {
1069*d7d00a41SKrystof Tulinger            $.messagesWindow = $window.create($.extend({
1070*d7d00a41SKrystof Tulinger                title: 'Messages Window',
1071*d7d00a41SKrystof Tulinger                draggable: false,
1072*d7d00a41SKrystof Tulinger                init: function ($window) {
1073*d7d00a41SKrystof Tulinger                    return $window.
1074*d7d00a41SKrystof Tulinger                            attr('id', 'messages_win').
1075*d7d00a41SKrystof Tulinger                            addClass('messages-window').
1076*d7d00a41SKrystof Tulinger                            addClass('diff_navigation_style').
1077*d7d00a41SKrystof Tulinger                            css({top: '150px', right: '20px'}).
1078*d7d00a41SKrystof Tulinger                            body().
1079*d7d00a41SKrystof Tulinger                            append(this.$messages = $("<div>")).
1080*d7d00a41SKrystof Tulinger                            window();
1081*d7d00a41SKrystof Tulinger                },
1082*d7d00a41SKrystof Tulinger                load: function ($window) {
1083*d7d00a41SKrystof Tulinger                    $window.mouseenter(function () {
1084*d7d00a41SKrystof Tulinger                        $window.show();
1085*d7d00a41SKrystof Tulinger                    }).mouseleave(function () {
1086*d7d00a41SKrystof Tulinger                        $window.hide();
1087*d7d00a41SKrystof Tulinger                    });
1088*d7d00a41SKrystof Tulinger
1089*d7d00a41SKrystof Tulinger                    // simulate show/toggle and move
1090*d7d00a41SKrystof Tulinger                    $.each(['show', 'toggle'], function () {
1091*d7d00a41SKrystof Tulinger                        const old = $window[this];
1092*d7d00a41SKrystof Tulinger                        $window[this] = function () {
1093*d7d00a41SKrystof Tulinger                            return old.call($window).move();
1094*d7d00a41SKrystof Tulinger                        };
1095*d7d00a41SKrystof Tulinger                    });
1096*d7d00a41SKrystof Tulinger                },
1097*d7d00a41SKrystof Tulinger                update: function (data) {
1098*d7d00a41SKrystof Tulinger                    this.$messages.empty();
1099*d7d00a41SKrystof Tulinger                    for (let tag of data) {
1100*d7d00a41SKrystof Tulinger                        if (!tag || tag.messages.length === 0) {
1101*d7d00a41SKrystof Tulinger                            continue;
1102*d7d00a41SKrystof Tulinger                        }
1103*d7d00a41SKrystof Tulinger                        this.$messages.append($("<h5>").
1104*d7d00a41SKrystof Tulinger                                addClass('message-group-caption').
1105*d7d00a41SKrystof Tulinger                                text(tag.tag.charAt(0).toUpperCase() + tag.tag.slice(1)));
1106*d7d00a41SKrystof Tulinger                        const $ul = $("<ul>").addClass('message-group limited');
1107*d7d00a41SKrystof Tulinger                        for (let j = 0; j < tag.messages.length; j++) {
1108*d7d00a41SKrystof Tulinger                            if (!tag.messages[j]) {
1109*d7d00a41SKrystof Tulinger                                continue;
1110*d7d00a41SKrystof Tulinger                            }
1111*d7d00a41SKrystof Tulinger                            $ul.append(
1112*d7d00a41SKrystof Tulinger                                $('<li>').
1113*d7d00a41SKrystof Tulinger                                    addClass('message-group-item').
1114*d7d00a41SKrystof Tulinger                                    addClass(tag.messages[j].messageLevel).
1115*d7d00a41SKrystof Tulinger                                    attr('title', 'Expires on ' + tag.messages[j].expiration).
1116*d7d00a41SKrystof Tulinger                                    html(tag.messages[j].created + ': ' + tag.messages[j].text)
1117*d7d00a41SKrystof Tulinger                            );
1118*d7d00a41SKrystof Tulinger                        }
1119*d7d00a41SKrystof Tulinger                        this.$messages.append($ul);
1120*d7d00a41SKrystof Tulinger                    }
1121*d7d00a41SKrystof Tulinger                }
1122*d7d00a41SKrystof Tulinger            }, options || {}), $.extend({
1123*d7d00a41SKrystof Tulinger                $messages: $()
1124*d7d00a41SKrystof Tulinger            }, context || {}));
1125*d7d00a41SKrystof Tulinger            return $.messagesWindow;
1126*d7d00a41SKrystof Tulinger        };
1127*d7d00a41SKrystof Tulinger    };
1128*d7d00a41SKrystof Tulinger    $.messagesWindow = new ($.extend(messagesWindow, $.messagesWindow ? $.messagesWindow : {}))();
1129*d7d00a41SKrystof Tulinger})(window, document, jQuery, jQuery.window);
1130*d7d00a41SKrystof Tulinger
1131*d7d00a41SKrystof Tulinger/**
1132*d7d00a41SKrystof Tulinger * Scopes window plugin.
1133*d7d00a41SKrystof Tulinger *
1134*d7d00a41SKrystof Tulinger * @author Kryštof Tulinger
1135*d7d00a41SKrystof Tulinger */
1136*d7d00a41SKrystof Tulinger(function (browserWindow, document, $, $window) {
1137*d7d00a41SKrystof Tulinger    if (!$window || typeof $window.create !== 'function') {
1138*d7d00a41SKrystof Tulinger        console.log("The scopesWindow plugin requires $.window plugin");
1139*d7d00a41SKrystof Tulinger        return;
1140*d7d00a41SKrystof Tulinger    }
1141*d7d00a41SKrystof Tulinger
1142*d7d00a41SKrystof Tulinger    const scopesWindow = function () {
1143*d7d00a41SKrystof Tulinger        this.init = function (options, context) {
1144*d7d00a41SKrystof Tulinger            $.scopesWindow = $window.create($.extend({
1145*d7d00a41SKrystof Tulinger                title: 'Scopes Window',
1146*d7d00a41SKrystof Tulinger                draggable: false,
1147*d7d00a41SKrystof Tulinger                init: function ($window) {
1148*d7d00a41SKrystof Tulinger                    return $window.
1149*d7d00a41SKrystof Tulinger                            attr('id', 'scopes_win').
1150*d7d00a41SKrystof Tulinger                            addClass('scopes-window').
1151*d7d00a41SKrystof Tulinger                            addClass('diff_navigation_style').
1152*d7d00a41SKrystof Tulinger                            css({top: '150px', right: '20px'}).
1153*d7d00a41SKrystof Tulinger                            body().
1154*d7d00a41SKrystof Tulinger                            append(this.$scopes = $("<div>")).
1155*d7d00a41SKrystof Tulinger                            window();
1156*d7d00a41SKrystof Tulinger                },
1157*d7d00a41SKrystof Tulinger                load: function ($window) {
1158*d7d00a41SKrystof Tulinger                    $window.hide().css('top', $("#content").offset().top + 10 + 'px');
1159*d7d00a41SKrystof Tulinger
1160*d7d00a41SKrystof Tulinger                    // override the hide and show to throw an event and run
1161*d7d00a41SKrystof Tulinger                    // scope_on_scroll() for update
1162*d7d00a41SKrystof Tulinger                    $.each(['hide', 'show'], function () {
1163*d7d00a41SKrystof Tulinger                        const event = this;
1164*d7d00a41SKrystof Tulinger                        const old = $window[event];
1165*d7d00a41SKrystof Tulinger                        $window[event] = function () {
1166*d7d00a41SKrystof Tulinger                            var $toReturn = old.call($window).trigger(event);
1167*d7d00a41SKrystof Tulinger                            if (!scope_on_scroll || typeof scope_on_scroll !== 'function') {
1168*d7d00a41SKrystof Tulinger                                console.debug("[scopesWindow]: The scope_on_scroll() is not a function at this point.");
1169*d7d00a41SKrystof Tulinger                                return $toReturn;
1170*d7d00a41SKrystof Tulinger                            }
1171*d7d00a41SKrystof Tulinger                            scope_on_scroll();
1172*d7d00a41SKrystof Tulinger                            return $toReturn;
1173*d7d00a41SKrystof Tulinger                        };
1174*d7d00a41SKrystof Tulinger                    });
1175*d7d00a41SKrystof Tulinger
1176*d7d00a41SKrystof Tulinger                    $('.scopes-toggle').click(function () {
1177*d7d00a41SKrystof Tulinger                        $window.toggle();
1178*d7d00a41SKrystof Tulinger                        return false;
1179*d7d00a41SKrystof Tulinger                    });
1180*d7d00a41SKrystof Tulinger                },
1181*d7d00a41SKrystof Tulinger                update: function (data) {
1182*d7d00a41SKrystof Tulinger                    if(!this.$window.is(':visible') && !this.$window.data('shown-once')) {
1183*d7d00a41SKrystof Tulinger                        this.$window.show().data('shown-once', true);
1184*d7d00a41SKrystof Tulinger                    }
1185*d7d00a41SKrystof Tulinger                    this.$scopes.empty();
1186*d7d00a41SKrystof Tulinger                    this.$scopes.html(this.buildLink(data.id, data.link));
1187*d7d00a41SKrystof Tulinger                    this.$window.trigger('update');
1188*d7d00a41SKrystof Tulinger                }
1189*d7d00a41SKrystof Tulinger            }, options || {}), $.extend({
1190*d7d00a41SKrystof Tulinger                $scopes: $(),
1191*d7d00a41SKrystof Tulinger                buildLink: function (href, name) {
1192*d7d00a41SKrystof Tulinger                    return $('<a>').attr('href', '#' + href).attr('title', name).html(name);
1193*d7d00a41SKrystof Tulinger                }
1194*d7d00a41SKrystof Tulinger            }, context || {}));
1195*d7d00a41SKrystof Tulinger            return $.scopesWindow;
1196*d7d00a41SKrystof Tulinger        };
1197*d7d00a41SKrystof Tulinger    };
1198*d7d00a41SKrystof Tulinger    $.scopesWindow = new ($.extend(scopesWindow, $.scopesWindow ? $.scopesWindow : {}))();
1199*d7d00a41SKrystof Tulinger})(window, document, jQuery, jQuery.window);
1200*d7d00a41SKrystof Tulinger
1201*d7d00a41SKrystof Tulinger/**
1202*d7d00a41SKrystof Tulinger * Navigate window plugin.
1203*d7d00a41SKrystof Tulinger *
1204*d7d00a41SKrystof Tulinger * @author Kryštof Tulinger
1205*d7d00a41SKrystof Tulinger */
1206*d7d00a41SKrystof Tulinger(function (browserWindow, document, $, $window) {
1207*d7d00a41SKrystof Tulinger    if (!$window || typeof $window.create !== 'function') {
1208*d7d00a41SKrystof Tulinger        console.log("The navigateWindow plugin requires $.window plugin");
1209*d7d00a41SKrystof Tulinger        return;
1210*d7d00a41SKrystof Tulinger    }
1211*d7d00a41SKrystof Tulinger
1212*d7d00a41SKrystof Tulinger    const navigateWindow = function () {
1213*d7d00a41SKrystof Tulinger        this.init = function (options, context) {
1214*d7d00a41SKrystof Tulinger            $.navigateWindow = $window.create($.extend({
1215*d7d00a41SKrystof Tulinger                title: 'Navigate Window',
1216*d7d00a41SKrystof Tulinger                draggable: false,
1217*d7d00a41SKrystof Tulinger                init: function ($window) {
1218*d7d00a41SKrystof Tulinger                    return $window.
1219*d7d00a41SKrystof Tulinger                            attr('id', 'navigate_win').
1220*d7d00a41SKrystof Tulinger                            addClass('navigate-window').
1221*d7d00a41SKrystof Tulinger                            addClass('diff_navigation_style').
1222*d7d00a41SKrystof Tulinger                            addClass('diff_navigation_style').
1223*d7d00a41SKrystof Tulinger                            css({top: '150px', right: '20px', height: this.defaultHeight + 'px'}).
1224*d7d00a41SKrystof Tulinger                            body().
1225*d7d00a41SKrystof Tulinger                            append(this.$content).
1226*d7d00a41SKrystof Tulinger                            window();
1227*d7d00a41SKrystof Tulinger                },
1228*d7d00a41SKrystof Tulinger                load: function ($window) {
1229*d7d00a41SKrystof Tulinger                    const that = this;
1230*d7d00a41SKrystof Tulinger                    $window.css('top', this.getTopOffset() + 10 + 'px');
1231*d7d00a41SKrystof Tulinger
1232*d7d00a41SKrystof Tulinger                    if ($.scopesWindow && $.scopesWindow.initialized) {
1233*d7d00a41SKrystof Tulinger                        $.scopesWindow.on('show', function () {
1234*d7d00a41SKrystof Tulinger                            setTimeout(function () {
1235*d7d00a41SKrystof Tulinger                                that.updatePosition($window);
1236*d7d00a41SKrystof Tulinger                            }, 100);
1237*d7d00a41SKrystof Tulinger                        }).on('hide', function () {
1238*d7d00a41SKrystof Tulinger                            that.updatePosition($window);
1239*d7d00a41SKrystof Tulinger                        }).on('update', function () {
1240*d7d00a41SKrystof Tulinger                            that.updatePosition($window);
1241*d7d00a41SKrystof Tulinger                        });
1242*d7d00a41SKrystof Tulinger
1243*d7d00a41SKrystof Tulinger                        if ($.scopesWindow.is(':visible')) {
1244*d7d00a41SKrystof Tulinger                            setTimeout(function () {
1245*d7d00a41SKrystof Tulinger                                that.updatePosition($window);
1246*d7d00a41SKrystof Tulinger                            }, 100);
1247*d7d00a41SKrystof Tulinger                        }
1248*d7d00a41SKrystof Tulinger                    }
1249*d7d00a41SKrystof Tulinger
1250*d7d00a41SKrystof Tulinger                    if ($('[data-navigate-window-enabled="true"]').length) {
1251*d7d00a41SKrystof Tulinger                        $window.show();
1252*d7d00a41SKrystof Tulinger                    }
1253*d7d00a41SKrystof Tulinger
1254*d7d00a41SKrystof Tulinger                    // override and show to throw an event and update position
1255*d7d00a41SKrystof Tulinger                    $.each(['show'], function () {
1256*d7d00a41SKrystof Tulinger                        const event = this;
1257*d7d00a41SKrystof Tulinger                        const old = $window[event];
1258*d7d00a41SKrystof Tulinger                        $window[event] = function () {
1259*d7d00a41SKrystof Tulinger                            return that.updatePosition(old.call($window).trigger(event));
1260*d7d00a41SKrystof Tulinger                        };
1261*d7d00a41SKrystof Tulinger                    });
1262*d7d00a41SKrystof Tulinger
1263*d7d00a41SKrystof Tulinger                    $(browserWindow).resize(function () {
1264*d7d00a41SKrystof Tulinger                        that.updatePosition($window);
1265*d7d00a41SKrystof Tulinger                    });
1266*d7d00a41SKrystof Tulinger                    that.updatePosition($window);
1267*d7d00a41SKrystof Tulinger                },
1268*d7d00a41SKrystof Tulinger                update: function (data) {
1269*d7d00a41SKrystof Tulinger                    let $ul;
1270*d7d00a41SKrystof Tulinger                    this.$content.empty();
1271*d7d00a41SKrystof Tulinger                    for (let i = 0; i < data.length; i++) {
1272*d7d00a41SKrystof Tulinger                        this.$content.append($('<h4>').text(data[i][0]));
1273*d7d00a41SKrystof Tulinger                        if (data[i][2].length === 0) {
1274*d7d00a41SKrystof Tulinger                            continue;
1275*d7d00a41SKrystof Tulinger                        }
1276*d7d00a41SKrystof Tulinger                        this.$content.append($ul = $('<ul>'));
1277*d7d00a41SKrystof Tulinger                        for (let j = 0; j < data[i][2].length; j++) {
1278*d7d00a41SKrystof Tulinger                            $ul.append($('<li>').append(this.buildLink(data[i][2][j][1], data[i][2][j][0], data[i][1])));
1279*d7d00a41SKrystof Tulinger                        }
1280*d7d00a41SKrystof Tulinger                    }
1281*d7d00a41SKrystof Tulinger                    this.updatePosition(this.$window);
1282*d7d00a41SKrystof Tulinger                }
1283*d7d00a41SKrystof Tulinger            }, options || {
1284*d7d00a41SKrystof Tulinger            }), $.extend({
1285*d7d00a41SKrystof Tulinger                $content: $('<div>'),
1286*d7d00a41SKrystof Tulinger                defaultHeight: 480,
1287*d7d00a41SKrystof Tulinger                buildLink: function (href, name, c) {
1288*d7d00a41SKrystof Tulinger                    return $('<a>').attr('href', '#' + href).attr('title', this.escapeHtml(name)).addClass(c).html(this.escapeHtml(name)).click(lnshow);
1289*d7d00a41SKrystof Tulinger                },
1290*d7d00a41SKrystof Tulinger                getTopOffset: function () {
1291*d7d00a41SKrystof Tulinger                    return $("#content").offset().top;
1292*d7d00a41SKrystof Tulinger                },
1293*d7d00a41SKrystof Tulinger                updatePosition: function ($w) {
1294*d7d00a41SKrystof Tulinger                    if (!$w.is(':visible')) {
1295*d7d00a41SKrystof Tulinger                        /**
1296*d7d00a41SKrystof Tulinger                         * If the window is not visible then this
1297*d7d00a41SKrystof Tulinger                         * function is expensive as
1298*d7d00a41SKrystof Tulinger                         * <a href="http://api.jquery.com/outerheight/">documented</a>
1299*d7d00a41SKrystof Tulinger                         * under additional notes.
1300*d7d00a41SKrystof Tulinger                         */
1301*d7d00a41SKrystof Tulinger                        return $w;
1302*d7d00a41SKrystof Tulinger                    }
1303*d7d00a41SKrystof Tulinger
1304*d7d00a41SKrystof Tulinger                    const a = {};
1305*d7d00a41SKrystof Tulinger                    a.top = this.getTopOffset() + 10;
1306*d7d00a41SKrystof Tulinger                    if ($.scopesWindow &&
1307*d7d00a41SKrystof Tulinger                            $.scopesWindow.initialized &&
1308*d7d00a41SKrystof Tulinger                            $.scopesWindow.is(':visible')) {
1309*d7d00a41SKrystof Tulinger                        a.top = $.scopesWindow.position().top + $.scopesWindow.outerHeight() + 20;
1310*d7d00a41SKrystof Tulinger                    }
1311*d7d00a41SKrystof Tulinger                    a.height = Math.min(parseFloat($w.css('max-height')) || this.defaultHeight, $(browserWindow).outerHeight() - a.top - ($w.outerHeight(true) - $w.height()) - 20);
1312*d7d00a41SKrystof Tulinger
1313*d7d00a41SKrystof Tulinger                    if (a.height == $w.height() && a.top == this.getTopOffset()) {
1314*d7d00a41SKrystof Tulinger                        return $w;
1315*d7d00a41SKrystof Tulinger                    }
1316*d7d00a41SKrystof Tulinger
1317*d7d00a41SKrystof Tulinger                    if (this.$content.children().length === 0) {
1318*d7d00a41SKrystof Tulinger                        // the window is empty
1319*d7d00a41SKrystof Tulinger                        delete a.height;
1320*d7d00a41SKrystof Tulinger                    }
1321*d7d00a41SKrystof Tulinger
1322*d7d00a41SKrystof Tulinger                    return $w.stop().animate(a);
1323*d7d00a41SKrystof Tulinger                },
1324*d7d00a41SKrystof Tulinger                escapeHtml: function (html) {
1325*d7d00a41SKrystof Tulinger                    return html.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1326*d7d00a41SKrystof Tulinger                }
1327*d7d00a41SKrystof Tulinger            }, context || {}));
1328*d7d00a41SKrystof Tulinger            return $.navigateWindow;
1329*d7d00a41SKrystof Tulinger        };
1330*d7d00a41SKrystof Tulinger    };
1331*d7d00a41SKrystof Tulinger    $.navigateWindow = new ($.extend(navigateWindow, $.navigateWindow ? $.navigateWindow : {}))();
1332*d7d00a41SKrystof Tulinger})(window, document, jQuery, jQuery.window);
1333*d7d00a41SKrystof Tulinger
1334*d7d00a41SKrystof Tulingerfunction init_scopes() {
1335*d7d00a41SKrystof Tulinger    $.scopesWindow.init();
1336*d7d00a41SKrystof Tulinger    $(window).scroll(scope_on_scroll);
1337*d7d00a41SKrystof Tulinger}
1338*d7d00a41SKrystof Tulinger
1339*d7d00a41SKrystof Tulingerfunction init_results_autohide() {
1340*d7d00a41SKrystof Tulinger    $("#sbox input[type='submit']").click(function (e) {
1341*d7d00a41SKrystof Tulinger        $("#footer").not(".main_page").hide(); // footer
1342*d7d00a41SKrystof Tulinger        $("#results > .message-group").hide(); // messages
1343*d7d00a41SKrystof Tulinger        $("#results > p.suggestions").hide(); // suggestions
1344*d7d00a41SKrystof Tulinger        $("#results > p.pagetitle").hide(); // description
1345*d7d00a41SKrystof Tulinger        $("#results > p.slider").hide(); // pagination
1346*d7d00a41SKrystof Tulinger        $("#results > h3").hide(); // error
1347*d7d00a41SKrystof Tulinger        $("#results > table, #results > ul").hide(); // results + empty
1348*d7d00a41SKrystof Tulinger        $("#results > table + p, #results > ul + p").hide(); // results + empty timing
1349*d7d00a41SKrystof Tulinger    });
1350*d7d00a41SKrystof Tulinger}
1351*d7d00a41SKrystof Tulinger
1352*d7d00a41SKrystof Tulingerfunction init_searchable_option_list() {
1353*d7d00a41SKrystof Tulinger    function init_sol_on_type_combobox() {
1354*d7d00a41SKrystof Tulinger        const $type = $('#type');
1355*d7d00a41SKrystof Tulinger        if ($type.length === 0) {
1356*d7d00a41SKrystof Tulinger            return;
1357*d7d00a41SKrystof Tulinger        }
1358*d7d00a41SKrystof Tulinger        /**
1359*d7d00a41SKrystof Tulinger         * Has to be here because otherwise the offset()
1360*d7d00a41SKrystof Tulinger         * takes the original long &lt;select&gt; box and the max-height
1361*d7d00a41SKrystof Tulinger         * does not work then.
1362*d7d00a41SKrystof Tulinger         */
1363*d7d00a41SKrystof Tulinger        $type.searchableOptionList({
1364*d7d00a41SKrystof Tulinger            texts: {
1365*d7d00a41SKrystof Tulinger                searchplaceholder: 'Click here to restrict the file type'
1366*d7d00a41SKrystof Tulinger            },
1367*d7d00a41SKrystof Tulinger            maxHeight: $type.offset().top + 'px',
1368*d7d00a41SKrystof Tulinger            /**
1369*d7d00a41SKrystof Tulinger             * Defined in menu.jsp just next to the original &lt;select&gt;
1370*d7d00a41SKrystof Tulinger             */
1371*d7d00a41SKrystof Tulinger            resultsContainer: $("#type-select-container")
1372*d7d00a41SKrystof Tulinger        });
1373*d7d00a41SKrystof Tulinger    }
1374*d7d00a41SKrystof Tulinger    const searchableOptionListOptions = {
1375*d7d00a41SKrystof Tulinger        maxHeight: '300px',
1376*d7d00a41SKrystof Tulinger        showSelectionBelowList: false,
1377*d7d00a41SKrystof Tulinger        showSelectAll: false,
1378*d7d00a41SKrystof Tulinger        maxShow: 30,
1379*d7d00a41SKrystof Tulinger        resultsContainer: $("#ltbl"),
1380*d7d00a41SKrystof Tulinger        numSelectedItem: $("#nn"),
1381*d7d00a41SKrystof Tulinger        quickDeleteForm: $("#sbox"),
1382*d7d00a41SKrystof Tulinger        quickDeletePermit: checkIsOnSearchPage,
1383*d7d00a41SKrystof Tulinger        texts: {
1384*d7d00a41SKrystof Tulinger            searchplaceholder: 'Click here to select project(s)'
1385*d7d00a41SKrystof Tulinger        },
1386*d7d00a41SKrystof Tulinger        events: {
1387*d7d00a41SKrystof Tulinger            onInitialized: function () {
1388*d7d00a41SKrystof Tulinger                if ($.messagesWindow.initialized) {
1389*d7d00a41SKrystof Tulinger                    this.$selectionContainer.find("[data-messages]").mouseenter(function () {
1390*d7d00a41SKrystof Tulinger                        var data = $(this).data('messages') || [];
1391*d7d00a41SKrystof Tulinger                        $.messagesWindow.update(data);
1392*d7d00a41SKrystof Tulinger                        $.messagesWindow.show();
1393*d7d00a41SKrystof Tulinger                    }).mouseleave(function (e) {
1394*d7d00a41SKrystof Tulinger                        $.messagesWindow.hide();
1395*d7d00a41SKrystof Tulinger                    });
1396*d7d00a41SKrystof Tulinger                }
1397*d7d00a41SKrystof Tulinger            },
1398*d7d00a41SKrystof Tulinger            // override the default onScroll positioning event if necessary
1399*d7d00a41SKrystof Tulinger            onScroll: function () {
1400*d7d00a41SKrystof Tulinger
1401*d7d00a41SKrystof Tulinger                const posY = this.$input.offset().top - this.config.scrollTarget.scrollTop() + this.$input.outerHeight() + 1;
1402*d7d00a41SKrystof Tulinger                let selectionContainerWidth = this.$innerContainer.outerWidth(false) - parseInt(this.$selectionContainer.css('border-left-width'), 10) - parseInt(this.$selectionContainer.css('border-right-width'), 10);
1403*d7d00a41SKrystof Tulinger
1404*d7d00a41SKrystof Tulinger                if (this.$innerContainer.css('display') !== 'block') {
1405*d7d00a41SKrystof Tulinger                    // container has a certain width
1406*d7d00a41SKrystof Tulinger                    // make selection container a bit wider
1407*d7d00a41SKrystof Tulinger                    selectionContainerWidth = Math.ceil(selectionContainerWidth * 1.2);
1408*d7d00a41SKrystof Tulinger                } else {
1409*d7d00a41SKrystof Tulinger                    // no border radius on top
1410*d7d00a41SKrystof Tulinger                    this.$selectionContainer.css('border-top-right-radius', 'initial');
1411*d7d00a41SKrystof Tulinger
1412*d7d00a41SKrystof Tulinger                    if (this.$actionButtons) {
1413*d7d00a41SKrystof Tulinger                        this.$actionButtons.css('border-top-right-radius', 'initial');
1414*d7d00a41SKrystof Tulinger                    }
1415*d7d00a41SKrystof Tulinger                }
1416*d7d00a41SKrystof Tulinger
1417*d7d00a41SKrystof Tulinger                this.$selectionContainer.
1418*d7d00a41SKrystof Tulinger                        css('top', Math.floor(posY)).
1419*d7d00a41SKrystof Tulinger                        css('left', Math.floor(this.$container.offset().left)).
1420*d7d00a41SKrystof Tulinger                        css('width', selectionContainerWidth);
1421*d7d00a41SKrystof Tulinger            },
1422*d7d00a41SKrystof Tulinger            onRendered: init_sol_on_type_combobox
1423*d7d00a41SKrystof Tulinger        }
1424*d7d00a41SKrystof Tulinger    };
1425*d7d00a41SKrystof Tulinger
1426*d7d00a41SKrystof Tulinger    const $project = $('#project');
1427*d7d00a41SKrystof Tulinger    if ($project.length === 1) {
1428*d7d00a41SKrystof Tulinger        $project.searchableOptionList(searchableOptionListOptions);
1429*d7d00a41SKrystof Tulinger    } else {
1430*d7d00a41SKrystof Tulinger        init_sol_on_type_combobox();
1431*d7d00a41SKrystof Tulinger    }
1432*d7d00a41SKrystof Tulinger}
1433*d7d00a41SKrystof Tulinger
1434*d7d00a41SKrystof Tulingerfunction init_history_input() {
1435*d7d00a41SKrystof Tulinger    $('input[data-revision-path]').click(function () {
1436*d7d00a41SKrystof Tulinger        const $this = $(this);
1437*d7d00a41SKrystof Tulinger        $("a.more").each(function () {
1438*d7d00a41SKrystof Tulinger            $(this).attr('href', setParameter($(this).attr('href'), 'r1', $this.data('revision-1')));
1439*d7d00a41SKrystof Tulinger            $(this).attr('href', setParameter($(this).attr('href'), 'r2', $this.data('revision-2')));
1440*d7d00a41SKrystof Tulinger        });
1441*d7d00a41SKrystof Tulinger
1442*d7d00a41SKrystof Tulinger        const $revisions = $('input[data-revision-path]');
1443*d7d00a41SKrystof Tulinger
1444*d7d00a41SKrystof Tulinger        // change the correct revision on every element
1445*d7d00a41SKrystof Tulinger        // (every element keeps a track which revision is selected)
1446*d7d00a41SKrystof Tulinger        $revisions.filter("[data-diff-revision='r1']").data('revision-2', $this.data('revision-2'));
1447*d7d00a41SKrystof Tulinger        $revisions.filter("[data-diff-revision='r2']").data('revision-1', $this.data('revision-1'));
1448*d7d00a41SKrystof Tulinger
1449*d7d00a41SKrystof Tulinger        // set the correct revision for the form submission
1450*d7d00a41SKrystof Tulinger        $("#input_" + $this.data('diff-revision')).val($this.data('revision-path'));
1451*d7d00a41SKrystof Tulinger
1452*d7d00a41SKrystof Tulinger        // enable all input
1453*d7d00a41SKrystof Tulinger        $revisions.prop('disabled', false);
1454*d7d00a41SKrystof Tulinger        // uncheck all input in my column
1455*d7d00a41SKrystof Tulinger        $revisions.filter("[data-diff-revision='" + $this.data('diff-revision') + "']").prop('checked', false);
1456*d7d00a41SKrystof Tulinger        // set me as checked
1457*d7d00a41SKrystof Tulinger        $this.prop('checked', true);
1458*d7d00a41SKrystof Tulinger
1459*d7d00a41SKrystof Tulinger        // disable from top to r2
1460*d7d00a41SKrystof Tulinger        let index = Math.max($revisions.index($("input[data-revision-path][data-diff-revision='r2']:checked")), 0);
1461*d7d00a41SKrystof Tulinger        $revisions.slice(0, index).filter("[data-diff-revision='r1']").prop('disabled', true);
1462*d7d00a41SKrystof Tulinger
1463*d7d00a41SKrystof Tulinger        // disable from bottom to r1
1464*d7d00a41SKrystof Tulinger        index = Math.max($revisions.index($("input[data-revision-path][data-diff-revision='r1']:checked")), index);
1465*d7d00a41SKrystof Tulinger        $revisions.slice(index + 1).filter("[data-diff-revision='r2']").prop('disabled', true);
1466*d7d00a41SKrystof Tulinger    });
1467*d7d00a41SKrystof Tulinger}
1468*d7d00a41SKrystof Tulinger
1469*d7d00a41SKrystof Tulingerfunction init_tablesorter() {
1470*d7d00a41SKrystof Tulinger    $("#dirlist").tablesorter({
1471*d7d00a41SKrystof Tulinger        sortList: [[0, 0]],
1472*d7d00a41SKrystof Tulinger        cancelSelection: true,
1473*d7d00a41SKrystof Tulinger        sortReset : true,
1474*d7d00a41SKrystof Tulinger        sortRestart : true,
1475*d7d00a41SKrystof Tulinger        sortInitialOrder: "desc",
1476*d7d00a41SKrystof Tulinger        headers: {
1477*d7d00a41SKrystof Tulinger            1: {
1478*d7d00a41SKrystof Tulinger                sorter: 'text',
1479*d7d00a41SKrystof Tulinger                sortInitialOrder: "asc"
1480*d7d00a41SKrystof Tulinger            },
1481*d7d00a41SKrystof Tulinger            3: {
1482*d7d00a41SKrystof Tulinger                sorter: 'dates'
1483*d7d00a41SKrystof Tulinger            },
1484*d7d00a41SKrystof Tulinger            4: {
1485*d7d00a41SKrystof Tulinger                sorter: 'groksizes'
1486*d7d00a41SKrystof Tulinger            }
1487*d7d00a41SKrystof Tulinger        }
1488*d7d00a41SKrystof Tulinger    });
1489*d7d00a41SKrystof Tulinger}
1490*d7d00a41SKrystof Tulinger
1491*d7d00a41SKrystof Tulingerfunction init_markdown_converter() {
1492*d7d00a41SKrystof Tulinger    let converter = null;
1493*d7d00a41SKrystof Tulinger    $('[data-markdown]').each(function () {
1494*d7d00a41SKrystof Tulinger        var $that = $(this);
1495*d7d00a41SKrystof Tulinger        $.script.loadScript('webjars/xss/1.0.8/dist/xss.min.js').done(function () {
1496*d7d00a41SKrystof Tulinger            $.script.loadScript('webjars/showdown/1.9.1/dist/showdown.min.js').done(function () {
1497*d7d00a41SKrystof Tulinger                $that.find('.markdown-content[data-markdown-download]').each(function () {
1498*d7d00a41SKrystof Tulinger                    var $dataMarkdownDownloadEl = $(this);
1499*d7d00a41SKrystof Tulinger                    if (converter === null) {
1500*d7d00a41SKrystof Tulinger                        converter = new showdown.Converter();
1501*d7d00a41SKrystof Tulinger                        converter.setOption('tables', true);
1502*d7d00a41SKrystof Tulinger                        converter.setOption('strikethrough', true);
1503*d7d00a41SKrystof Tulinger                        converter.setOption('tasklists', true);
1504*d7d00a41SKrystof Tulinger                        converter.setOption('simplifiedAutoLink', true);
1505*d7d00a41SKrystof Tulinger                        converter.setOption('parseImgDimension', true);
1506*d7d00a41SKrystof Tulinger                        converter.setOption('literalMidWordUnderscores', true);
1507*d7d00a41SKrystof Tulinger                    }
1508*d7d00a41SKrystof Tulinger
1509*d7d00a41SKrystof Tulinger                    $.ajax({
1510*d7d00a41SKrystof Tulinger                        url: $(this).data('markdown-download'),
1511*d7d00a41SKrystof Tulinger                        dataType: 'text',
1512*d7d00a41SKrystof Tulinger                        timeout: 5000,
1513*d7d00a41SKrystof Tulinger                        mimeType: 'text/plain'
1514*d7d00a41SKrystof Tulinger                    }).done(function (payload) {
1515*d7d00a41SKrystof Tulinger                        $dataMarkdownDownloadEl.html(filterXSS(converter.makeHtml(payload))).show();
1516*d7d00a41SKrystof Tulinger                        $that.addClass('markdown').find('[data-markdown-original]').hide();
1517*d7d00a41SKrystof Tulinger                    });
1518*d7d00a41SKrystof Tulinger                });
1519*d7d00a41SKrystof Tulinger            });
1520*d7d00a41SKrystof Tulinger        });
1521*d7d00a41SKrystof Tulinger    });
1522*d7d00a41SKrystof Tulinger}
1523*d7d00a41SKrystof Tulinger
1524*d7d00a41SKrystof Tulingerwindow.onload = function () {
1525*d7d00a41SKrystof Tulinger    for (let i in document.pageReady) {
1526*d7d00a41SKrystof Tulinger        document.pageReady[i]();
1527*d7d00a41SKrystof Tulinger    }
1528*d7d00a41SKrystof Tulinger};
1529*d7d00a41SKrystof Tulinger
1530*d7d00a41SKrystof Tulinger$(document).ready(function () {
1531*d7d00a41SKrystof Tulinger    for (let i in this.domReady) {
1532*d7d00a41SKrystof Tulinger        document.domReady[i]();
1533*d7d00a41SKrystof Tulinger    }
1534*d7d00a41SKrystof Tulinger
1535*d7d00a41SKrystof Tulinger    /**
1536*d7d00a41SKrystof Tulinger     * Initialize scope scroll event to display scope information correctly when
1537*d7d00a41SKrystof Tulinger     * the element comes into the viewport.
1538*d7d00a41SKrystof Tulinger     */
1539*d7d00a41SKrystof Tulinger    $('#src').each(function () {
1540*d7d00a41SKrystof Tulinger        init_scopes();
1541*d7d00a41SKrystof Tulinger    });
1542*d7d00a41SKrystof Tulinger
1543*d7d00a41SKrystof Tulinger    /**
1544*d7d00a41SKrystof Tulinger     * Initialize table sorter on every directory listing.
1545*d7d00a41SKrystof Tulinger     */
1546*d7d00a41SKrystof Tulinger    init_tablesorter();
1547*d7d00a41SKrystof Tulinger
1548*d7d00a41SKrystof Tulinger    /**
1549*d7d00a41SKrystof Tulinger     * Initialize intelligence window plugin. Presence of #contextpath indicates
1550*d7d00a41SKrystof Tulinger     * that we use the code view.
1551*d7d00a41SKrystof Tulinger     */
1552*d7d00a41SKrystof Tulinger    $("#contextpath").each(function () {
1553*d7d00a41SKrystof Tulinger        $.intelliWindow.init();
1554*d7d00a41SKrystof Tulinger        return false;
1555*d7d00a41SKrystof Tulinger    });
1556*d7d00a41SKrystof Tulinger
1557*d7d00a41SKrystof Tulinger    /**
1558*d7d00a41SKrystof Tulinger     * Initialize the messages plugin to display
1559*d7d00a41SKrystof Tulinger     * message onhover on every affected element.
1560*d7d00a41SKrystof Tulinger     */
1561*d7d00a41SKrystof Tulinger    $.messagesWindow.init();
1562*d7d00a41SKrystof Tulinger
1563*d7d00a41SKrystof Tulinger    /**
1564*d7d00a41SKrystof Tulinger     * Attaches a onhover listener to display messages for affected elements.
1565*d7d00a41SKrystof Tulinger     */
1566*d7d00a41SKrystof Tulinger    if ($.messagesWindow.initialized) {
1567*d7d00a41SKrystof Tulinger        $("[data-messages]").mouseenter(function () {
1568*d7d00a41SKrystof Tulinger            const data = $(this).data('messages') || [];
1569*d7d00a41SKrystof Tulinger            $.messagesWindow.update(data);
1570*d7d00a41SKrystof Tulinger            $.messagesWindow.show();
1571*d7d00a41SKrystof Tulinger        }).mouseleave(function (e) {
1572*d7d00a41SKrystof Tulinger            $.messagesWindow.hide();
1573*d7d00a41SKrystof Tulinger        });
1574*d7d00a41SKrystof Tulinger    }
1575*d7d00a41SKrystof Tulinger
1576*d7d00a41SKrystof Tulinger    /**
1577*d7d00a41SKrystof Tulinger     * Initialize spaces plugin which automatically inserts a single space between
1578*d7d00a41SKrystof Tulinger     * the line number and the following text. It strongly relies on the fact
1579*d7d00a41SKrystof Tulinger     * that the line numbers are stored in 'name' attribute on each line link.
1580*d7d00a41SKrystof Tulinger     */
1581*d7d00a41SKrystof Tulinger    $.spaces.init();
1582*d7d00a41SKrystof Tulinger
1583*d7d00a41SKrystof Tulinger    /**
1584*d7d00a41SKrystof Tulinger     * Initialize the window hash management. Mainly this allows users to select
1585*d7d00a41SKrystof Tulinger     * multiple lines of code and use that url to send it to somebody else.
1586*d7d00a41SKrystof Tulinger     */
1587*d7d00a41SKrystof Tulinger    $.hash.init({parent: "pre"});
1588*d7d00a41SKrystof Tulinger
1589*d7d00a41SKrystof Tulinger    /**
1590*d7d00a41SKrystof Tulinger     * After hitting the search button, the results or projects are temporarily hidden
1591*d7d00a41SKrystof Tulinger     * until the new page is loaded. That helps to distinguish if search is being in process.
1592*d7d00a41SKrystof Tulinger     */
1593*d7d00a41SKrystof Tulinger    init_results_autohide();
1594*d7d00a41SKrystof Tulinger
1595*d7d00a41SKrystof Tulinger    /**
1596*d7d00a41SKrystof Tulinger     * Initialize the new project picker
1597*d7d00a41SKrystof Tulinger     */
1598*d7d00a41SKrystof Tulinger    init_searchable_option_list();
1599*d7d00a41SKrystof Tulinger
1600*d7d00a41SKrystof Tulinger    /**
1601*d7d00a41SKrystof Tulinger     * Initialize the history input picker.
1602*d7d00a41SKrystof Tulinger     * Checkboxes are automatically covered with a click event and automatically
1603*d7d00a41SKrystof Tulinger     * colored as disabled or checked.
1604*d7d00a41SKrystof Tulinger     *
1605*d7d00a41SKrystof Tulinger     * Also works for paging where it stores the actual selected revision range in the
1606*d7d00a41SKrystof Tulinger     * pagination links.
1607*d7d00a41SKrystof Tulinger     */
1608*d7d00a41SKrystof Tulinger    init_history_input();
1609*d7d00a41SKrystof Tulinger
1610*d7d00a41SKrystof Tulinger    /**
1611*d7d00a41SKrystof Tulinger     * Initialize the markdown converter.
1612*d7d00a41SKrystof Tulinger     *
1613*d7d00a41SKrystof Tulinger     * WARNING: The converter is not XSS safe. If you're not sure about what
1614*d7d00a41SKrystof Tulinger     * could occur in the readmes then rather comment out this.
1615*d7d00a41SKrystof Tulinger     */
1616*d7d00a41SKrystof Tulinger    init_markdown_converter();
1617*d7d00a41SKrystof Tulinger
1618*d7d00a41SKrystof Tulinger    restoreFocusAfterSearchSubmit();
1619*d7d00a41SKrystof Tulinger});
1620*d7d00a41SKrystof Tulinger
1621*d7d00a41SKrystof Tulinger/**
1622*d7d00a41SKrystof Tulinger * Get a parameter value from the URL.
1623*d7d00a41SKrystof Tulinger *
1624*d7d00a41SKrystof Tulinger * @param p the name of the parameter
1625*d7d00a41SKrystof Tulinger * @return the decoded value of parameter p
1626*d7d00a41SKrystof Tulinger */
1627*d7d00a41SKrystof Tulingerfunction getParameter(p) {
1628*d7d00a41SKrystof Tulinger    // First split up the parameter list. That is, transform from
1629*d7d00a41SKrystof Tulinger    //       ?a=b&c=d
1630*d7d00a41SKrystof Tulinger    // to
1631*d7d00a41SKrystof Tulinger    //       [ ["a", "b"], ["c","d"] ]
1632*d7d00a41SKrystof Tulinger    if (getParameter.params === undefined) {
1633*d7d00a41SKrystof Tulinger        getParameter.params = window.location.search.substr(1).split("&").map(
1634*d7d00a41SKrystof Tulinger                function (x) { return x.split("="); });
1635*d7d00a41SKrystof Tulinger    }
1636*d7d00a41SKrystof Tulinger    const params = getParameter.params;
1637*d7d00a41SKrystof Tulinger    // Then look for the parameter.
1638*d7d00a41SKrystof Tulinger    for (let i in params) {
1639*d7d00a41SKrystof Tulinger        if (params[i][0] === p && params[i].length > 1) {
1640*d7d00a41SKrystof Tulinger            return decodeURIComponent(params[i][1]);
1641*d7d00a41SKrystof Tulinger        }
1642*d7d00a41SKrystof Tulinger    }
1643*d7d00a41SKrystof Tulinger    return undefined;
1644*d7d00a41SKrystof Tulinger}
1645*d7d00a41SKrystof Tulinger
1646*d7d00a41SKrystof Tulinger/**
1647*d7d00a41SKrystof Tulinger * Set parameter in the given url.
1648*d7d00a41SKrystof Tulinger * @param string url
1649*d7d00a41SKrystof Tulinger * @param string p parameter name
1650*d7d00a41SKrystof Tulinger * @param string v parameter value
1651*d7d00a41SKrystof Tulinger * @returns string the modified url
1652*d7d00a41SKrystof Tulinger */
1653*d7d00a41SKrystof Tulingerfunction setParameter(url, p, v) {
1654*d7d00a41SKrystof Tulinger    const base = url.substr(0, url.indexOf('?'));
1655*d7d00a41SKrystof Tulinger    const params = url.substr(base.length + 1).split("&").map(
1656*d7d00a41SKrystof Tulinger            function (x) {
1657*d7d00a41SKrystof Tulinger                return x.split("=");
1658*d7d00a41SKrystof Tulinger            });
1659*d7d00a41SKrystof Tulinger    let found = false;
1660*d7d00a41SKrystof Tulinger    for (let i in params) {
1661*d7d00a41SKrystof Tulinger        if (params[i][0] === p && params[i].length > 1) {
1662*d7d00a41SKrystof Tulinger            params[i][1] = encodeURIComponent(v);
1663*d7d00a41SKrystof Tulinger            found = true;
1664*d7d00a41SKrystof Tulinger        }
1665*d7d00a41SKrystof Tulinger    }
1666*d7d00a41SKrystof Tulinger    if (!found) {
1667*d7d00a41SKrystof Tulinger        params.push([p, encodeURIComponent(v)]);
1668*d7d00a41SKrystof Tulinger    }
1669*d7d00a41SKrystof Tulinger
1670*d7d00a41SKrystof Tulinger    return base + '?' + params.map(function (x) {
1671*d7d00a41SKrystof Tulinger        return x[0] + '=' + x[1];
1672*d7d00a41SKrystof Tulinger    }).join('&');
1673*d7d00a41SKrystof Tulinger}
1674*d7d00a41SKrystof Tulinger
1675*d7d00a41SKrystof Tulingerfunction domReadyMast() {
1676*d7d00a41SKrystof Tulinger    if (!window.location.hash) {
1677*d7d00a41SKrystof Tulinger        const h = getParameter("h");
1678*d7d00a41SKrystof Tulinger        if (h && h !== "") {
1679*d7d00a41SKrystof Tulinger            window.location.hash = h;
1680*d7d00a41SKrystof Tulinger        }
1681*d7d00a41SKrystof Tulinger    }
1682*d7d00a41SKrystof Tulinger    if (document.annotate) {
1683*d7d00a41SKrystof Tulinger        $('a.r').tooltip({
1684*d7d00a41SKrystof Tulinger            content: function () {
1685*d7d00a41SKrystof Tulinger                const element = $(this);
1686*d7d00a41SKrystof Tulinger                const title = element.attr("title") || "";
1687*d7d00a41SKrystof Tulinger                const parts = title.split(/<br\/>(?=[a-zA-Z0-9]+:)/g);
1688*d7d00a41SKrystof Tulinger                if (parts.length <= 0) {
1689*d7d00a41SKrystof Tulinger                    return "";
1690*d7d00a41SKrystof Tulinger                }
1691*d7d00a41SKrystof Tulinger                const $el = $("<dl>");
1692*d7d00a41SKrystof Tulinger                for (let i = 0; i < parts.length; i++) {
1693*d7d00a41SKrystof Tulinger                    const definitions = parts[i].split(":");
1694*d7d00a41SKrystof Tulinger                    if (definitions.length < 2) {
1695*d7d00a41SKrystof Tulinger                        continue;
1696*d7d00a41SKrystof Tulinger                    }
1697*d7d00a41SKrystof Tulinger                    $("<dt>").text(definitions.shift().trim()).appendTo($el);
1698*d7d00a41SKrystof Tulinger                    const $dd = $("<dd>");
1699*d7d00a41SKrystof Tulinger                    $.each(definitions.join(":").split("<br/>"), function (i, el) {
1700*d7d00a41SKrystof Tulinger                        $dd.append(escapeHtml(el.trim()));
1701*d7d00a41SKrystof Tulinger                        $dd.append($("<br/>"));
1702*d7d00a41SKrystof Tulinger                    });
1703*d7d00a41SKrystof Tulinger                    $dd.appendTo($el);
1704*d7d00a41SKrystof Tulinger                }
1705*d7d00a41SKrystof Tulinger                return $el;
1706*d7d00a41SKrystof Tulinger            }
1707*d7d00a41SKrystof Tulinger        });
1708*d7d00a41SKrystof Tulinger
1709*d7d00a41SKrystof Tulinger        $("#toggle-annotate-by-javascript").css('display', 'inline');
1710*d7d00a41SKrystof Tulinger        $("#toggle-annotate").hide();
1711*d7d00a41SKrystof Tulinger    }
1712*d7d00a41SKrystof Tulinger}
1713*d7d00a41SKrystof Tulinger
1714*d7d00a41SKrystof Tulingerfunction pageReadyMast() {
1715*d7d00a41SKrystof Tulinger}
1716*d7d00a41SKrystof Tulinger
1717*d7d00a41SKrystof Tulingerfunction domReadyMenu(minisearch) {
1718*d7d00a41SKrystof Tulinger    if (getSettingsValue('suggester-enabled') === 'false') {
1719*d7d00a41SKrystof Tulinger        return;
1720*d7d00a41SKrystof Tulinger    }
1721*d7d00a41SKrystof Tulinger
1722*d7d00a41SKrystof Tulinger    $.ajax({
1723*d7d00a41SKrystof Tulinger        // cannot use "/api/v1/configuration/suggester" because of security
1724*d7d00a41SKrystof Tulinger        url: window.contextPath + "/api/v1/suggest/config",
1725*d7d00a41SKrystof Tulinger        dataType: "json",
1726*d7d00a41SKrystof Tulinger        success: function(config) {
1727*d7d00a41SKrystof Tulinger            if (config.enabled) {
1728*d7d00a41SKrystof Tulinger                initAutocomplete(config, minisearch);
1729*d7d00a41SKrystof Tulinger            }
1730*d7d00a41SKrystof Tulinger        },
1731*d7d00a41SKrystof Tulinger        error: function(xhr, ajaxOptions, error) {
1732*d7d00a41SKrystof Tulinger            console.log('Could not get autocomplete configuration, probably disabled');
1733*d7d00a41SKrystof Tulinger        }
1734*d7d00a41SKrystof Tulinger    });
1735*d7d00a41SKrystof Tulinger}
1736*d7d00a41SKrystof Tulinger
1737*d7d00a41SKrystof Tulingerfunction initAutocomplete(config, minisearch) {
1738*d7d00a41SKrystof Tulinger    if (minisearch) {
1739*d7d00a41SKrystof Tulinger        initMinisearchAutocomplete(config);
1740*d7d00a41SKrystof Tulinger    } else {
1741*d7d00a41SKrystof Tulinger        initAutocompleteForField("full", "full", config);
1742*d7d00a41SKrystof Tulinger        initAutocompleteForField("defs", "defs", config);
1743*d7d00a41SKrystof Tulinger        initAutocompleteForField("refs", "refs", config);
1744*d7d00a41SKrystof Tulinger        initAutocompleteForField("path", "path", config);
1745*d7d00a41SKrystof Tulinger        initAutocompleteForField("hist", "hist", config);
1746*d7d00a41SKrystof Tulinger    }
1747*d7d00a41SKrystof Tulinger}
1748*d7d00a41SKrystof Tulinger
1749*d7d00a41SKrystof Tulingerfunction initMinisearchAutocomplete(config) {
1750*d7d00a41SKrystof Tulinger    if (config.allowedFields && config.allowedFields.indexOf('full') < 0) {
1751*d7d00a41SKrystof Tulinger        return;
1752*d7d00a41SKrystof Tulinger    }
1753*d7d00a41SKrystof Tulinger
1754*d7d00a41SKrystof Tulinger    let project = '';
1755*d7d00a41SKrystof Tulinger
1756*d7d00a41SKrystof Tulinger    var projectElem = $('#minisearch-project');
1757*d7d00a41SKrystof Tulinger    if (projectElem) {
1758*d7d00a41SKrystof Tulinger        project = projectElem.val();
1759*d7d00a41SKrystof Tulinger    }
1760*d7d00a41SKrystof Tulinger
1761*d7d00a41SKrystof Tulinger    const pathElem = $('#minisearch-path');
1762*d7d00a41SKrystof Tulinger
1763*d7d00a41SKrystof Tulinger    initAutocompleteForField('search', 'full', config, function (input, field) {
1764*d7d00a41SKrystof Tulinger        const caretPos = input.caret();
1765*d7d00a41SKrystof Tulinger        if (!(typeof caretPos === 'number')) {
1766*d7d00a41SKrystof Tulinger            console.error("Suggest: could not get caret position");
1767*d7d00a41SKrystof Tulinger            return;
1768*d7d00a41SKrystof Tulinger        }
1769*d7d00a41SKrystof Tulinger        return {
1770*d7d00a41SKrystof Tulinger            projects: [project],
1771*d7d00a41SKrystof Tulinger            field: field,
1772*d7d00a41SKrystof Tulinger            full: input.val(),
1773*d7d00a41SKrystof Tulinger            path: pathElem.is(':checked') ? pathElem.val() : '',
1774*d7d00a41SKrystof Tulinger            caret: caretPos
1775*d7d00a41SKrystof Tulinger        };
1776*d7d00a41SKrystof Tulinger    }, 'search');
1777*d7d00a41SKrystof Tulinger}
1778*d7d00a41SKrystof Tulinger
1779*d7d00a41SKrystof Tulingerfunction initAutocompleteForField(inputId, field, config, dataFunction, errorElemId) {
1780*d7d00a41SKrystof Tulinger    if (config.allowedFields && config.allowedFields.indexOf(field) < 0) {
1781*d7d00a41SKrystof Tulinger        return;
1782*d7d00a41SKrystof Tulinger    }
1783*d7d00a41SKrystof Tulinger
1784*d7d00a41SKrystof Tulinger    let text;
1785*d7d00a41SKrystof Tulinger    let identifier;
1786*d7d00a41SKrystof Tulinger    let time;
1787*d7d00a41SKrystof Tulinger    let partialResult;
1788*d7d00a41SKrystof Tulinger
1789*d7d00a41SKrystof Tulinger    const input = $("#" + inputId);
1790*d7d00a41SKrystof Tulinger
1791*d7d00a41SKrystof Tulinger    if (!dataFunction) {
1792*d7d00a41SKrystof Tulinger        dataFunction = getAutocompleteMenuData;
1793*d7d00a41SKrystof Tulinger    }
1794*d7d00a41SKrystof Tulinger    if (!errorElemId) {
1795*d7d00a41SKrystof Tulinger        errorElemId = 'full';
1796*d7d00a41SKrystof Tulinger    }
1797*d7d00a41SKrystof Tulinger    const errorElem = $('#' + errorElemId);
1798*d7d00a41SKrystof Tulinger
1799*d7d00a41SKrystof Tulinger    input.autocomplete({
1800*d7d00a41SKrystof Tulinger        source: function(request, response) {
1801*d7d00a41SKrystof Tulinger            $.ajax({
1802*d7d00a41SKrystof Tulinger                url: window.contextPath + "/api/v1/suggest",
1803*d7d00a41SKrystof Tulinger                dataType: "json",
1804*d7d00a41SKrystof Tulinger                data: dataFunction(input, field),
1805*d7d00a41SKrystof Tulinger                success: function(data) {
1806*d7d00a41SKrystof Tulinger                    hideError(errorElem);
1807*d7d00a41SKrystof Tulinger
1808*d7d00a41SKrystof Tulinger                    text = data.queryText;
1809*d7d00a41SKrystof Tulinger                    identifier = data.identifier;
1810*d7d00a41SKrystof Tulinger                    time = data.time;
1811*d7d00a41SKrystof Tulinger                    partialResult = data.partialResult;
1812*d7d00a41SKrystof Tulinger
1813*d7d00a41SKrystof Tulinger                    response(data.suggestions);
1814*d7d00a41SKrystof Tulinger                },
1815*d7d00a41SKrystof Tulinger                error: function(xhr, ajaxOptions, error) {
1816*d7d00a41SKrystof Tulinger                    input.autocomplete("close");
1817*d7d00a41SKrystof Tulinger                    response(undefined); // to remove loading indicator
1818*d7d00a41SKrystof Tulinger
1819*d7d00a41SKrystof Tulinger                    showError(xhr.responseJSON.message, errorElem);
1820*d7d00a41SKrystof Tulinger                },
1821*d7d00a41SKrystof Tulinger                statusCode: {
1822*d7d00a41SKrystof Tulinger                    404: function() {
1823*d7d00a41SKrystof Tulinger                        response(); // do not show anything
1824*d7d00a41SKrystof Tulinger                    }
1825*d7d00a41SKrystof Tulinger                }
1826*d7d00a41SKrystof Tulinger            });
1827*d7d00a41SKrystof Tulinger        },
1828*d7d00a41SKrystof Tulinger        create: function () {
1829*d7d00a41SKrystof Tulinger            $(this).data('ui-autocomplete')._renderItem = function (ul, item) {
1830*d7d00a41SKrystof Tulinger                const listItem = getSuggestionListItem(item, config);
1831*d7d00a41SKrystof Tulinger
1832*d7d00a41SKrystof Tulinger                return listItem.appendTo(ul);
1833*d7d00a41SKrystof Tulinger            };
1834*d7d00a41SKrystof Tulinger
1835*d7d00a41SKrystof Tulinger            $(this).data('ui-autocomplete')._renderMenu = function (ul, items) {
1836*d7d00a41SKrystof Tulinger                const _this = this;
1837*d7d00a41SKrystof Tulinger                $.each(items, function(index, item) {
1838*d7d00a41SKrystof Tulinger                    _this._renderItemData(ul, item);
1839*d7d00a41SKrystof Tulinger                });
1840*d7d00a41SKrystof Tulinger                if (config.showTime) {
1841*d7d00a41SKrystof Tulinger                    $("<li>", {
1842*d7d00a41SKrystof Tulinger                        "class": "ui-state-disabled",
1843*d7d00a41SKrystof Tulinger                        style: 'padding-left: 5px;',
1844*d7d00a41SKrystof Tulinger                        text: time + ' ms'
1845*d7d00a41SKrystof Tulinger                    }).appendTo(ul);
1846*d7d00a41SKrystof Tulinger                }
1847*d7d00a41SKrystof Tulinger                if (partialResult) {
1848*d7d00a41SKrystof Tulinger                    $("<li>", {
1849*d7d00a41SKrystof Tulinger                        "class": "ui-state-disabled",
1850*d7d00a41SKrystof Tulinger                        style: 'padding-left: 5px;',
1851*d7d00a41SKrystof Tulinger                        text: 'Partial result due to timeout'
1852*d7d00a41SKrystof Tulinger                    }).appendTo(ul);
1853*d7d00a41SKrystof Tulinger                }
1854*d7d00a41SKrystof Tulinger            };
1855*d7d00a41SKrystof Tulinger        },
1856*d7d00a41SKrystof Tulinger        focus: function (event, ui) {
1857*d7d00a41SKrystof Tulinger            if (ui.item.selectable === false) {
1858*d7d00a41SKrystof Tulinger                event.preventDefault();
1859*d7d00a41SKrystof Tulinger                return;
1860*d7d00a41SKrystof Tulinger            }
1861*d7d00a41SKrystof Tulinger            if (event.originalEvent.originalEvent.type.indexOf('key') === 0) { // replace value only on key events
1862*d7d00a41SKrystof Tulinger                replaceValueWithSuggestion(input, text, identifier, ui.item.phrase);
1863*d7d00a41SKrystof Tulinger            }
1864*d7d00a41SKrystof Tulinger
1865*d7d00a41SKrystof Tulinger            event.preventDefault(); // to prevent the movement of the caret to the end
1866*d7d00a41SKrystof Tulinger        },
1867*d7d00a41SKrystof Tulinger        select: function (event, ui) {
1868*d7d00a41SKrystof Tulinger            replaceValueWithSuggestion(input, text, identifier, ui.item.phrase);
1869*d7d00a41SKrystof Tulinger
1870*d7d00a41SKrystof Tulinger            event.preventDefault(); // to prevent the movement of the caret to the end
1871*d7d00a41SKrystof Tulinger        },
1872*d7d00a41SKrystof Tulinger        response: function (event, ui) {
1873*d7d00a41SKrystof Tulinger            if (!ui.content) {
1874*d7d00a41SKrystof Tulinger                // error occurred
1875*d7d00a41SKrystof Tulinger                return;
1876*d7d00a41SKrystof Tulinger            }
1877*d7d00a41SKrystof Tulinger            if (ui.content.length === 0 && !partialResult) {
1878*d7d00a41SKrystof Tulinger                const noMatchesFoundResult = {phrase: 'No matches found', selectable: false};
1879*d7d00a41SKrystof Tulinger                ui.content.push(noMatchesFoundResult);
1880*d7d00a41SKrystof Tulinger            }
1881*d7d00a41SKrystof Tulinger        },
1882*d7d00a41SKrystof Tulinger        minLength: config.minChars
1883*d7d00a41SKrystof Tulinger    }).click(function() {
1884*d7d00a41SKrystof Tulinger        $(this).autocomplete('search', $(this).val());
1885*d7d00a41SKrystof Tulinger    }).keyup(function(e) {
1886*d7d00a41SKrystof Tulinger        if (e.keyCode === 37 || e.keyCode === 39) { // left or right arrow key
1887*d7d00a41SKrystof Tulinger            $(this).autocomplete('search', $(this).val());
1888*d7d00a41SKrystof Tulinger        }
1889*d7d00a41SKrystof Tulinger        // try to refresh on empty input (error might go away) except when pressed esc key
1890*d7d00a41SKrystof Tulinger        if (input.val() === "" && e.keyCode !== 27) {
1891*d7d00a41SKrystof Tulinger            $(this).autocomplete('search', ' ');
1892*d7d00a41SKrystof Tulinger        }
1893*d7d00a41SKrystof Tulinger    });
1894*d7d00a41SKrystof Tulinger}
1895*d7d00a41SKrystof Tulinger
1896*d7d00a41SKrystof Tulingerfunction getAutocompleteMenuData(input, field) {
1897*d7d00a41SKrystof Tulinger    const caretPos = input.caret();
1898*d7d00a41SKrystof Tulinger    if (!Number.isInteger(caretPos)) {
1899*d7d00a41SKrystof Tulinger        console.error("Suggest: could not get caret position");
1900*d7d00a41SKrystof Tulinger        return;
1901*d7d00a41SKrystof Tulinger    }
1902*d7d00a41SKrystof Tulinger    return {
1903*d7d00a41SKrystof Tulinger        projects: getSelectedProjectNames(),
1904*d7d00a41SKrystof Tulinger        field: field,
1905*d7d00a41SKrystof Tulinger        full: $('#full').val(),
1906*d7d00a41SKrystof Tulinger        defs: $('#defs').val(),
1907*d7d00a41SKrystof Tulinger        refs: $('#refs').val(),
1908*d7d00a41SKrystof Tulinger        path: $('#path').val(),
1909*d7d00a41SKrystof Tulinger        hist: $('#hist').val(),
1910*d7d00a41SKrystof Tulinger        type: $('#type').val(),
1911*d7d00a41SKrystof Tulinger        caret: caretPos
1912*d7d00a41SKrystof Tulinger    };
1913*d7d00a41SKrystof Tulinger}
1914*d7d00a41SKrystof Tulinger
1915*d7d00a41SKrystof Tulingerfunction replaceValueWithSuggestion(input, queryText, identifier, suggestion) {
1916*d7d00a41SKrystof Tulinger    const pos = queryText.indexOf(identifier);
1917*d7d00a41SKrystof Tulinger    const phrase = escapeLuceneCharacters(suggestion);
1918*d7d00a41SKrystof Tulinger    input.val(queryText.replace(identifier, phrase));
1919*d7d00a41SKrystof Tulinger    input.caret(pos + phrase.length);
1920*d7d00a41SKrystof Tulinger}
1921*d7d00a41SKrystof Tulinger
1922*d7d00a41SKrystof Tulingerfunction showError(errorText, errorElem) {
1923*d7d00a41SKrystof Tulinger    const parent = errorElem.parent();
1924*d7d00a41SKrystof Tulinger
1925*d7d00a41SKrystof Tulinger    parent.css('position', 'relative');
1926*d7d00a41SKrystof Tulinger
1927*d7d00a41SKrystof Tulinger    let span = parent.find('#autocomplete-error')[0];
1928*d7d00a41SKrystof Tulinger    if (!span) {
1929*d7d00a41SKrystof Tulinger        span = $("<span>", {
1930*d7d00a41SKrystof Tulinger            "class": "note-error important-note important-note-rounded",
1931*d7d00a41SKrystof Tulinger            style: "right: -10px; position: absolute; top: 0px;",
1932*d7d00a41SKrystof Tulinger            text: "!",
1933*d7d00a41SKrystof Tulinger            id: 'autocomplete-error'
1934*d7d00a41SKrystof Tulinger        });
1935*d7d00a41SKrystof Tulinger
1936*d7d00a41SKrystof Tulinger        span.appendTo(parent);
1937*d7d00a41SKrystof Tulinger    } else {
1938*d7d00a41SKrystof Tulinger        span = $(span);
1939*d7d00a41SKrystof Tulinger        span.off("mouseenter mouseleave");
1940*d7d00a41SKrystof Tulinger    }
1941*d7d00a41SKrystof Tulinger
1942*d7d00a41SKrystof Tulinger    span.hover(function() { // mouse in
1943*d7d00a41SKrystof Tulinger        $.messagesWindow.empty();
1944*d7d00a41SKrystof Tulinger        $.messagesWindow.append(escapeHtml(errorText));
1945*d7d00a41SKrystof Tulinger        $.messagesWindow.show();
1946*d7d00a41SKrystof Tulinger    }, function() { // mouse out
1947*d7d00a41SKrystof Tulinger        $.messagesWindow.hide();
1948*d7d00a41SKrystof Tulinger    });
1949*d7d00a41SKrystof Tulinger}
1950*d7d00a41SKrystof Tulinger
1951*d7d00a41SKrystof Tulingerfunction hideError(errorElem) {
1952*d7d00a41SKrystof Tulinger    const parent = errorElem.parent();
1953*d7d00a41SKrystof Tulinger    const span = parent.find('#autocomplete-error')[0];
1954*d7d00a41SKrystof Tulinger    if (span) {
1955*d7d00a41SKrystof Tulinger        span.remove();
1956*d7d00a41SKrystof Tulinger    }
1957*d7d00a41SKrystof Tulinger}
1958*d7d00a41SKrystof Tulinger
1959*d7d00a41SKrystof Tulingerfunction getSuggestionListItem(itemData, config) {
1960*d7d00a41SKrystof Tulinger    if (itemData.selectable === false) {
1961*d7d00a41SKrystof Tulinger        return $("<li>", {
1962*d7d00a41SKrystof Tulinger            "class": "ui-state-disabled",
1963*d7d00a41SKrystof Tulinger            text: itemData.phrase
1964*d7d00a41SKrystof Tulinger        });
1965*d7d00a41SKrystof Tulinger    }
1966*d7d00a41SKrystof Tulinger
1967*d7d00a41SKrystof Tulinger    const listItem = $("<li>", {
1968*d7d00a41SKrystof Tulinger        "class": "ui-menu-item",
1969*d7d00a41SKrystof Tulinger        style: "display: block;"
1970*d7d00a41SKrystof Tulinger    });
1971*d7d00a41SKrystof Tulinger    const listItemChild = $("<div>", {
1972*d7d00a41SKrystof Tulinger        "class": "ui-menu-item-wrapper",
1973*d7d00a41SKrystof Tulinger        style: "height: 20px; padding: 0;",
1974*d7d00a41SKrystof Tulinger        tabindex: "-1"
1975*d7d00a41SKrystof Tulinger    });
1976*d7d00a41SKrystof Tulinger
1977*d7d00a41SKrystof Tulinger    listItemChild.appendTo(listItem);
1978*d7d00a41SKrystof Tulinger
1979*d7d00a41SKrystof Tulinger    $("<span>", {
1980*d7d00a41SKrystof Tulinger        text: itemData.phrase,
1981*d7d00a41SKrystof Tulinger        style: "float: left; padding-left: 5px; max-height: 20px;"
1982*d7d00a41SKrystof Tulinger    }).appendTo(listItemChild);
1983*d7d00a41SKrystof Tulinger
1984*d7d00a41SKrystof Tulinger    let projectInfoText = "";
1985*d7d00a41SKrystof Tulinger    if (config.showProjects) {
1986*d7d00a41SKrystof Tulinger        if (itemData.projects.length > 1) {
1987*d7d00a41SKrystof Tulinger            projectInfoText = 'Found in ' + itemData.projects.length + ' projects';
1988*d7d00a41SKrystof Tulinger        } else {
1989*d7d00a41SKrystof Tulinger            projectInfoText = itemData.projects[0];
1990*d7d00a41SKrystof Tulinger        }
1991*d7d00a41SKrystof Tulinger    }
1992*d7d00a41SKrystof Tulinger
1993*d7d00a41SKrystof Tulinger    let score = "";
1994*d7d00a41SKrystof Tulinger    if (config.showScores) {
1995*d7d00a41SKrystof Tulinger        score = ' (' + itemData.score + ')';
1996*d7d00a41SKrystof Tulinger    }
1997*d7d00a41SKrystof Tulinger    $("<span>", {
1998*d7d00a41SKrystof Tulinger        text: projectInfoText + score,
1999*d7d00a41SKrystof Tulinger        style: "float: right; color: #999999; font-style: italic; padding-right: 5px;"
2000*d7d00a41SKrystof Tulinger    }).appendTo(listItemChild);
2001*d7d00a41SKrystof Tulinger
2002*d7d00a41SKrystof Tulinger    return listItem;
2003*d7d00a41SKrystof Tulinger}
2004*d7d00a41SKrystof Tulinger
2005*d7d00a41SKrystof Tulingerfunction escapeLuceneCharacters(term) {
2006*d7d00a41SKrystof Tulinger    // must escape: + - && || ! ( ) { } [ ] ^ " ~ * ? : \
2007*d7d00a41SKrystof Tulinger    const pattern = /([\+\-\!\(\)\{\}\[\]\^\"\~\*\?\:\\]|&&|\|\|)/g;
2008*d7d00a41SKrystof Tulinger
2009*d7d00a41SKrystof Tulinger    return term.replace(pattern, "\\$1");
2010*d7d00a41SKrystof Tulinger}
2011*d7d00a41SKrystof Tulinger
2012*d7d00a41SKrystof Tulingerfunction domReadyHistory() {
2013*d7d00a41SKrystof Tulinger    // start state should ALWAYS be: first row: r1 hidden, r2 checked ;
2014*d7d00a41SKrystof Tulinger    // second row: r1 clicked, (r2 hidden)(optionally)
2015*d7d00a41SKrystof Tulinger    // I cannot say what will happen if they are not like that, togglediffs
2016*d7d00a41SKrystof Tulinger    // will go mad !
2017*d7d00a41SKrystof Tulinger    togglerevs();
2018*d7d00a41SKrystof Tulinger    toggleProjectInfo();
2019*d7d00a41SKrystof Tulinger}
2020*d7d00a41SKrystof Tulinger
2021*d7d00a41SKrystof Tulingerfunction get_annotations() {
2022*d7d00a41SKrystof Tulinger    let link = window.location.pathname + "?a=true";
2023*d7d00a41SKrystof Tulinger    if (document.rev && document.rev()) {
2024*d7d00a41SKrystof Tulinger        link += "&r=" + encodeURIComponent(document.rev());
2025*d7d00a41SKrystof Tulinger    }
2026*d7d00a41SKrystof Tulinger    if (window.location.hash) {
2027*d7d00a41SKrystof Tulinger        // If a line is highlighted when "annotate" is clicked, we want to
2028*d7d00a41SKrystof Tulinger        // preserve the highlighting, but we don't want the page to scroll
2029*d7d00a41SKrystof Tulinger        // to the highlighted line. So put the line number in a URL parameter
2030*d7d00a41SKrystof Tulinger        // instead of in the hash.
2031*d7d00a41SKrystof Tulinger        link += "&h=";
2032*d7d00a41SKrystof Tulinger        link += window.location.hash.substring(1, window.location.hash.length);
2033*d7d00a41SKrystof Tulinger    }
2034*d7d00a41SKrystof Tulinger    window.location = link;
2035*d7d00a41SKrystof Tulinger}
2036*d7d00a41SKrystof Tulinger
2037*d7d00a41SKrystof Tulingerfunction toggle_annotations() {
2038*d7d00a41SKrystof Tulinger    $(document.body).toggleClass("blame-hidden");
2039*d7d00a41SKrystof Tulinger}
2040*d7d00a41SKrystof Tulinger
2041*d7d00a41SKrystof Tulinger/** list.jsp */
2042*d7d00a41SKrystof Tulinger
2043*d7d00a41SKrystof Tulinger/**
2044*d7d00a41SKrystof Tulinger * Initialize defaults for list.jsp
2045*d7d00a41SKrystof Tulinger */
2046*d7d00a41SKrystof Tulingerfunction pageReadyList() {
2047*d7d00a41SKrystof Tulinger    document.highlight_count = 0;
2048*d7d00a41SKrystof Tulinger    $.navigateWindow.init();
2049*d7d00a41SKrystof Tulinger    if (typeof get_sym_list === 'function') {
2050*d7d00a41SKrystof Tulinger        $.navigateWindow.update(get_sym_list());
2051*d7d00a41SKrystof Tulinger    }
2052*d7d00a41SKrystof Tulinger    $('#navigate').click(function () {
2053*d7d00a41SKrystof Tulinger        $.navigateWindow.toggle();
2054*d7d00a41SKrystof Tulinger        return false;
2055*d7d00a41SKrystof Tulinger    });
2056*d7d00a41SKrystof Tulinger}
2057*d7d00a41SKrystof Tulinger
2058*d7d00a41SKrystof Tulinger/**
2059*d7d00a41SKrystof Tulinger * Toggle the display of line numbers.
2060*d7d00a41SKrystof Tulinger */
2061*d7d00a41SKrystof Tulingerfunction lntoggle() {
2062*d7d00a41SKrystof Tulinger    $(document.body).toggleClass("lines-hidden");
2063*d7d00a41SKrystof Tulinger    $('.fold-space, .fold-icon, .unfold-icon').toggle();
2064*d7d00a41SKrystof Tulinger}
2065*d7d00a41SKrystof Tulinger
2066*d7d00a41SKrystof Tulingerfunction lnshow() {
2067*d7d00a41SKrystof Tulinger    $(document.body).removeClass("lines-hidden");
2068*d7d00a41SKrystof Tulinger    $('.fold-space, .fold-icon, .unfold-icon').show();
2069*d7d00a41SKrystof Tulinger}
2070*d7d00a41SKrystof Tulinger
2071*d7d00a41SKrystof Tulinger/* ------ Highlighting ------ */
2072*d7d00a41SKrystof Tulinger
2073*d7d00a41SKrystof Tulinger/**
2074*d7d00a41SKrystof Tulinger *  Highlight keywords by changing the style of matching tags.
2075*d7d00a41SKrystof Tulinger */
2076*d7d00a41SKrystof Tulingerfunction highlightKeyword(keyword) {
2077*d7d00a41SKrystof Tulinger    const high_colors = [ "#ffff66", "#ffcccc", "#ccccff", "#99ff99", "#cc66ff" ];
2078*d7d00a41SKrystof Tulinger    const pattern = "a:contains('" + keyword + "')";
2079*d7d00a41SKrystof Tulinger    $(pattern).css({
2080*d7d00a41SKrystof Tulinger        'text-decoration' : 'underline',
2081*d7d00a41SKrystof Tulinger        'background-color' : high_colors[document.highlight_count % high_colors.length],
2082*d7d00a41SKrystof Tulinger        'font-weight' : 'bold'
2083*d7d00a41SKrystof Tulinger    });
2084*d7d00a41SKrystof Tulinger    document.highlight_count++;
2085*d7d00a41SKrystof Tulinger}
2086*d7d00a41SKrystof Tulinger//Test: HighlightKeyword('timeval');
2087*d7d00a41SKrystof Tulinger
2088*d7d00a41SKrystof Tulinger/**
2089*d7d00a41SKrystof Tulinger * Highlight the text given as value of the element with the ID "input_highlight" .
2090*d7d00a41SKrystof Tulinger * @see HighlightKeyword
2091*d7d00a41SKrystof Tulinger */
2092*d7d00a41SKrystof Tulingerfunction add_highlight() {
2093*d7d00a41SKrystof Tulinger    const tbox = document.getElementById('input_highlight');
2094*d7d00a41SKrystof Tulinger    highlightKeyword(tbox.value);
2095*d7d00a41SKrystof Tulinger}
2096*d7d00a41SKrystof Tulinger
2097*d7d00a41SKrystof Tulingerfunction toggle_filelist() {
2098*d7d00a41SKrystof Tulinger    const $a = $('div.filelist');
2099*d7d00a41SKrystof Tulinger    const $b = $('div.filelist-hidden');
2100*d7d00a41SKrystof Tulinger    $a.toggle().toggleClass('filelist').toggleClass('filelist-hidden');
2101*d7d00a41SKrystof Tulinger    $b.toggle().toggleClass('filelist').toggleClass('filelist-hidden');
2102*d7d00a41SKrystof Tulinger}
2103*d7d00a41SKrystof Tulinger
2104*d7d00a41SKrystof Tulingerfunction toggle_revtags() {
2105*d7d00a41SKrystof Tulinger    const $a = $('tr.revtags, span.revtags');
2106*d7d00a41SKrystof Tulinger    const $b = $('tr.revtags-hidden, span.revtags-hidden');
2107*d7d00a41SKrystof Tulinger    $a.toggle().toggleClass('revtags').toggleClass('revtags-hidden');
2108*d7d00a41SKrystof Tulinger    $b.toggle().toggleClass('revtags').toggleClass('revtags-hidden');
2109*d7d00a41SKrystof Tulinger}
2110*d7d00a41SKrystof Tulinger
2111*d7d00a41SKrystof Tulinger/**
2112*d7d00a41SKrystof Tulinger *  Function to toggle message length presentation
2113*d7d00a41SKrystof Tulinger */
2114*d7d00a41SKrystof Tulingerfunction toggleCommon(closestType) {
2115*d7d00a41SKrystof Tulinger  $(".rev-toggle-a").click(function() {
2116*d7d00a41SKrystof Tulinger    const toggleState = $(this).closest(closestType).attr("data-toggle-state");
2117*d7d00a41SKrystof Tulinger    const thisCell = $(this).closest("td");
2118*d7d00a41SKrystof Tulinger
2119*d7d00a41SKrystof Tulinger    if (toggleState === "less") {
2120*d7d00a41SKrystof Tulinger      $(this).closest(closestType).attr("data-toggle-state", "more");
2121*d7d00a41SKrystof Tulinger      thisCell.find(".rev-message-summary").addClass("rev-message-hidden");
2122*d7d00a41SKrystof Tulinger      thisCell.find(".rev-message-full").removeClass("rev-message-hidden");
2123*d7d00a41SKrystof Tulinger      $(this).html("... show less");
2124*d7d00a41SKrystof Tulinger    }
2125*d7d00a41SKrystof Tulinger    else if (toggleState === "more") {
2126*d7d00a41SKrystof Tulinger      $(this).closest(closestType).attr("data-toggle-state", "less");
2127*d7d00a41SKrystof Tulinger      thisCell.find(".rev-message-full").addClass("rev-message-hidden");
2128*d7d00a41SKrystof Tulinger      thisCell.find(".rev-message-summary").removeClass("rev-message-hidden");
2129*d7d00a41SKrystof Tulinger      $(this).html("show more ...");
2130*d7d00a41SKrystof Tulinger    }
2131*d7d00a41SKrystof Tulinger
2132*d7d00a41SKrystof Tulinger    return false;
2133*d7d00a41SKrystof Tulinger  });
2134*d7d00a41SKrystof Tulinger}
2135*d7d00a41SKrystof Tulinger
2136*d7d00a41SKrystof Tulinger/**
2137*d7d00a41SKrystof Tulinger *  Function to toggle revision message length for long revision messages
2138*d7d00a41SKrystof Tulinger */
2139*d7d00a41SKrystof Tulingerfunction togglerevs() {
2140*d7d00a41SKrystof Tulinger  $(".rev-toggle-a").click(toggleCommon("p"));
2141*d7d00a41SKrystof Tulinger}
2142*d7d00a41SKrystof Tulinger/**
2143*d7d00a41SKrystof Tulinger *  Function to toggle project info message length
2144*d7d00a41SKrystof Tulinger */
2145*d7d00a41SKrystof Tulingerfunction toggleProjectInfo() {
2146*d7d00a41SKrystof Tulinger  $(".rev-toggle-a").click(toggleCommon("span"));
2147*d7d00a41SKrystof Tulinger}
2148*d7d00a41SKrystof Tulinger
2149*d7d00a41SKrystof Tulingerfunction selectAllProjects() {
2150*d7d00a41SKrystof Tulinger    if ($("#project").data(SearchableOptionList.prototype.DATA_KEY)) {
2151*d7d00a41SKrystof Tulinger        $("#project").searchableOptionList().selectAll();
2152*d7d00a41SKrystof Tulinger    } else {
2153*d7d00a41SKrystof Tulinger        $("#project option").prop('selected', true);
2154*d7d00a41SKrystof Tulinger    }
2155*d7d00a41SKrystof Tulinger}
2156*d7d00a41SKrystof Tulinger
2157*d7d00a41SKrystof Tulingerfunction invertAllProjects() {
2158*d7d00a41SKrystof Tulinger    if ($("#project").data(SearchableOptionList.prototype.DATA_KEY)) {
2159*d7d00a41SKrystof Tulinger        $("#project").searchableOptionList().invert();
2160*d7d00a41SKrystof Tulinger    } else {
2161*d7d00a41SKrystof Tulinger        $("#project option").each(function () {
2162*d7d00a41SKrystof Tulinger            $(this).prop('selected', !$(this).prop('selected'));
2163*d7d00a41SKrystof Tulinger        });
2164*d7d00a41SKrystof Tulinger    }
2165*d7d00a41SKrystof Tulinger}
2166*d7d00a41SKrystof Tulinger
2167*d7d00a41SKrystof Tulingerfunction deselectAllProjects() {
2168*d7d00a41SKrystof Tulinger    if ($("#project").data(SearchableOptionList.prototype.DATA_KEY)) {
2169*d7d00a41SKrystof Tulinger        $("#project").searchableOptionList().deselectAll();
2170*d7d00a41SKrystof Tulinger    } else {
2171*d7d00a41SKrystof Tulinger        $("#project option").prop('selected', false);
2172*d7d00a41SKrystof Tulinger    }
2173*d7d00a41SKrystof Tulinger}
2174*d7d00a41SKrystof Tulinger
2175*d7d00a41SKrystof Tulingerfunction clearSearchFrom() {
2176*d7d00a41SKrystof Tulinger    $("#sbox input[type='text']").each(function () {
2177*d7d00a41SKrystof Tulinger        $(this).val("");
2178*d7d00a41SKrystof Tulinger    });
2179*d7d00a41SKrystof Tulinger    $("#type").searchableOptionList().selectRadio("");
2180*d7d00a41SKrystof Tulinger}
2181*d7d00a41SKrystof Tulinger
2182*d7d00a41SKrystof Tulingerfunction getSelectedProjectNames() {
2183*d7d00a41SKrystof Tulinger    try {
2184*d7d00a41SKrystof Tulinger        return $.map($("#project").searchableOptionList().getSelection().filter("[name='project']"), function (item) {
2185*d7d00a41SKrystof Tulinger            return $(item).attr("value");
2186*d7d00a41SKrystof Tulinger        });
2187*d7d00a41SKrystof Tulinger    } catch (e) { // happens when projects are not enabled
2188*d7d00a41SKrystof Tulinger        return [];
2189*d7d00a41SKrystof Tulinger    }
2190*d7d00a41SKrystof Tulinger}
2191*d7d00a41SKrystof Tulinger
2192*d7d00a41SKrystof Tulinger/**
2193*d7d00a41SKrystof Tulinger * Fold or unfold a function definition.
2194*d7d00a41SKrystof Tulinger */
2195*d7d00a41SKrystof Tulingerfunction fold(id) {
2196*d7d00a41SKrystof Tulinger    $('#' + id + '_fold_icon').
2197*d7d00a41SKrystof Tulinger            children().
2198*d7d00a41SKrystof Tulinger            first().
2199*d7d00a41SKrystof Tulinger            toggleClass('unfold-icon').
2200*d7d00a41SKrystof Tulinger            toggleClass('fold-icon');
2201*d7d00a41SKrystof Tulinger    $('#' + id + '_fold').toggle('fold');
2202*d7d00a41SKrystof Tulinger}
2203*d7d00a41SKrystof Tulinger
2204*d7d00a41SKrystof Tulingerlet scope_timeout = null;
2205*d7d00a41SKrystof Tulinger/**
2206*d7d00a41SKrystof Tulinger * Function that is called when the #content div element is scrolled. Checks
2207*d7d00a41SKrystof Tulinger * if the top of the page is inside a function scope. If so, update the
2208*d7d00a41SKrystof Tulinger * scope element to show the name of the function and a link to its definition.
2209*d7d00a41SKrystof Tulinger */
2210*d7d00a41SKrystof Tulingerfunction scope_on_scroll() {
2211*d7d00a41SKrystof Tulinger    if($.scopesWindow && $.scopesWindow.initialized && !$.scopesWindow.is(':visible')) {
2212*d7d00a41SKrystof Tulinger        return;
2213*d7d00a41SKrystof Tulinger    }
2214*d7d00a41SKrystof Tulinger    if (scope_timeout !== null) {
2215*d7d00a41SKrystof Tulinger        clearTimeout(scope_timeout);
2216*d7d00a41SKrystof Tulinger        scope_timeout = null;
2217*d7d00a41SKrystof Tulinger    }
2218*d7d00a41SKrystof Tulinger    scope_timeout = setTimeout(function () {
2219*d7d00a41SKrystof Tulinger        const y = $('#whole_header').outerHeight() + 2;
2220*d7d00a41SKrystof Tulinger        const c = document.elementFromPoint(15, y + 1);
2221*d7d00a41SKrystof Tulinger
2222*d7d00a41SKrystof Tulinger        if ($(c).is('.l, .hl')) {
2223*d7d00a41SKrystof Tulinger            const $par = $(c).closest('.scope-body, .scope-head');
2224*d7d00a41SKrystof Tulinger
2225*d7d00a41SKrystof Tulinger            if (!$par.length) {
2226*d7d00a41SKrystof Tulinger                return;
2227*d7d00a41SKrystof Tulinger            }
2228*d7d00a41SKrystof Tulinger
2229*d7d00a41SKrystof Tulinger            const $head = $par.hasClass('scope-body') ? $par.prev() : $par;
2230*d7d00a41SKrystof Tulinger            const $sig = $head.children().first();
2231*d7d00a41SKrystof Tulinger            if ($.scopesWindow.initialized) {
2232*d7d00a41SKrystof Tulinger                $.scopesWindow.update({
2233*d7d00a41SKrystof Tulinger                    'id': $head.attr('id'),
2234*d7d00a41SKrystof Tulinger                    'link': $sig.html()
2235*d7d00a41SKrystof Tulinger                });
2236*d7d00a41SKrystof Tulinger            }
2237*d7d00a41SKrystof Tulinger        }
2238*d7d00a41SKrystof Tulinger        scope_timeout = null;
2239*d7d00a41SKrystof Tulinger    }, 150);
2240*d7d00a41SKrystof Tulinger}
2241*d7d00a41SKrystof Tulinger
2242*d7d00a41SKrystof Tulinger/**
2243*d7d00a41SKrystof Tulinger * Determines whether current page is search page = list with queried documents.
2244*d7d00a41SKrystof Tulinger * @returns true if on search page, false otherwise
2245*d7d00a41SKrystof Tulinger */
2246*d7d00a41SKrystof Tulingerfunction isOnSearchPage() {
2247*d7d00a41SKrystof Tulinger    return $(document.documentElement).hasClass('search');
2248*d7d00a41SKrystof Tulinger}
2249*d7d00a41SKrystof Tulinger
2250*d7d00a41SKrystof Tulingerfunction checkIsOnSearchPage() {
2251*d7d00a41SKrystof Tulinger    if (isOnSearchPage()) {
2252*d7d00a41SKrystof Tulinger        $('#xrd').val("1"); // no redirect
2253*d7d00a41SKrystof Tulinger        return true;
2254*d7d00a41SKrystof Tulinger    }
2255*d7d00a41SKrystof Tulinger    return false;
2256*d7d00a41SKrystof Tulinger}
2257*d7d00a41SKrystof Tulinger
2258*d7d00a41SKrystof Tulinger/**
2259*d7d00a41SKrystof Tulinger * Preprocess the searched projects in the form with:
2260*d7d00a41SKrystof Tulinger *
2261*d7d00a41SKrystof Tulinger * <ol>
2262*d7d00a41SKrystof Tulinger *  <li>For all project search -> replace the projects with simple searchall parameter</li>
2263*d7d00a41SKrystof Tulinger *  <li>For group search -> replace all projects in a group by the group parameter</li>
2264*d7d00a41SKrystof Tulinger * </ol>
2265*d7d00a41SKrystof Tulinger * @param form the form containing the checkboxes
2266*d7d00a41SKrystof Tulinger */
2267*d7d00a41SKrystof Tulingerfunction preprocess_searched_projects(form) {
2268*d7d00a41SKrystof Tulinger    const sol = $('#project').searchableOptionList();
2269*d7d00a41SKrystof Tulinger
2270*d7d00a41SKrystof Tulinger    const $sel = sol.$selectionContainer;
2271*d7d00a41SKrystof Tulinger
2272*d7d00a41SKrystof Tulinger    /*
2273*d7d00a41SKrystof Tulinger     * For all project search check if all project checkbox are checked and then uncheck them (they
2274*d7d00a41SKrystof Tulinger     * would appear in the url) and add a hidden checkbox with searchall name.
2275*d7d00a41SKrystof Tulinger     */
2276*d7d00a41SKrystof Tulinger    const allProjectsSearch = $.makeArray($sel.find('.sol-checkbox[name=project]')).every(function (checkbox) {
2277*d7d00a41SKrystof Tulinger        return $(checkbox).is(':checked');
2278*d7d00a41SKrystof Tulinger    });
2279*d7d00a41SKrystof Tulinger
2280*d7d00a41SKrystof Tulinger    if (allProjectsSearch && $('#search_all_projects').length === 0) {
2281*d7d00a41SKrystof Tulinger        $sel.find('.sol-checkbox').prop('checked', false);
2282*d7d00a41SKrystof Tulinger        const $input = $('<input>');
2283*d7d00a41SKrystof Tulinger        const $all = $input.
2284*d7d00a41SKrystof Tulinger            attr({
2285*d7d00a41SKrystof Tulinger                id: 'search_all_projects',
2286*d7d00a41SKrystof Tulinger                type: 'checkbox',
2287*d7d00a41SKrystof Tulinger                value: true,
2288*d7d00a41SKrystof Tulinger                name: 'searchall'
2289*d7d00a41SKrystof Tulinger            }).
2290*d7d00a41SKrystof Tulinger            prop('checked', true).
2291*d7d00a41SKrystof Tulinger            css('display', 'none');
2292*d7d00a41SKrystof Tulinger        $all.appendTo($(form));
2293*d7d00a41SKrystof Tulinger        return;
2294*d7d00a41SKrystof Tulinger    }
2295*d7d00a41SKrystof Tulinger
2296*d7d00a41SKrystof Tulinger    /*
2297*d7d00a41SKrystof Tulinger     * For selecting groups instead of projects, ommit the "Other" group. Loop over
2298*d7d00a41SKrystof Tulinger     * all project checkbox in a group and when all of them are checked, uncheck them (they
2299*d7d00a41SKrystof Tulinger     * would appear in the URL) and then check the group checkbox.
2300*d7d00a41SKrystof Tulinger     */
2301*d7d00a41SKrystof Tulinger    $sel.find('.sol-optiongroup').each(function () {
2302*d7d00a41SKrystof Tulinger        const $el = $(this);
2303*d7d00a41SKrystof Tulinger
2304*d7d00a41SKrystof Tulinger        // handle "Other" group for ungrouped projects
2305*d7d00a41SKrystof Tulinger        if ($el.find('.sol-optiongroup-label').text() === 'Other') {
2306*d7d00a41SKrystof Tulinger            $el.find('.sol-checkbox[name=group]').prop('checked', false);
2307*d7d00a41SKrystof Tulinger            return;
2308*d7d00a41SKrystof Tulinger        }
2309*d7d00a41SKrystof Tulinger
2310*d7d00a41SKrystof Tulinger        const checkboxs = $el.find('.sol-option .sol-checkbox');
2311*d7d00a41SKrystof Tulinger        for (let i = 0; i < checkboxs.length; i++) {
2312*d7d00a41SKrystof Tulinger            const checkbox = $(checkboxs[i]);
2313*d7d00a41SKrystof Tulinger            if (!checkbox.is(":checked")) {
2314*d7d00a41SKrystof Tulinger                return;
2315*d7d00a41SKrystof Tulinger            }
2316*d7d00a41SKrystof Tulinger        }
2317*d7d00a41SKrystof Tulinger
2318*d7d00a41SKrystof Tulinger        $el.find('.sol-checkbox[name=group]').prop('checked', true);
2319*d7d00a41SKrystof Tulinger        for (let j = 0; j < checkboxs.length; j++) {
2320*d7d00a41SKrystof Tulinger            const cb = $(checkboxs[j]);
2321*d7d00a41SKrystof Tulinger            cb.prop('checked', false);
2322*d7d00a41SKrystof Tulinger        }
2323*d7d00a41SKrystof Tulinger    });
2324*d7d00a41SKrystof Tulinger}
2325*d7d00a41SKrystof Tulinger
2326*d7d00a41SKrystof Tulinger/**
2327*d7d00a41SKrystof Tulinger * Handles submit on search form.
2328*d7d00a41SKrystof Tulinger *
2329*d7d00a41SKrystof Tulinger * If submit was initiated by pressing the return key when focus was
2330*d7d00a41SKrystof Tulinger * in a text field then `si` attribute will be added to the form.
2331*d7d00a41SKrystof Tulinger *
2332*d7d00a41SKrystof Tulinger * @param {HTMLFormElement} form
2333*d7d00a41SKrystof Tulinger */
2334*d7d00a41SKrystof Tulingerfunction searchSubmit(form) {
2335*d7d00a41SKrystof Tulinger    let submitInitiator = '';
2336*d7d00a41SKrystof Tulinger    if (textInputHasFocus()) {
2337*d7d00a41SKrystof Tulinger        submitInitiator = document.activeElement.getAttribute('id');
2338*d7d00a41SKrystof Tulinger    }
2339*d7d00a41SKrystof Tulinger    if (submitInitiator) {
2340*d7d00a41SKrystof Tulinger        const input = document.createElement('INPUT');
2341*d7d00a41SKrystof Tulinger        input.setAttribute('name', 'si');
2342*d7d00a41SKrystof Tulinger        input.value = submitInitiator;
2343*d7d00a41SKrystof Tulinger        input.type = 'hidden';
2344*d7d00a41SKrystof Tulinger        form.appendChild(input);
2345*d7d00a41SKrystof Tulinger    }
2346*d7d00a41SKrystof Tulinger
2347*d7d00a41SKrystof Tulinger    // replace all projects search with searchall parameter
2348*d7d00a41SKrystof Tulinger    // select groups instead of projects if all projects in one group are selected
2349*d7d00a41SKrystof Tulinger    preprocess_searched_projects(form);
2350*d7d00a41SKrystof Tulinger}
2351*d7d00a41SKrystof Tulinger
2352*d7d00a41SKrystof Tulinger/**
2353*d7d00a41SKrystof Tulinger * Restores focus on page load
2354*d7d00a41SKrystof Tulinger *
2355*d7d00a41SKrystof Tulinger * @see #searchSubmit
2356*d7d00a41SKrystof Tulinger */
2357*d7d00a41SKrystof Tulingerfunction restoreFocusAfterSearchSubmit() {
2358*d7d00a41SKrystof Tulinger    const siParam = getParameter('si');
2359*d7d00a41SKrystof Tulinger    if (siParam) {
2360*d7d00a41SKrystof Tulinger        const $input = $('input[type=text][id="' + siParam + '"]');
2361*d7d00a41SKrystof Tulinger        if ($input.length === 1) {
2362*d7d00a41SKrystof Tulinger            $input[0].selectionStart = $input.val().length;
2363*d7d00a41SKrystof Tulinger            $input[0].selectionEnd = $input[0].selectionStart;
2364*d7d00a41SKrystof Tulinger            $input.focus();
2365*d7d00a41SKrystof Tulinger        }
2366*d7d00a41SKrystof Tulinger    }
2367*d7d00a41SKrystof Tulinger}
2368*d7d00a41SKrystof Tulinger
2369*d7d00a41SKrystof Tulinger/**
2370*d7d00a41SKrystof Tulinger * @return {boolean} true if focus is on a input[type=text] element
2371*d7d00a41SKrystof Tulinger */
2372*d7d00a41SKrystof Tulingerfunction textInputHasFocus() {
2373*d7d00a41SKrystof Tulinger    return !!document.activeElement &&
2374*d7d00a41SKrystof Tulinger        document.activeElement.nodeName === 'INPUT' &&
2375*d7d00a41SKrystof Tulinger        document.activeElement.type === 'text';
2376*d7d00a41SKrystof Tulinger}
2377*d7d00a41SKrystof Tulinger
2378*d7d00a41SKrystof Tulingerfunction escapeHtml(string) { // taken from https://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery
2379*d7d00a41SKrystof Tulinger    const htmlEscapeMap = {
2380*d7d00a41SKrystof Tulinger        '&': '&amp;',
2381*d7d00a41SKrystof Tulinger        '<': '&lt;',
2382*d7d00a41SKrystof Tulinger        '>': '&gt;',
2383*d7d00a41SKrystof Tulinger        '"': '&quot;',
2384*d7d00a41SKrystof Tulinger        "'": '&#39;',
2385*d7d00a41SKrystof Tulinger        '/': '&#x2F;',
2386*d7d00a41SKrystof Tulinger        '`': '&#x60;',
2387*d7d00a41SKrystof Tulinger        '=': '&#x3D;'
2388*d7d00a41SKrystof Tulinger    };
2389*d7d00a41SKrystof Tulinger    return String(string).replace(/[&<>"'`=\/]/g, function (s) {
2390*d7d00a41SKrystof Tulinger        return htmlEscapeMap[s];
2391*d7d00a41SKrystof Tulinger    });
2392*d7d00a41SKrystof Tulinger}
2393*d7d00a41SKrystof Tulinger
2394*d7d00a41SKrystof Tulinger/**
2395*d7d00a41SKrystof Tulinger * Returns user-specific local settings for a provided key.
2396*d7d00a41SKrystof Tulinger * @param key settings key
2397*d7d00a41SKrystof Tulinger * @returns {string} settings value or {@code undefined} if not set
2398*d7d00a41SKrystof Tulinger */
2399*d7d00a41SKrystof Tulingerfunction getSettingsValue(key) {
2400*d7d00a41SKrystof Tulinger    return localStorage.getItem(key);
2401*d7d00a41SKrystof Tulinger}
2402*d7d00a41SKrystof Tulinger
2403*d7d00a41SKrystof Tulinger/**
2404*d7d00a41SKrystof Tulinger * Event listener which stores user-specific settings change for an {@code input} element.
2405*d7d00a41SKrystof Tulinger * @param el settings {@code input} element
2406*d7d00a41SKrystof Tulinger */
2407*d7d00a41SKrystof Tulingerfunction onSettingsValueChange(el) {
2408*d7d00a41SKrystof Tulinger    let value = getSettingsInputValue(el);
2409*d7d00a41SKrystof Tulinger    localStorage.setItem(el.name, value);
2410*d7d00a41SKrystof Tulinger}
2411*d7d00a41SKrystof Tulinger
2412*d7d00a41SKrystof Tulinger/**
2413*d7d00a41SKrystof Tulinger * Decodes a settings value from a settings {@code input} element.
2414*d7d00a41SKrystof Tulinger * @param el settings {@code input} element
2415*d7d00a41SKrystof Tulinger * @returns {string|*} settings value
2416*d7d00a41SKrystof Tulinger */
2417*d7d00a41SKrystof Tulingerfunction getSettingsInputValue(el) {
2418*d7d00a41SKrystof Tulinger    if (el.type === 'checkbox') {
2419*d7d00a41SKrystof Tulinger        if (el.checked) {
2420*d7d00a41SKrystof Tulinger            return el.dataset.checkedValue;
2421*d7d00a41SKrystof Tulinger        } else {
2422*d7d00a41SKrystof Tulinger            return el.dataset.uncheckedValue;
2423*d7d00a41SKrystof Tulinger        }
2424*d7d00a41SKrystof Tulinger    } else {
2425*d7d00a41SKrystof Tulinger        return el.value;
2426*d7d00a41SKrystof Tulinger    }
2427*d7d00a41SKrystof Tulinger}
2428*d7d00a41SKrystof Tulinger
2429*d7d00a41SKrystof Tulinger/**
2430*d7d00a41SKrystof Tulinger * Resets all settings elements with class {@code local-setting} to their default values.
2431*d7d00a41SKrystof Tulinger */
2432*d7d00a41SKrystof Tulingerfunction resetAllSettings() {
2433*d7d00a41SKrystof Tulinger    for (const el of document.getElementsByClassName('local-setting')) {
2434*d7d00a41SKrystof Tulinger        setDefaultSettingsValue(el);
2435*d7d00a41SKrystof Tulinger        onSettingsValueChange(el);
2436*d7d00a41SKrystof Tulinger    }
2437*d7d00a41SKrystof Tulinger}
2438*d7d00a41SKrystof Tulinger
2439*d7d00a41SKrystof Tulinger/**
2440*d7d00a41SKrystof Tulinger * Sets a default value for a settings {@code input} element.
2441*d7d00a41SKrystof Tulinger * @param el settings {@code input} element
2442*d7d00a41SKrystof Tulinger */
2443*d7d00a41SKrystof Tulingerfunction setDefaultSettingsValue(el) {
2444*d7d00a41SKrystof Tulinger    setSettingsInputValue(el, el.dataset.defaultValue);
2445*d7d00a41SKrystof Tulinger}
2446*d7d00a41SKrystof Tulinger
2447*d7d00a41SKrystof Tulinger/**
2448*d7d00a41SKrystof Tulinger * Sets the settings {@code input} element value to the provided one.
2449*d7d00a41SKrystof Tulinger * @param el element to change
2450*d7d00a41SKrystof Tulinger * @param value value to set
2451*d7d00a41SKrystof Tulinger */
2452*d7d00a41SKrystof Tulingerfunction setSettingsInputValue(el, value) {
2453*d7d00a41SKrystof Tulinger    if (el.type === 'checkbox') {
2454*d7d00a41SKrystof Tulinger        el.checked = el.dataset.checkedValue === value;
2455*d7d00a41SKrystof Tulinger    } else {
2456*d7d00a41SKrystof Tulinger        el.value = value;
2457*d7d00a41SKrystof Tulinger    }
2458*d7d00a41SKrystof Tulinger}
2459*d7d00a41SKrystof Tulinger
2460*d7d00a41SKrystof Tulinger/**
2461*d7d00a41SKrystof Tulinger * Initializes all settings elements with {@code local-setting} class.
2462*d7d00a41SKrystof Tulinger */
2463*d7d00a41SKrystof Tulingerfunction initSettings() {
2464*d7d00a41SKrystof Tulinger    for (const el of document.getElementsByClassName('local-setting')) {
2465*d7d00a41SKrystof Tulinger        const value = getSettingsValue(el.name);
2466*d7d00a41SKrystof Tulinger        if (value) {
2467*d7d00a41SKrystof Tulinger            setSettingsInputValue(el, value);
2468*d7d00a41SKrystof Tulinger        } else {
2469*d7d00a41SKrystof Tulinger            setDefaultSettingsValue(el);
2470*d7d00a41SKrystof Tulinger        }
2471*d7d00a41SKrystof Tulinger    }
2472*d7d00a41SKrystof Tulinger}
2473