PageSourceSearch

https://gatesopenresearch.org/js/app/research.helpers.js

js gatesopenresearch.org collected 2026-09-24 17:16:22 UTC 56,879 bytes, 1,023 lines download raw bytes

1// *********************************************************************************
2// THE NEW CODE FOR SOME HELPER FUNCTIONS FOR F1000RESEARCH
3//
4// TO USE THE HELPERS IN A PAGE ADD THE FOLLOWING SCRIPT LINK
5//  <script src="/js/app/research.helpers.js" type="text/javascript"></script>
6//
7// YOU CAN THEN CALL THE HELPERS INITIALIZE FUNCTION WITH THE OPTIONS REQUIRED, IF ANY
8// OPTIONS AVAILABLE WHEN INITIALIZING ARE... (backToTop, debug)
9//  $(document).ready(function() {
10//      R.helpers.initialize({ "backToTop": true, "debug": true });
11//      // R.helpers.initialize(); <!-- INITIALIZING WITH NO OPTIONS -->
12//  });
13//
14// OR YOU CAN CALL THE PUBLIC FUNCTIONS INDIVIDUALLY
15//  $(document).ready(function() {
16//      R.helpers.addBackToTop();
17//      R.helpers.showModalWindow({
18//          "modal-title": "Title For Modal",
19//          "modal-content": "<span class='test'>CONTENT</span>",
20//          "modal-footer": "FOOTER",
21//          "add-close-button": true
22//      });
23//      R.helpers.setPageColour("#f7f6f5");
24//      R.helpers.checkForDuplicateID();
25//  });
26// *********************************************************************************
27
28
29/*
30==============================================================================
31SOME NOTES FOR JAMES
32--------------------
3301) Need to add an error handling function/option etc.
34        displayError: function(container, message) {
35            if (this.inDevelopmentMode) { console.log("running displayError(" + container + ", " + message + ") function"); }
36            message = message || "An unknown error has occurred.";
37            container.html(message);
38            container.fadeIn(400);
39            setTimeout(function() {
40                container.effect("drop", "easeInCubic", 1000);
41            }, 5000);
42        },
43==============================================================================
44*/
45
46/* jslint plusplus: true */
47/* global $, window, document, console, R */
48$(document).ready(function () {
49    /* jslint validthis: true */
50    "use strict";
51
52    R.helpers = new ResearchHelpers();
53
54    function ResearchHelpers() {
55        var publicProperties = {
56            description: "The F1000Research Helpers JS",
57            inDevelopmentMode: window.location.hostname === "XXX.f1000.com" ? true : false,
58            // localServerHostCode: function() {
59            //    THIS IS JUST AN EXAMPLE OF ASSIGNING A VARIABLE WITH A FUNCTION ETC.
60            //    for (var i=0,hn="hall.f1000.com",hc="";i<hn.length;i++) { hc+=hn.charCodeAt(i); }
61            //    return hc;
62            //},
63
64            initialize: initialize,
65            addBackToTop: addBackToTop,
66            setPageColour: setPageColour,
67            platformToolbar: platformToolbar,
68            stickyFooter: stickyFooter,
69            stickyNavigation: stickyNavigation,
70            bindFormFieldHandlers: bindFormFieldHandlers,
71            bindAllFormFields: bindAllFormFields,
72            addTextSearcher: addTextSearcher,
73            addSectionNumbersToTitles: addSectionNumbersToTitles,
74            showModalWindow: showModalWindow,
75            closeModalWindow: closeModalWindow,
76            checkForDuplicateID: checkForDuplicateID,
77            debounce: debounce,
78            hasExtension: hasExtension,
79            initialScrollDownToElement:initialScrollDownToElement
80        };
81
82        function initialize(options) {
83            options = options || "";
84            if (options["debug"] === true) { this.inDevelopmentMode = true; }
85            if (this.inDevelopmentMode) { console.log("running initialize() function"); }
86
87            var option = "",
88                optionValue = "";
89
90            // SET ITEMS FOR ALL PAGES
91            createHelperStyles();
92
93            appendBackgroundMaskMarkup();
94            appendModalWindowMarkup();
95            appendMoreInfoPopupMarkup();
96            //appendPageFaderMarkup();
97
98            if (!options["bindAllFormFields"]) {
99                bindFormFieldHandlers();
100                bindFormCheckboxHandlers();
101                bindFormToggleHandlers();
102                bindFormToggleRadioHandlers();
103            }
104            bindModalWindowHandlers();
105            bindMoreInfoWindowHandlers();
106            bindAccordionListHandlers();
107            bindMoreLessHandlers();
108            //bindPageFaderHandlers();
109
110            // LOOP THROUGH OPTION PARAMETER AND SET FUNCTIONS AS REQUIRED
111            if (options) {
112                for (option in options) {
113                    optionValue = options[option];
114                    switch (option) {
115                        case "backToTop" :
116                            this.addBackToTop();
117                            break;
118                        case "setPageColour" :
119                            this.setPageColour(optionValue === "" ? "" : optionValue);
120                            break;
121                        case "platformToolbar" :
122                            this.platformToolbar(optionValue);
123                            break;
124                        case "stickyFooter" :
125                            this.stickyFooter(optionValue);
126                            break;
127                        case "stickyNavigation" :
128                            this.stickyNavigation(optionValue);
129                            break;
130                        case "bindAllFormFields" :
131                            this.bindAllFormFields();
132                            break;
133                        case "addTextSearcher" :
134                            this.addTextSearcher(optionValue);
135                            break;
136                        case "addSectionNumbersToTitles" :
137                            this.addSectionNumbersToTitles();
138                            break;
139                        case "debug" :
140                            this.checkForDuplicateID();
141                            break;
142                        case "initialScrollDownToElement" :
143                                this.initialScrollDownToElement();
144                                break;
145                    }
146                }
147            }
148        }
149
150        function initialScrollDownToElement() {
151            var splitArr = window.location.href.split('#')
152            var selectorFromUrl = splitArr[1] ||'prp'
153            // var newUrl =  splitArr[0]
154              if ($("#" + selectorFromUrl).size()) {
155                  scrollToElement("#" + selectorFromUrl, 600, -140);
156            //   history.pushState({}, null, newUrl);
157           }
158        }
159        // ================================================================================
160        // THIS FUNCTION CREATES THE 'BACK TO TOP' ICON MARKUP AND HANDLERS
161        function addBackToTop() {
162            if (R.helpers.inDevelopmentMode) { console.log("running addBackToTop() function"); }
163            var that = this,
164                reqdHTML =  "<div id=\"back-to-top-wrapper\" class=\"back-to-top-wrapper\" style=\"display: none;\">" +
165                            "<div id=\"back-to-top-arrow\" class=\"up-arrow bigger back-to-top-arrow\"></div></div>";
166            if ($("#back-to-top-wrapper").size() === 0) { $("body").append(reqdHTML); }
167            bindBackToTopHandlers({ "display-position": 150, "display-time": 400, "scroll-time": 1500 });
168
169            function bindBackToTopHandlers(settings) {
170                if (R.helpers.inDevelopmentMode) { console.log("running bindBackToTopHandlers(" + settings + ") function"); }
171                var displayPosition = settings["display-position"] || 150,
172                    displayTime = settings["display-time"] || 400,
173                    scrollTime = settings["scroll-time"] || 1500;
174
175                if (R.helpers.inDevelopmentMode) { console.log("Current scrollTop() = " + $(this).scrollTop()); }
176                if (R.helpers.inDevelopmentMode) { console.log("displayPosition = " + displayPosition); }
177
178                $(window).scroll(function () {
179                    if ($(this).scrollTop() > displayPosition) {
180                        if ($(".sticky").filter(":visible").size() > 0) { $("#back-to-top-wrapper").css({ "bottom": $(".sticky").height() + 10 }); }
181                        if ($(".cookie-warning").filter(":visible").size() > 0) { $("#back-to-top-wrapper").css({ "bottom": $(".cookie-warning").height() + 10 }); }
182                        $(".back-to-top-wrapper").fadeIn(displayTime);
183                    } else {
184                        $(".back-to-top-wrapper").fadeOut(displayTime);
185                    }
186                });
187
188                $(".back-to-top-wrapper").on("click", function() {
189                    $("body,html").animate({ scrollTop: 0 }, { duration: scrollTime, easing: "easeInOutCubic" });
190                });
191            }
192        }
193        // END OF THE BACK TO TOP FUNCTION
194        // ================================================================================
195
196
197        // ================================================================================
198        // THIS FUNCTION SETS THE BACKGROUND COLOUR OF THE PAGE
199        function setPageColour(theColour) {
200            theColour = theColour || "#f7f6f5";
201            if (R.helpers.inDevelopmentMode) { console.log("running setPageColour() function"); }
202            $("body, .content-wrapper").css({ "background-color": theColour });
203        }
204        // END OF THE SET PAGE COLOUR FUNCTION
205        // ================================================================================
206
207
208        // ================================================================================
209        // THIS FUNCTION HANDLES THE DISPLAY OF THE PLATFORM TOOLBAR
210        function platformToolbar(website) {
211            var Toolbar = (function() {
212                var $toolbar = $('.f1r-platform-toolbar'),
213                    $info = $toolbar.find(".information-text"),
214                    hideToolbar = $toolbar.hasClass("is-hidden");
215                var updateWrapperMargin = R.helpers.debounce(function() {
216                    var toolbarHeight = $toolbar.height();
217                    //previewAssetWrapper.css('margin-top', toolbarHeight-20);
218                }, 250);
219                var updatePreviewBox = R.helpers.debounce(function () {
220                    var windowWidth = $(window).width(),
221                        scrollTop = $(window).scrollTop(),
222                        platform = $(".f1r-platform-toolbar select").size() ? $(".f1r-platform-toolbar select option:selected").text() : $.trim($(".f1r-platform-toolbar .form-field .h2-title").text());
223                    platform = platform.split(" ");
224                    platform = platform[0].toLowerCase();
225                    if (platform === "") { platform = website.toLowerCase(); }
226                    if (scrollTop > 56 && windowWidth <= 810) {
227                        $toolbar.css({
228                            width: "100%",
229                            "margin": "0 0 0 -20px",
230                            "padding-left": "20px",
231                            top: 56,
232                            position: "fixed",
233                            "z-index": 99
234                        }).addClass(platform);
235                        if (hideToolbar) { $toolbar.removeClass("is-hidden"); }
236                        $info.addClass("white");
237                    } else if (scrollTop > 120) {
238                        $toolbar.css({
239                            width: "100%",
240                            "margin": "0 0 0 -20px",
241                            "padding-left": "20px",
242                            top: 0,
243                            position: "fixed",
244                            "z-index": 99
245                        }).addClass(platform);
246                        if (hideToolbar) { $toolbar.removeClass("is-hidden"); }
247                        $info.addClass("white");
248                    } else {
249                       $toolbar.removeClass(platform).attr("style", "");
250                       if (hideToolbar) { $toolbar.addClass("is-hidden"); }
251                       $info.removeClass("white");
252                    }
253                }, 5);
254                return {
255                    updateWrapperMargin: updateWrapperMargin,
256                    updatePreviewBox: updatePreviewBox
257                };
258
259            }());
260            $(window).on("resize", Toolbar.updateWrapperMargin);
261            $(window).on("scroll", Toolbar.updatePreviewBox);
262            Toolbar.updatePreviewBox();
263            Toolbar.updateWrapperMargin();
264        }
265        // END OF THE PLATFORM TOOLBAR FUNCTION
266        // ================================================================================
267
268
269        // ================================================================================
270        // THIS FUNCTION FORCES THE FOOTER TO DISPLAY ON THE PAGE BOTTOM (WHEN NOT ENOUGH CONTENT)
271        function stickyFooter(page) {
272            if (R.helpers.inDevelopmentMode) { console.log("running stickyFooter() function"); }
273            page = page || "default";
274            var theWindowHeight = $(window).height(),
275                theBodyHeight = $("body").outerHeight(true),
276                footerID = "footer-wrapper",
277                theFooterHeight = 0,
278                headerID = "header-wrapper",
279                headHeight = 0,
280                titleID = "",
281                titleHeight = 0,
282                contentHeight = 0,
283                nonBodyHeight = 0,
284                isChannels = page === "channels" ? true : false;
285            if (isChannels) {
286                headerID = "channel-header-bar";
287                titleID = "channel-view-header";
288                titleHeight = $(".channel-view-header").outerHeight(true);
289            }
290            theFooterHeight = $("." + footerID).outerHeight(true);
291            headHeight = $("." + headerID).outerHeight(true);
292            if (theBodyHeight < getTheWindowHeight()) {
293                //$("." + footerID).css({ "position": "fixed", "width": "100%", "top": theWindowHeight - theFooterHeight + "px" });
294                //$("." + footerID).css({ "bottom": 0, "position": "absolute", "width": "100%" });
295                if (isChannels) {
296                    nonBodyHeight = headHeight + titleHeight + theFooterHeight;
297                    contentHeight = theWindowHeight - (nonBodyHeight + 35);
298                    $(".channel-view-list").css({ "min-height": contentHeight + "px" });
299                }
300            } else {
301                if (isChannels) { $("#page-spacer-container").css({ "height": "40px", "display": "block" }); }
302            }
303        //  Return the height of the current window
304            function getTheWindowHeight() {
305                var theHeight = 0;
306                if (typeof window.innerHeight === "number") {
307                    theHeight = window.innerHeight;
308                } else {
309                    if (document.documentElement && document.documentElement.clientHeight) {
310                        theHeight = document.documentElement.clientHeight;
311                    } else {
312                        if (document.body && document.body.clientHeight) {
313                            theHeight = document.body.clientHeight;
314                        }
315                    }
316                }
317                return theHeight;
318            }
319        }
320        // END OF THE STICKY FOOTER FUNCTION
321        // ================================================================================
322
323
324        // ================================================================================
325        // THIS FUNCTION STICKS THE LEFT 'NAVIGATION' MENU WHEN THE PAGE IS SCROLLED
326        function stickyNavigation() {
327            var fullBar = $('.js-sticky-nav'),
328                pageContent = $(".page-content"),
329                stickyHeader = $(".js-sticky-header__content"),
330                stickyHeaderHeight = stickyHeader.outerHeight(true),
331                navWidth = fullBar.outerWidth(true),
332                navHeight = fullBar.outerHeight(true),
333                windowHeight = $(window).height(),
334                topPadding = 25,
335                stickyContainer = $('.row.research-layout._mdl-layout'),
336                stickyTop = pageContent.offset().top + topPadding,
337                stickyBottom = stickyTop + pageContent.outerHeight(true) - navHeight,
338                isGateCollPage = location.pathname.match(/(gateways|collections)/) ? true : false,
339                myExpandingSectionEvent = new CustomEvent('sectionExpanded');
340                stickyContainer.css({ position: 'relative' });
341                //console.log('top, bottom', stickyTop, stickyBottom);
342
343            $('.mdl-expandingsection, .read-more-trigger').on('click', function(e) {
344                pageContent.trigger(myExpandingSectionEvent);
345            });
346
347            $(".read-more-trigger").on("click", function (e) {
348                setTimeout(function(){ pageContent.trigger(myExpandingSectionEvent); }, 50);
349            });
350
351            $(window)
352                .on("scroll", reposition)
353                .on("resize sectionExpanded", function (e) {
354                    navWidth = fullBar.outerWidth(true);
355                    reposition();
356                });
357
358            function reposition(e) {
359                var m = $(window).scrollTop();
360                stickyHeaderHeight = isGateCollPage ? stickyHeader.outerHeight(true) : 0;
361                stickyTop = pageContent.offset().top + topPadding - stickyHeaderHeight;
362                stickyBottom = stickyTop + pageContent.outerHeight(true) - navHeight;
363                // RESEARCH-8609: When the nav bar is longer then the content, trying to make it 'sticky' causes issues with scrolling
364                // In those circumstances there's no need to make the menu to be 'sticky'
365                if((m > stickyBottom) && (navHeight <= pageContent.outerHeight(true))) {
366                    fullBar.css({ position: "absolute", top: "", bottom: "0", width: navWidth + "px", "padding-top": topPadding + "px" });
367                    pageContent.css({ "margin-left": navWidth + "px" });
368                } else if ((m > stickyTop) && (navHeight <= pageContent.outerHeight(true))) {
369                    fullBar.css({ position: "fixed", top: stickyHeaderHeight, width: navWidth + "px", "padding-top": 0 });
370                    pageContent.css({ "margin-left": navWidth + "px" });
371                } else {
372                    fullBar.css({ position: "", top: "", width: "", "padding-top": topPadding + "px" });
373                    pageContent.css({ "margin-left": 0 });
374                }
375            }
376        }
377        // END OF THE STICKY NAVIGATION FUNCTION
378        // ================================================================================
379
380
381        // ================================================================================
382        // THIS FUNCTION BINDS THE ITEMS REQUIRED FOR A STANDARD FORM PAGE (INPUT FIELDS, CHECKBOX ETC.)
383        function bindAllFormFields() {
384            var that = this;
385            if (R.helpers.inDevelopmentMode) { console.log("running bindAllFormFields() function"); }
386            bindFormFieldHandlers();
387            bindFormCheckboxHandlers();
388            bindFormToggleHandlers();
389            bindFormToggleRadioHandlers();
390        }
391        // END OF THE BIND ALL FORM FIELDS FUNCTION
392        // ================================================================================
393
394
395        // ================================================================================
396        // THIS FUNCTION CREATES THE SIMPLE HTML/CSS3 TEXT SEARCHER
397        // Mandatory options are "text-field-ID" (The ID ref of the text field where the search is entered)
398        // Optional options are "error-field-ID" (The ID ref of the error item to display when nothing selected)
399        function addTextSearcher(searchOptions) {
400            if (R.helpers.inDevelopmentMode) { console.log("running addTextSearcher() function"); }
401            var searchField = searchOptions["text-field-ID"] || "search-box",
402                errorField = searchOptions["error-field-ID"] ? $("#" + searchOptions["error-field-ID"]) : "",
403                numberOfSearchableItems = $(".f1r-searchable").size();
404            if ($("#" + searchField).size() > 0) {
405                if ($("#f1r_search_style").size() < 1) { $("body").append("<style id='f1r_search_style'></style>"); }
406                var searchStyle = document.getElementById("f1r_search_style");
407                document.getElementById(searchField).addEventListener("input", function() {
408                    if (!this.value) {
409                        searchStyle.innerHTML = "";
410                        if (errorField) { errorField.hide(); }
411                    } else {
412                        searchStyle.innerHTML = ".f1r-searchable:not([data-index*=\"" + this.value.toLowerCase() + "\"]) { display: none !important; }";
413                        if ($(".f1r-searchable:not([data-index*=\"" + this.value.toLowerCase() + "\"])").size() >= numberOfSearchableItems && errorField) {
414                            errorField.fadeIn(200);
415                        } else {
416                            errorField.hide();
417                        }
418                    }
419                });
420            }
421        }
422        // END OF THE ADD TEXT SEARCHER FUNCTION
423        // ================================================================================
424
425
426        // ================================================================================
427        // THIS FUNCTION ADDS THE RELEVANT SECTION NUMBERS TO THE SECTION TITLE
428        function addSectionNumbersToTitles() {
429            if (R.helpers.inDevelopmentMode) { console.log("running addSectionNumbersToTitles() function"); }
430
431            $(".numbered-list-links").each(function(i, list) {
432                $(list).find("li a").each(function(j, item) {
433                    var sectionID = $(item).attr("href") || "",
434                        sectionNo = j + 1;
435                    if (sectionID !== "" && sectionID !== "undefined") {
436                        $(sectionID).html(sectionNo + ". " + $(sectionID).html());
437                    }
438                });
439            });
440        }
441        // END OF THE SECTION NUMBER UPDATE FUNCTION
442        // ================================================================================
443
444
445        // ================================================================================
446        // THIS FUNCTION OPENS THE MODAL WINDOW WITH THE SPECIFIED OPTIONS
447        function showModalWindow(options) {
448            if (R.helpers.inDevelopmentMode) { console.log("running showModalWindow(" + options + ") function"); }
449            var modalWindow = $("#rhelper-modal-window"),
450                modalWindowBackgroundMask = $("#rhelper-background-mask"),
451                modalAltClass = options["modal-class"] || "",
452                modalTitle = options["modal-title"] || "",
453                modalContent = options["modal-content"] || "",
454                modalFooter = options["modal-footer"] || "",
455                modalCloseButton = options["add-close-button"] || false;
456            if (R.helpers.inDevelopmentMode) { console.log(options, modalTitle, modalContent, modalFooter); }
457            if (modalWindow.size() > 0) {
458                if (modalCloseButton) {
459                    modalFooter = modalFooter + "<button id='rhelper-modal-close-button' class='rhelper-modal-close-button'>Close</button>";
460                }
461                if(modalAltClass) modalWindow.addClass(modalAltClass);
462                modalWindow.find(".rhelper-modal-title").html(modalTitle);
463                modalWindow.find(".rhelper-modal-content").html(modalContent);
464                modalWindow.find(".rhelper-modal-footer").html(modalFooter);
465            }
466            modalWindow.fadeIn(400);
467            modalWindowBackgroundMask.fadeIn(200);
468        }
469        // END OF THE MODAL WINDOW FUNCTIONS
470        // ================================================================================
471
472
473        // ================================================================================
474        // THIS FUNCTION CREATES THE MARKUP FOR THE POPUP WINDOWS BACKGROUND MASK
475        function appendBackgroundMaskMarkup() {
476            if (R.helpers.inDevelopmentMode) { console.log("running appendBackgroundMaskMarkup() function"); }
477            var maskID = "rhelper-background-mask",
478                messageID = "rhelper-background-message",
479                tickerID = "rhelper-bg-ticker",
480                maskHTML = "<div id='" + maskID + "' name='" + maskID + "' class='" + maskID + "'>" +
481                           "<div id='" + messageID + "' name='" + messageID +
482                           "' class='" + messageID + "'></div>" +
483                           "</div>" +
484                           "<div id='" + tickerID + "' name='" + tickerID +
485                           "' class='" + tickerID + "'></div>";
486            if ($("#" + maskID).size() === 0) { $("body").append(maskHTML); }
487        }
488        // END OF THE BACKGROUND MASK FUNCTION
489        // ================================================================================
490
491
492        // ================================================================================
493        // THIS FUNCTION CREATES THE MARKUP FOR THE POPUP WINDOWS
494        function appendModalWindowMarkup() {
495            if (R.helpers.inDevelopmentMode) { console.log("running appendModalWindowMarkup() function"); }
496            var modalID = "rhelper-modal-window",
497                modalHTML = "<div id=\"" + modalID + "\" name=\"" + modalID + "\" class=\"" + modalID + "\">" +
498                            "<div class=\"rhelper-modal-close-icon\"></div>" +
499                            "<div class=\"rhelper-modal-title\"></div>" +
500                            "<div class=\"rhelper-modal-content\"></div>" +
501                            "<div class=\"rhelper-modal-footer\"><button class=\"rhelper-modal-close-button\">Close</button></div>" +
502                            "</div>";
503            if ($("#" + modalID).size() === 0) { $("body").append(modalHTML); }
504        }
505        // END OF THE POPUPS MARKUP FUNCTION
506        // ================================================================================
507
508
509        // ================================================================================
510        // THIS FUNCTION CREATES THE MARKUP FOR THE MORE INFO ICONS
511        function appendMoreInfoPopupMarkup() {
512            if (R.helpers.inDevelopmentMode) { console.log("running appendMoreInfoPopupMarkup() function"); }
513            var popupID = "rhelper-more-info-popup",
514                popupHTML = '<div id="' + popupID + '" name="' + popupID + '" class="' + popupID + '">' +
515                                '<div class="rhelper-more-info research-layout">' +
516                                    '<div class="more-info-content"></div>' +
517                                '</div>' +
518                                '<div class="more-info-pointer"></div>' +
519                            '</div>';
520            if ($("#" + popupID).size() === 0) { $("body").append(popupHTML); }
521        }
522        // END OF THE MORE INFO POPUP MARKUP
523        // ================================================================================
524
525
526        // ================================================================================
527        // THIS FUNCTION CREATES THE MARKUP FOR THE POPUP WINDOWS
528        function appendPageFaderMarkup() {
529            if (R.helpers.inDevelopmentMode) { console.log("running appendPageFaderMarkup() function"); }
530            var modalID = "rhelper-page-fader",
531                modalHTML = "<div id=\"" + modalID + "\" name=\"" + modalID + "\" class=\"" + modalID + "\">" +
532                            "</div>";
533            if ($("#" + modalID).size() === 0) { $("body").append(modalHTML); }
534        }
535        // END OF THE POPUPS MARKUP FUNCTION
536        // ================================================================================
537
538
539        // ================================================================================
540        // THIS FUNCTION BINDS THE HANDLERS FOR INPUT FIELDS (ADDS CLASS ON ACTIVE ETC.)
541        function bindFormFieldHandlers() {
542            if (R.helpers.inDevelopmentMode) { console.log("running bindFormFieldHandlers() function"); }
543            $("body").on("focus", "input:not('#header-search-input'), textarea", function() {
544                if (!$(this).parent().hasClass("selected-option") && !$(this).closest("form.sticky-email__form")) {
545                    $(this).removeClass("form-field-error form-checkbox-error");
546                    $(this).addClass("form-field-active");
547                }
548            }).on("blur", "input, textarea", function() {
549                $(this).removeClass("form-field-active");
550            });
551            $(".new-select-standard-wrapper input").on("focus", function() {
552                $(this).closest(".new-select-standard-wrapper").removeClass("form-field-error");
553            });
554            $("body").on("focus", "input.form-field-error, textarea.form-field-error", function() {
555                $(this).removeClass('form-field-error');
556            });
557        }
558        // END OF THE BIND INPUT FIELDS FUNCTION
559        // ================================================================================
560
561
562        // ================================================================================
563        // THIS FUNCTION BINDS THE HANDLERS FOR THE MODAL WINDOWS
564        function bindModalWindowHandlers() {
565            if (R.helpers.inDevelopmentMode) { console.log("running bindModalWindowHandlers() function"); }
566            $(".rhelper-modal-close-button, .rhelper-modal-close-icon").on("click", function() {
567                closeModalWindow();
568            });
569            $("body").on("click", "#rhelper-modal-close-button", function(e) {
570                e.preventDefault();
571                closeModalWindow();
572            });
573            $("#rhelper-background-mask").click(function(e) {
574                e.preventDefault();
575                if ($("#rhelper-modal-window").has(e.target).length === 0) {
576                    closeModalWindow();
577                }
578            });
579        }
580
581        function closeModalWindow(callback) {
582            if (R.helpers.inDevelopmentMode) { console.log("running closeModalWindow() function"); }
583            $("#rhelper-modal-window").fadeOut(200);
584            $("#rhelper-background-mask").fadeOut(400);
585
586            if(callback) setTimeout(callback, 600);
587        }
588        // END OF THE MODAL WINDOW HANDLER BINDING FUNCTION
589        // ================================================================================
590
591
592        // ================================================================================
593        // THIS FUNCTION BINDS THE HANDLERS FOR THE MORE INFO POPUPS
594        // --------------------------------------------------------------------------------
595        // Expected markup for the more-info popup to work
596        //
597        //  <span class="icon-more-info"> <!-- You can always add new classes etc. -->
598        //      <div class="is-hidden more-info"> <!-- This DIV should contain the text for the popup -->
599        //          MORE INFO TEXT GOES HERE
600        //      </div>
601        //  </span>
602        // --------------------------------------------------------------------------------
603        // If you need a bigger 'popup containter' add class 'more-info-big' to the main <span>
604        // --------------------------------------------------------------------------------
605        function bindMoreInfoWindowHandlers() {
606            if (R.helpers.inDevelopmentMode) { console.log("running bindMoreInfoWindowHandlers() function"); }
607            $("body").on("click", ".icon-more-info", function(e) {
608                var textForPopup = $(this).find(".more-info").html(),
609                    infoPopupWrapper = $("#rhelper-more-info-popup"),
610                    popupText = infoPopupWrapper.find(".more-info-content"),
611                    popupPointer = infoPopupWrapper.find(".more-info-pointer"),
612                    popupBigger = $(this).hasClass("more-info-big") ? true : false,
613                    pointerMargin = 90,
614                    $parentContainer = $("#highlight-area"),
615                    parentLeft = parseInt(($parentContainer.position().left + 5), 10),
616                    parentRight = parseInt(parentLeft + ($parentContainer.width() - 10), 10),
617                    leftPosition = popupBigger ? $(this).offset().left - 487 : $(this).offset().left - 87,
618                    rightPosition = popupBigger ? $(this).offset().left + 600 : $(this).offset().left + 200,
619                    topPosition = $(this).offset().top - 2; // The "- 2" adds a margin of 2 between icon & pointer
620                if (popupBigger) {
621                    infoPopupWrapper.find(".rhelper-more-info").css({ "width": "560px" });
622                } else {
623                    infoPopupWrapper.find(".rhelper-more-info").css({ "width": "160px" });
624                }
625                // Check if the popup stays in the boundaries of the parent container
626                if (rightPosition > parentRight) {
627                    leftPosition = popupBigger ? parentRight - 575 : parentRight - 175;
628                    pointerMargin = (e.pageX - (leftPosition + 10));
629                } else if (leftPosition < parentLeft) {
630                    leftPosition = parentLeft + 20;
631                    pointerMargin = (e.pageX - (leftPosition + 10));
632                }
633                // Populate the popup text
634                popupText.html(textForPopup);
635                // Set the pointer position
636                popupPointer.css({ "margin-left": pointerMargin + "px" });
637                // Add some margin when on mobile in the browse view
638                if ($("#the-responsive-header").is(":visible") && $(".subject-browse .wellcome.article-listing").size()) {
639                    $("#rhelper-more-info-popup .rhelper-more-info").css({ "margin-left": "10px" });
640                }
641                // Annoying hack so we can get the height of the hidden item
642                infoPopupWrapper.css({ "visibility": "hidden", "display": "block"});
643                topPosition = topPosition - infoPopupWrapper.height();
644                infoPopupWrapper.css({ "top": topPosition + "px", "left"
644: leftPosition + "px", "visibility": "", "display": "none" });
645                //infoPopupWrapper.fadeIn(100);
646                infoPopupWrapper.show();
647            });
648            $(document).on("mouseup", function(e) {
649                var helpContainer = $(".rhelper-more-info-popup");
650                if (!helpContainer.is(e.target) && helpContainer.has(e.target).length === 0) {
651                    //helpContainer.fadeOut(200);
652                    helpContainer.hide();
653                }
654            });
655        }
656        // END OF THE MORE INFO POPUP BINDING FUNCTION
657        // ================================================================================
658
659
660        // ================================================================================
661        // THIS FUNCTION BINDS THE HANDLER FOR CHECKBOX GROUPS (CLASS IS .form-checkbox-group)
662        // --------------------------------------------------------------------------------
663        // Expected markup for the checkboxes to work
664        //
665        //  <div class="form-input-wrapper full-width form-checkbox-group" data-checkbox-group="SOME_NAME">
666        //      Checkbox markup (likely to be MDL)
667        //  </div>
668        // --------------------------------------------------------------------------------
669        function bindFormCheckboxHandlers() {
670            if (R.helpers.inDevelopmentMode) { console.log("running bindFormCheckboxHandlers() function"); }
671
672            $("body").on("change", ".mdl-checkbox__input", function(e) {
673                if (!this.checked) return;
674
675                const thisCheckboxGroup = this.closest('.form-checkbox-group')
676                if (!thisCheckboxGroup) return;
677
678                const checkboxGroupName = thisCheckboxGroup.dataset.checkboxGroup,
679                    allCheckboxGroup = document.querySelectorAll(`[data-checkbox-group="${checkboxGroupName}"]`);
680
681                for (const checkboxGroup of allCheckboxGroup) {
682                    if (checkboxGroup !== thisCheckboxGroup) {
683                        F1000.MdlHelper.prototype.checkboxUncheck(checkboxGroup.querySelector('input'));
684                    }
685                }
686            });
687        }
688        // END OF THE BIND CHECKBOX GROUPS FUNCTION
689        // ================================================================================
690
691
692        // ================================================================================
693        // BIND THE HANDLERS FOR THE YES/NO TOGGLE BUTTON (CLASS IS .form-toggle-button)
694        // --------------------------------------------------------------------------------
695        // Expected markup for the toggle to work
696        //
697        //  <div class="form-toggle-button" data-associated-container="has_priority_area">
698        //      <input type="radio" class="is-hidden yes-button" value="true" />
699        //      <input type="radio" class="is-hidden no-button" value="false" />
700        //  </div>
701        //
702        // Notes: The 'data-associated-container' attribute allows you to specify the ID of
703        //        an item to show/hide when the toggle button is pressed. This is optional.
704        //        The radio buttons are also optional (if you just need the toggle)
705        // --------------------------------------------------------------------------------
706        function bindFormToggleHandlers() {
707            if (R.helpers.inDevelopmentMode) { console.log("running bindFormToggleHandlers() function"); }
708            $(".form-toggle-button").on("change", function() {
709                const assocContainerID = $(this).attr("data-associated-container");
710                if (assocContainerID) {
711                    $(`#${assocContainerID}`).slideToggle(250);
712                }
713            });
714        }
715
716
717        //similar to above but working on radio buttons
718        function bindFormToggleRadioHandlers(){
719            $(".js-show-form-section, .js-hide-form-section").on("click", function() {
720                var sectionID = $(this).attr("data-associated-container"),
721                    section = $("#" + sectionID),
722                    toggleSection = ($(this).hasClass("js-show-form-section") && !section.is(":visible")) || ($(this).hasClass("js-hide-form-section") && section.is(":visible")) ? true : false;
723                if (toggleSection) { section.slideToggle(600); }
724            });
725
726
727        }
728
729
730        // END OF THE BIND YES/NO TOGGLE HANDLERS FUNCTION
731        // ================================================================================
732
733
734        // ================================================================================
735        // THIS FUNCTION BINDS THE HANDLERS FOR THE 'ACCORDION' LISTS
736        // --------------------------------------------------------------------------------
737        // Expected markup for an accordion list (Requires parent class of .research-layout)
738        //
739        // NOTE: The optional data-accordion-type attribute is to specify how the exp
739anding
740        //       and collapsing of the list items works, as follows:
741        //       accordion  This will close any existing open sections when expanding an item
742        //       multiple   This is the default. Allows multiple sections to be open. You can
743        //                  just omit adding the data-accordion-type attribute all together.
744        //
745        //  <div class="accordion-list" [OPTIONAL: data-accordion-type="accordion || multiple"]>
746        //      <div class="section-box">
747        //          <ul class="section-detail">
748        //              <li class="heading5 section-item">
749        //                  <span class="open-row"><a href="#" class="title-link"> -TITLE HERE- </a></span>
750        //                  <span class="open-section f1r-icon icon-20_plus_big" title="Open"></span>
751        //                  <span class="close-section f1r-icon icon-12_minus_big" title="Close"></span>
752        //                  <div class="section-content heading10 padding-bottom"> -CONTENT TEXT HERE- </div>
753        //              </li>
754        //          </ul>
755        //      </div>
756        //  </div>
757        // --------------------------------------------------------------------------------
758        function bindAccordionListHandlers() {
759            if (R.helpers.inDevelopmentMode) { console.log("running bindAccordionListHandlers() function"); }
760            $(".accordion-list").on("click", ".section-detail .accordion-control", function(e) {
761                e.preventDefault();
762                var openSections = $(".content-shown"),
763                    $listItem = $(this).closest("li"),
764                    accordionList = $(this).closest(".accordion-list").attr("data-accordion-type") === "accordion" ? true : false,
765                    options = {
766                        "action": "hide",
767                        "content": $listItem.find(".section-content"),
768                        "plus": $listItem.find(".open-section"),
769                        "minus": $listItem.find(".close-section"),
770                        "fadeDuration": 200,
771                        "slideDuration": 400
772                    },
773                    accordionController = {
774                        update: function(options) {
775                            var action = options["action"] || "show",
776                                content = options["content"] || "",
777                                plus = options["plus"] || "",
778                                minus = options["minus"] || "",
779                                fadeDuration = options["fadeDuration"] || 200,
780                                slideDuration = options["slideDuration"] || (fadeDuration * 2),
781                                effect = options["effect"] || "easeOutCirc";
782                            content.slideToggle(slideDuration, effect);
783                            if (action === "show") {
784                                this.switchIcon(plus, minus, fadeDuration);
785                            } else {
786                                this.switchIcon(minus, plus, fadeDuration);
787                            }
788                        },
789                        switchIcon: function(itemToHide, itemToShow, fadeDuration) {
790                            itemToHide.fadeOut(fadeDuration, function() { itemToShow.fadeIn(fadeDuration); });
791                        }
792                    };
793                if ($listItem.hasClass("content-shown")) {
794                    $listItem.removeClass("content-shown");
795                    accordionController.update(options);
796                } else {
797                    if (accordionList && openSections.size() > 0) {
798                        openSections.each(function (index, element) {
799                            $(this).removeClass("content-shown");
800                            accordionController.update({
801                                "action": "hide",
802                                "content": $(this).find(".section-content"),
803                                "plus": $(this).find(".open-section"),
804                                "minus": $(this).find(".close-section"),
805                                "fadeDuration": 100,
806                                "slideDuration": 200,
807                                "effect": "swing"
808                            });
809                        });
810                    }
811                    $listItem.addClass("content-shown");
812                    options["action"] = "show";
813                    accordionController.update(options);
814                }
815            });
816        }
817        // END OF THE ACCORDION LIST HANDLERS
818        // ================================================================================
819
820
821        // ================================================================================
822        // THIS FUNCTION BINDS THE HANDLERS FOR THE MORE/LESS SECTION EXPAND
823        // --------------------------------------------------------------------------------
824        // Expected markup for the more/less to work
825        //
826        //  <div class="example-detail">
827        //      <div class="collapsed-section [CAN INCLUDE OTHER CLASSES HERE]">
828        //          == The text for the collapsed section goes here ==
829        //          <a href="#" title="More" class="more-less-link more [MORE CLASSES HERE]">
830        //              Show More <span class="f1r-icon icon-27_sub_marker link-arrow-tiny orange"></span>
831        //          </a>
832        //      </div>
833        //      <div class="expanded-section [CAN INCLUDE OTHER CLASSES HERE]">
834        //          == The text for the expanded section goes here ==
835        //          <a href="#" title="Less" class="more-less-link less [MORE CLASSES HERE]">
836        //              Show Less <span class="f1r-icon icon-77_sub_marker_up link-arrow-tiny orange"></span>
837        //          </a>
838        //      </div>
839        //  </div>
840        // --------------------------------------------------------------------------------
841        function bindMoreLessHandlers() {
842          if (R.helpers.inDevelopmentMode) {  }
843          $(".more-less-link").on("click", function(e) {
844            e.preventDefault();
845            var theContainer = $(this).closest(".example-detail"),
846                fullSection = theContainer.find(".expanded-section"),
847                partSection = theContainer.find(".collapsed-section"),
848                myExpandingSectionEvent = new CustomEvent('sectionExpanded'),
849                pageContent = $(".page-content");
850            if ($(this).hasClass("more") || $(this).hasClass("more-text")) {
851              partSection.hide(1, function() {
852                fullSection.fadeIn(200, resizeStickyNav);
853              });
854            } else {
855              fullSection.hide(1, function(pageContent) {
856                partSection.fadeIn(200, resizeStickyNav);
857              });
858            }
859
860            function resizeStickyNav() {
861              pageContent.trigger(myExpandingSectionEvent);
862            }
863          });
864        }
865        // END OF THE BIND MORE/LESS SECTION EXPAND FUNCTION
866        // ================================================================================
867
868
869        // ================================================================================
870        // THIS FUNCTION BINDS THE HANDLERS FOR THE PAGE FADE EFFECT WHEN CHANGING URLS
871        function bindPageFaderHandlers() {
872            if (R.helpers.inDevelopmentMode) { console.log("running bindPageFaderHandlers() function"); }
873            $(".fade-link, .fade-link-med, .fade-link-slow").on("click", function(e) {
874                e.preventDefault();
875                var theURL = $(this).attr("href"),
876                    fadeTime = 100;
877                if ($(this).hasClass("fade-link-med")) {
878                    fadeTime = 500;
879                } else if ($(this).hasClass("fade-link-slow")) {
880                    fadeTime = 2000;
881                }
882                //theURL = theURL + "?speed=" + fadeTime;
883                $(".rhelper-page-fader").fadeTo(fadeTime, 1, function() {
884                    window.location.replace(theURL);
885                });
886            });
887            $(".rhelper-page-fader").fadeTo(100, 0, function() { $(this).hide(); });
888        }
889        // END OF THE PAGE FADER HANDLERS
890        // ================================================================================
891
892
893        // ================================================================================
894        // THIS FUNCTION CHECKS THE PAGE FOR ITEMS WITH A DUPLICATE ID REF
895        function checkForDuplicateID() {
896            if (R.helpers.inDevelopmentMode) { console.log("running checkForDuplicateID() function"); }
897            $("[id]").each(function() {
898                var ids = $("[id='" + this.id + "']");
899                if (ids.length > 1 && ids[0] == this) {
900                    console.warn("Multiple IDs on item #" + this.id);
901                }
902            });
903        }
904        // END OF THE CHECK FOR DUPLICATE ID'S
905        // ================================================================================
906
907
908        // ================================================================================
909        // THIS FUNCTION CREATES THE STYLES REQUIRED FOR THE HELPER FUNCTIONS
910        function createHelperStyles() {
911            if (R.helpers.inDevelopmentMode) { console.log("running createHelperStyles() function"); }
912            var helperStyles = $("<style type=\"text/css\">").appendTo("head"),
913                bgmaskCSS = ".rhelper-background-mask { " +
914                                "position: fixed; top: 0; left: 0; display: none; z-index: 1000; " +
915                                "width: 100%; height: 100%; background-color: #000; " +
916                                "-ms-filter: \"progid:DXImageTransform.Microsoft.Alpha(Opacity=40)\"; " +
917                                "filter: alpha(opacity=40); -moz-opacity: 0.4; opacity: 0.4; " +
918                            "}",
919                bgMessageCSS = ".rhelper-background-message { " +
920                                    "font: normal normal normal 30px/30px 'ProximaBold', Arial, sans-serif; " +
921                                    "width: 500px; margin: 0 auto; padding-top: 20%; color: white; letter-spacing: 0.05em; " +
922                                    "text-align: center; position: relative; z-index: 1001;" +
923                                "}",
924                bgTickerCSS = ".rhelper-bg-ticker { " +
925                                    "display: none; position: fixed; z-index: 1001; " +
926                                    "top: 50%; left: 50%; margin: -10px 0 0 -8px;" +
927                                    "width: 16px; height: 11px; " +
928                                    "background: transparent url(\"/img/ticker.gif\") no-repeat center center;" +
929                                "}",
930                modalWindowCSS = ".rhelper-modal-window { " +
931                                    "display: none; position: absolute; z-index: 1001; " +
932                                    "font: normal normal normal 14px/18px Verdana,Arial,sans-serif; " +
933                                    "background-color: #e2e2e2; width: 400px; height: 300px; " +
934                                    "left: 50%; top: 50%; margin-left: -220px; margin-top: -150px; " +
935                                    "text-align: left; padding: 20px; " +
936                                    "border: 1px solid #ccc; border-radius: 6px; " +
937                                    "-moz-box-shadow: 0 6px 25px #808080; " +
938                                    "-webkit-box-shadow: 0 6px 25px #808080; " +
939                                    "box-shadow: 0 6px 25px #808080; " +
940                                 "}",
941                modalCloseIconCSS = ".rhelper-modal-close-icon { " +
942                                        "background: url(\"/img/ui/close-icon.png\") no-repeat scroll 0 0 transparent; " +
943                                        "position: absolute; height: 20px;
943 width: 20px; right: -10px; top: -10px; " +
944                                    "}" +
945                                    ".rhelper-modal-close-icon:hover { cursor: pointer; }",
946                modalTitleCSS = ".rhelper-modal-title { font-weight: bold; font-size: 20px; line-height: 24px; }",
947                modalContentCSS = ".rhelper-modal-content { height: 200px; padding: 10px 0; }",
948                modalCloseCSS = ".rhelper-modal-close-button { " +
949                                    "text-transform: uppercase; padding: 4px 8px; " +
950                                    "border: 1px solid #e96527; border-radius: 4px; " +
951                                    "background-color: #fff; " +
952                                "}",
953                modalOverCloseCSS = ".rhelper-modal-close-button:hover { " +
954                                        "color: #e96527; " +
955                                        "-moz-box-shadow: 0 2px 6px #808080; " +
956                                        "-webkit-box-shadow: 0 2px 6px #808080; " +
957                                        "box-shadow: 0 2px 6px #808080; " +
958                                    "}",
959                moreInfoPopupCSS = ".rhelper-more-info-popup { display: none; position: absolute; }",
960                moreInfoCSS = ".rhelper-more-info-popup .rhelper-more-info { " +
961                                    "background-color: white; border: 1px solid #ccc;" +
962                                    "-moz-border-radius: 6px; -webkit-border-radius: 6px; border-radius: 6px;" +
963                                    "-moz-box-shadow: 0 0 10px 2px #c3c3c3;" +
964                                    "-webkit-box-shadow: 0 0 10px 2px #c3c3c3;" +
965                                    "box-shadow: 0 2px 10px 2px #c3c3c3;" +
966                                    "padding: 20px; width: 160px;" +
967                                    "position: relative;" +
968                                    "z-index: 9;" +
969                                "}",
970                moreInfoPointerCSS = ".rhelper-more-info-popup .more-info-pointer {" +
971                                        "background: transparent url(/img/new_submission/more_info_pointer.png) no-repeat center center;" +
972                                        "width: 20px; height: 10px; margin: -1px 0 0 90px;" +
973                                        "position: relative; z-index: 10;" +
974                                    "}",
975                pageFaderCSS = ".rhelper-page-fader {" +
976                                   "position: fixed; top: 115px; left: 0; bottom: 0; width: 100%;" +
977                                   "background-color: white; z-index: 1000;" +
978                               "}";
979            helperStyles.append(bgmaskCSS);
980            helperStyles.append(bgMessageCSS);
981            helperStyles.append(bgTickerCSS);
982            helperStyles.append(modalWindowCSS);
983            helperStyles.append(modalCloseIconCSS);
984            helperStyles.append(modalCloseCSS);
985            helperStyles.append(modalOverCloseCSS);
986            helperStyles.append(modalTitleCSS);
987            helperStyles.append(modalContentCSS);
988            helperStyles.append(moreInfoPopupCSS);
989            helperStyles.append(moreInfoCSS);
990            helperStyles.append(moreInfoPointerCSS);
991            helperStyles.append(pageFaderCSS);
992        }
993        // END OF THE STYLES CREATION FUNCTION
994        // ================================================================================
995
996        // Returns a function, that, as long as it continues to be invoked, will not
997        // be triggered. The function will be called after it stops being called for
998        // N milliseconds. If `immediate` is passed, trigger the function on the
999        // leading edge, instead of the trailing.
1000        function debounce(func, wait, immediate) {
1001            var timeout;
1002            return function() {
1003                var context = this, args = arguments;
1004                var later = function() {
1005                    timeout = null;
1006                    if (!immediate) func.apply(context, args);
1007                };
1008                var callNow = immediate && !timeout;
1009                clearTimeout(timeout);
1010                timeout = setTimeout(later, wait);
1011                if (callNow) func.apply(context, args);
1012            };
1013        };
1014
1015        //check file extension
1016        function hasExtension(fileName, exts) {
1017            return (new RegExp('(' + exts.join('|').replace(/\./g, '\\.') + ')$')).test(fileName);
1018        }
1019
1020        return publicProperties;
1021    }
1022
1023});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.