PageSourceSearch

https://www.grantforward.com/public/js/lib/czd_onboard_popup.js?v=284

js grantforward.com collected 2026-09-24 18:37:41 UTC 27,501 bytes, 603 lines download raw bytes

1/**
2 * czdOnboard is a jQuery plugin that makes it easy to create popup like onboard tooltips
3 *
4 * Author: VuongPham
5 *
6 * Requires: jQuery
7 */
8
9(function($){
10
11    /**
12     * Copyright 2012, Digital Fusion
13     * Licensed under the MIT license.
14     * http://teamdf.com/jquery-plugins/license/
15     *
16     * @author Sam Sehnert
17     * @desc A small plugin that checks whether elements are within
18     *       the user visible viewport of a web browser.
19     *       only accounts for vertical position, not horizontal.
20     */
21    var $w=$(window);
22    $.fn.visible = function(bottomScreenDistance,partial,hidden,direction,container){
23
24        if (this.length < 1)
25            return;
26
27    // Set direction default to 'both'.
28    direction = direction || 'both';
29
30        var $t          = this.length > 1 ? this.eq(0) : this,
31            isContained = typeof container !== 'undefined' && container !== null,
32            $c          = isContained ? $(container) : $w,
33            wPosition   = isContained ? $c.position() : 0,
34            t           = $t.get(0),
35            vpWidth     = $c.outerWidth(),
36            vpHeight    = $c.outerHeight(),
37            clientSize  = hidden === true ? t.offsetWidth * t.offsetHeight : true;
38
39        if (typeof t.getBoundingClientRect === 'function'){
40
41            // Use this native browser method, if available.
42            var rec  = t.getBoundingClientRect(),
43                tViz = isContained ?
44                                    rec.top - wPosition.top >= 0 && rec.top < vpHeight + wPosition.top :
45                                    rec.top >= 0 && rec.top < vpHeight,
46                bViz = isContained ?
47                                    rec.bottom - wPosition.top > 0 && rec.bottom <= vpHeight + wPosition.top :
48                                    rec.bottom > 0 && rec.bottom + bottomScreenDistance <= vpHeight,
49                lViz = isContained ?
50                                    rec.left - wPosition.left >= 0 && rec.left < vpWidth + wPosition.left :
51                                    rec.left >= 0 && rec.left <  vpWidth,
52                rViz = isContained ?
53                                    rec.right - wPosition.left > 0  && rec.right < vpWidth + wPosition.left  :
54                                    rec.right > 0 && rec.right <= vpWidth,
55                vVisible   = partial ? tViz || bViz : tViz && bViz,
56                hVisible   = partial ? lViz || rViz : lViz && rViz,
57        vVisible = (rec.top < 0 && rec.bottom > vpHeight) ? true : vVisible,
58                hVisible = (rec.left < 0 && rec.right > vpWidth) ? true : hVisible;
59
60            if(direction === 'both')
61                return clientSize && vVisible && hVisible;
62            else if(direction === 'vertical')
63                return clientSize && vVisible;
64            else if(direction === 'horizontal')
65                return clientSize && hVisible;
66        } else {
67
68            var viewTop         = isContained ? 0 : wPosition,
69                viewBottom      = viewTop + vpHeight,
70                viewLeft        = $c.scrollLeft(),
71                viewRight       = viewLeft + vpWidth,
72                position        = $t.position(),
73                _top            = position.top,
74                _bottom         = _top + $t.height(),
75                _left           = position.left,
76                _right          = _left + $t.width(),
77                compareTop      = partial === true ? _bottom : _top,
78                compareBottom   = partial === true ? _top : _bottom,
79                compareLeft     = partial === true ? _right : _left,
80                compareRight    = partial === true ? _left : _right;
81
82            if(direction === 'both')
83                return !!clientSize && ((compareBottom <= viewBottom) && (compareTop >= viewTop)) && ((compareRight <= viewRight) && (compareLeft >= viewLeft));
84            else if(direction === 'vertical')
85                return !!clientSize && ((compareBottom <= viewBottom) && (compareTop >= viewTop));
86            else if(direction === 'horizontal')
87                return !!clientSize && ((compareRight <= viewRight) && (compareLeft >= viewLeft));
88        }
89    };
90
91})(jQuery);
92
93$.fn.czdOnboard = function(options){
94    var me = this;
95    me.popup = me.popup || null;
96    var lastScrollTop = null;
97    var scrollPending = false;
98
99    me.keys = {
100        end: 35,
101        home: 36,
102        up: 38,
103        down: 40,
104        esc: 27,
105        enter: 13,
106        space: 32,
107        tab: 9
108    };
109    me.menu = {
110        eh: {
111            onKeydown: function(event) {
112                const currentElm = $(event.currentTarget)
113                const key = event.which
114                switch(key) {
115                    // move this to keydown to overide browser normal key down tab
116                    case me.keys.tab:
117                        event.preventDefault();
118                        var r = 0;
119                        if (me.options.onTabKey && typeof me.options.onTabKey == 'function') {
120                            r = me.options.onTabKey.call(this, event, event.shiftKey);
121                        }
122                        // r == 1 -> keep the popup open
123                        if (!r) {
124                            // close the popup
125                            me.menu.ui.focusUpward(event);
126                        }
127                        break;
128                    case me.keys.up:
129                    case me.keys.down:
130                        event.preventDefault();
131                        event.stopPropagation();
132                        if (!currentElm.is(':radio')) {
133                            // not a radio button
134                            me.menu.ui.focusVertical(event);
135                        }
136                        break;
137                    case me.keys.end:
138                        event.preventDefault();
139                        // Activate last tab
140                        me.menu.ui.focusLast(event);
141                        break;
142                    case me.keys.home:
143                        event.preventDefault();
144                        // Activate first tab
145                        me.menu.ui.focusFirst(event);
146                        break;
147                    case me.keys.enter:
148                        event.preventDefault();
149                        break;
150                    case me.keys.space:
151                        //prevent scroll on space
152                        if ($(event.target).is('input[type="text"]')) return;
153                        event.preventDefault();
154                        break;
155                
156                }
157            },
158            onKeyup: function(event) {
159                const currentElm = $(event.currentTarget)
160                var key = event.which
161                switch (key) {
162                    case me.keys.space:
163                    case me.keys.enter:
164                        event.stopPropagation();
165                        if ($(event.target).is('input[type="text"]')) return;
166                        event.preventDefault();
167                        me.menu.eh.onFakeClick(event);
168                        break;
169                        // Up and down are in keydown
170                        // because we need to prevent page scroll >:)
171                    case me.keys.esc:
172                        event.preventDefault();
173                        event.stopPropagation();
174                        if (me.isOpen && (1 || me.options.closeOnEsc)) {
175                            me.isOpen = false;
176                            me.menu.ui.focusUpward(event);
177                        }
178                        break;
179                };
180            },
181            onFakeClick: function(event) {
182                var currentElm = $(event.currentTarget);
183                currentElm[0].click();
184            }
185        },
186        ui: {
187            /*
188            The logic of czdOnboard UI/UX looks like this:
189                - groups a list of consecutive items (.tag-box-item) into a .tag-box element
190                - use up/down arrow keys to move among .tag-box-item(s) in a .tag-box group
191                - use Tab key to move between adjacent .tag-box elements
192
193            Sample HTML:
194                <div class="tag-box">
195                    <button class="tag-box-item">group 1, item 1</button>
196                    <button class="tag-box-item">group 1, item 2</button>
197                </div>
198                <div class="tag-box">
199                    <button class="tag-box-item">group 2, item 1</button>
200                    <button class="tag-box-item">group 2, item 2</button>
201                </div>
202            */
203            _focus: function(elem){
204                $(elem).closest('.czd-popup-container').find('.tag-box .tag-box-item').attr('tabindex', -1);
205                $(elem).attr('tabindex', 0).focus();
206            },
207            focusFirst: function(event){
208                var currentElm = $(event.currentTarget);
209
210                me.menu.ui._focus(currentElm.parent().children('.tag-box-item').first());
211            },
212            focusLast: function(event){
213                var currentElm = $(event.currentTarget);
214
215                me.menu.ui._focus(currentElm.parent().children('.tag-box-item').last());
216            },
217            focusVertical: function(event){
218                var currentElm = $(event.currentTarget);
219
220                if ( event.which === me.keys.down ){
221                    var gotoElm = currentElm.nextAll('.tag-box .tag-box-item').first();
222
223                    if ( gotoElm.length === 0 ){ //no more items 
224                        me.menu.ui.focusFirst(event);
225                    }else{
226                        me.menu.ui._focus(gotoElm);
227                    }
228                }else{
229                    var gotoElm = currentElm.prevAll('.tag-box .tag-box-item').first();
230
231                    if ( gotoElm.length === 0 ){ //no more items 
232                        me.menu.ui.focusLast(event);
233                    }else{
234                        me.menu.ui._focus(gotoElm);
235                    }
236                }
237            },
238            focusUpward: function(event){
239                eventHandlers.onClosePopup(event, true);
240            }
241        }
242    }
243    this.options = {
244        // czdOnboard ID
245        id: null,                    // Choose a unique id, otherwise czdOnboard will set one for you (czdOnboard1, czdOnboard2, ...)
246
247        // Dimensions
248        width: 'auto',               // The width of the content area, e.g. 'auto', 200, '80%'
249        height: 'auto',              // The height of the content area
250        minWidth: null,              // Minimal width
251        minHeight: null,             // Minimal height
252        maxWidth: null,              // Maximal width
253        maxHeight: null,             // Maximal height
254
255        // Content
256        content: null,               // You can use HTML or a jQuery element, e.g. jQuery('#czdOnboard-content'). The elements will be appended to the content element and then made visible, so hide them with style="display: none" beforehand
257        getContent: null,            // Get the content from an attribute when czdOnboard opens, e.g. getContent: 'data-content'. Use 'html' to get the attached elements HTML as content
258        title: null,                 // Adds a title to your czdOnboard
259        getTitle: null,              // Get the title from an attribute when czdOnboard opens, e.g. getTitle: 'data-title'
260        footer: null,                // Adds a footer to your czdOnboard
261        isolateScroll: true,         // Isolates scrolling to the content container
262
263        position: {
264            x: 'center',               // Horizontal position, use a number, 'left', 'right' or 'center'
265            y: 'center'                // Vertical position, use a number, 'top', 'bottom' or 'center'
266        },
267        outside: null,               // Use 'x', 'y', or 'xy' to move your czdOnboard outside of the target element
268        offset: 0,                   // Offset to final position, you can set different values for x and y with an object, e.g. {x: 20, y: 10}
269        attributes: {                // Note that attributes can only be 'left' or 'right' when using numbers for position, e.g. {x: 300, y: 20}
270            x: 'left',                 // Horizontal position, use 'left' or 'right'
271            y: 'top'                   // Vertical position, use 'top' or 'bottom'
272        },
273        fixed: false,                // Your czdOnboard will stay on position when scrolling
274        adjustPosition: true,        // Adjusts your czdOnboardes position if there is not enough space, use 'flip', 'move' or true for both. This option overrides the reposition options
275        adjustTracker: false,        // By default czdOnboard adjusts its position when it opens or when the window size changes, set to true to also adjust when scrolling
276        adjustDistance: 5,           // The minimal distance to the viewport edge while adjusting. Use an object to set different values, e.g. {top: 50, right: 5, bottom: 20, left: 5}
277        reposition: true,            // Calculates new position when the window-size changes
278        repositionOnOpen: true,      // Calculates new position each time czdOnboard opens (rather than only when it opens the first time)
279        repositionOnContent: true,   // Calculates new position when the content changes with .setContent() or .setTitle()
280
281
282        // Animations
283        fade: 180,                   // Fade duration in ms, set to 0 or false to disable
284        animation: null,             // Animation when opening or closing, use 'pulse', 'zoomIn', 'zoomOut', 'move', 'slide', 'flip', 'tada'
285
286        // Closing
287        closeOnEsc: false,           // Close czdOnboard when pressing [esc] key
288        closeOnClick: false,         // Close czdOnboard with mouseclick. Use true (click anywhere), 'box' (click on czdOnboard itself), 'overlay' (click on the overlay), 'body' (click anywhere but czdOnboard)
289        closeOnMouseleave: false,    // Close czdOnboard when the mouse leaves the czdOnboard area or the area of the attached element
290        closeButton: false,          // Adds a close button to your czdOnboard. Use 'title', 'box', 'overlay' or true (true will add the button to the overlay, title or the czdOnboard itself, in that order if any of those elements can be found)
291
292        topDocumentDistance: 0,      // Add distance from top of czdOnBoard to top of screen to adjust board bottom, can use to prevent board being cover by header
293        bottomDocumentDistance: 0,   // Add distance from bottom of czdOnBoard to bottom of document to adjust board top, can use to prevent board being cover by footer
294
295        bottomScreenDistance: 0,     // Add distance from bottom of czdOnBoard to bottom of screen to adjust board top 
296        bottomToParentOuter: 0,      // Add distance from bottom of czdOnBoard to bottom of parent wrapper to adjust board top 
297
298        outerWrapper: null,          // Define outer wrapper to caculate popup position (JQuery selector)
299
300        checkVisibleToAdjust: true,   // Your czdOnBoard will adjust position if bottom part of it not visible in screen
301
302        // custom callback
303        onTabKey: null,
304
305        meInvisDisplay: null,
306
307        notFocusBack: false,
308    }
309
310    me.isOpen = false;
311
312    /** Element list */
313    var elements = {
314        "czdPopupWrapper"   : ".czd-popup-wrapper",
315        "czdPopupContainer" : ".czd-popup-container",
316        "czdPopupContent"   : ".czd-popup-content",
317        "czdPopupStyle"     : ".czd-popup-wrapper style",
318    }
319
320    /* Element getter */
321    function elm(name) {
322        return $(elements[name]);
323    }
324
325
326    function getScrollableParent($el) {
327        return $el.parents().filter(function () {
328          var overflowY = $(this).css('overflowY');
329          var overflow = $(this).css('overflow');
330          var hasScroll = /(auto|scroll)/.test(overflowY + overflow);
331          return hasScroll && this.scrollHeight > this.clientHeight;
332        }).first();
333    }
334
335    const POPUP_ARROW_WIDTH = 16;
336    const POPUP_BORDER_WIDTH = 1;
337    const DEFAULT_ARROW_OFFSET_FROM_BORDER = 7;
338    /**
339     * The arrow looks like this:
340     *                                      |
341     *            ....8px.....8px....       |
342     * ___________|_.______.______._|_______|
343     *             \ \           / /    d 
344     *               \ \       / /
345     *                 \ \   / /
346     *                   \ v /
347     *                     v
348     * 
349     * Default value of d (arrow offset) in czd_onboard_popup.css file is 7px, but the popup is not shown fully within the viewport.
350     * We have to calculate d (arrow offset) in order to "fit" the popup within the viewport.
351     */
352    function calculate_arrow_offset(Mx, popup_width) {
353        var window_width = $(window).width();
354        /*
355        d will fit if
356            _check_fit(d) := POPUP_ARROW_WIDTH / 2 + (d + POPUP_BORDER_WIDTH) <= window_width - Mx && Mx >= popup_width - (d + POPUP_BORDER_WIDTH) - POPUP_ARROW_WIDTH / 2;
357        */
358        // we assume that popup_width <= window_width
359        // if not, just return the default value
360        if (popup_width > window_width) {
361            return DEFAULT_ARROW_OFFSET_FROM_BORDER;
362        }
363        // calculate the possible value range of d, d must be in the range [pivot_left, pivot_right]
364        var pivot_right = Math.min(popup_width - POPUP_ARROW_WIDTH, (window_width - Mx) - POPUP_ARROW_WIDTH / 2);
365        var pivot_left = Math.max(0, popup_width - POPUP_ARROW_WIDTH / 2 - Mx);
366        if (pivot_left > pivot_right) {
367            // let pivot_left <= pivot_right
368            var temp = pivot_left;
369            pivot_left = pivot_right;
370            pivot_right = temp;
371        }
372        // we respect the default value in the css file for these cases
373        if (pivot_left <= DEFAULT_ARROW_OFFSET_FROM_BORDER && DEFAULT_ARROW_OFFSET_FROM_BORDER <= pivot_right) {
374            // the popup will offset to the left
375            return DEFAULT_ARROW_OFFSET_FROM_BORDER;
376        }
377        const default_offset_right = popup_width - DEFAULT_ARROW_OFFSET_FROM_BORDER - POPUP_ARROW_WIDTH;
378        if (pivot_left <= default_offset_right && default_offset_right <= pivot_right) {
379            // the popup will offset to the right
380            return default_offset_right;
381        }
382        // if the default value is not good enough
383        return pivot_left;
384    }
385
386    /**
387     * contains all event handlers for the view
388     * @type {Object}
389     */
390    var eventHandlers = {
391        setPopupPosition: function(event) {
392            if (me.popup == null) return;
393            var elmOffset = me.offset();
394            var elmWidth = me.outerWidth();
395            var elmHeight = me.outerHeight();
396
397            var documentHeight = $(document).height();
398            var shownAbove = true;
399            var elmTopViewPort = me[0].getBoundingClientRect().top; // get the top view port
400            me.popup.css({
401                "width": me.options['width'] + 'px'
402            });
403            const Mx = elmOffset.left + elmWidth / 2; // position of the midpoint of the elm
404            var arrow_offset_from_border = calculate_arrow_offset(Mx, me.options['width']);
405            me.popup.removeClass("right").addClass("left");
406            me.popup.css({
407                'left': (Mx - me.options['width'] + (arrow_offset_from_border + POPUP_ARROW_WIDTH / 2 + POPUP_BORDER_WIDTH)) + 'px'
408            });
409            // set position of arrow
410            elm("czdPopupStyle").append('.czd-popup-wrapper:before {right: ' + (arrow_offset_from_border).toString() + 'px !important;}');
411            elm("czdPopupStyle").append('.czd-popup-wrapper:after {right: ' + (arrow_offset_from_border + 2).toString() + 'px !important;}');
412
413            var popupHeight = me.popup.outerHeight(); // it seems that this assignment here will not cause the bug of me.popup.outerHeight() is smaller than me.popup.height()
414            if (me.options['checkVisibleToAdjust'] === true) {
415                if (popupHeight + 10 > elmTopViewPort) {
416                    shownAbove = false;
417                }
418            }
419            // me.popup[0].getBoundingClientRect().height should be the most correct value than me.popup.outerHeight()...
420            // but I will leave this console.log here to let you can choose the correct height value
421            // console.log(popupHeight, me.popup.height(), me.popup[0].getBoundingClientRect().height);
422            if (shownAbove) {
423                me.popup.removeClass("bottom");
424                me.popup.addClass("top");
425                me.popup.css({
426                    'top': (elmOffset.top - (POPUP_ARROW_WIDTH / 2 - POPUP_BORDER_WIDTH) - popupHeight) + 'px'
427                });
428            } else {
429                me.popup.removeClass("top");
430                me.popup.addClass("bottom");
431                me.popup.css({
432                    'top': (elmOffset.top + elmHeight + (POPUP_ARROW_WIDTH / 2 - POPUP_BORDER_WIDTH)) + 'px'
433                });
434            }
435        },
436        onClosePopup: function (event, forced=false) {
437            if (!me.options['id']) return;
438            var target = $(event.target);
439            //if not forced close && click on popup, ignore it
440            if (!forced && (target.closest(elements['czdPopupWrapper']).length > 0 || target.closest('.'+me.attr('class').split(' ').join('.')).length > 0)) {
441                return;
442            }
443            hidePopup();
444            //console.log("closed " + me.options['id']);
445        }
446        ,
447        startFocus: function (event) {
448            // ref: https://stackoverflow.com/questions/2823471/jquery-how-to-find-first-visible-input-select-textarea-excluding-buttons#comment108875890_2823749
449            elm('czdPopupContent').find('a[tabindex], :input:visible:enabled:not([readonly])').first().focus();
450        }
451    }
452
453    function showPopup(){
454        if (me.popup.length == 0 ){
455            me.isOpen = true;
456            czdPopupWrapper = $(
457                '<div class="czd-popup-wrapper" js-id="'+me.options['id']+'">'
458                    +'<div class="czd-popup-container">'
459                        +'<div class="czd-popup-content"></div>'
460                        +'<div class="czd-popup-close"></div>'
461                    +'</div>'
462                    +'<style></style>' // styling element for czdPopupWrapper (ref: https://community.latenode.com/t/10298/2)
463                +'</div>');
464            // czdPopupWrapper.insertAfter(me);
465            // move popup to body and recalculate the postion of the popup from relative to static
466            czdPopupWrapper.appendTo('body');
467            me.popup = czdPopupWrapper;
468            me.addClass('active');
469
470            var popupContent = czdPopupWrapper.children(elements['czdPopupContainer']).children(elements['czdPopupContent']);
471            popupContent.html(me.options['content']);
472            eventHandlers.setPopupPosition();
473            eventHandlers.startFocus();
474        }
475    }
476
477    me.offPopupOnScroll = function (e) {
478        var target = e.target;
479
480        if (target !== document && target !== document.body && target !== document.documentElement) {
481            if (!scrollPending) {
482                scrollPending = true;
483    
484                window.requestAnimationFrame(function () {
485                    var currentScrollTop = target.scrollTop;
486                    var scrollDiff = Math.abs(currentScrollTop - (lastScrollTop || 0));
487                    if (scrollDiff < 5) {
488                        eventHandlers.setPopupPosition(e);
489                    } else if (scrollDiff < 10) {
490                        eventHandlers.onClosePopup(e);
491                    }
492    
493                    lastScrollTop = currentScrollTop;
494                    scrollPending = false;
495                });
496            }
497        }
498    }
499
500    function hidePopup(){
501        me.removeClass('active');
502        me.trigger('czd_onboard_popup_hide');
503        // if (me.attr('js-display-default') === 'hidden'){
504        //     me.addClass('hidden');
505        // }
506        if (me.popup){
507            me.popup.remove();
508            me.popup = null;
509            unbindEvents(me.options['id']);
510            me.options['id'] = null;
511        }
512        // focus on the opener after hid the popup
513        // check me is visible or display
514        if (me.options['notFocusBack']) {
515            return;
516        }
517
518        if (me.hasClass('add-grant-tag')){
519            me.attr('tabindex', '0');
520        }
521
522        if (me.is(":visible")) {
523            me.focus();
524        }else {
525            // what can me do if me is invisible
526            var display = this.meInvisDisplay || "block";
527            me.css("display", display).focus()
528        }
529    }
530
531    function bindEvents(id) {
532        // console.log('initiated event ' + id);
533        $(window).on('resize.' + id, eventHandlers.setPopupPosition);
534        $(window).on('czd_onboard_close.' + id, eventHandlers.onClosePopup);
535        $(document).on('mousedown.' + id, eventHandlers.onClosePopup);
536        //2.22.2 : accessibility
537        if ( me.popup ){
538            me.popup.on('focusout', function(e){
539                //when using focusout event
540                //we can find the target that is going to get the focus
541                //if the element getting the focus is outside of the onboard popup
542                var destinationElm = $(e.relatedTarget);
543                if ( destinationElm.length > 0 && destinationElm.closest(elements['czdPopupWrapper']).length === 0 ){
544                    hidePopup();
545                }
546                //hidePopup();
547            });
548        }
549        /*me.on('focusout', function(e){
550            //if the target of the focus is the popup then it's fine
551            var destinationElm = $(e.relatedTarget);
552            if ( destinationElm.length === 0 || destinationElm.closest(elements['czdPopupWrapper']).length > 0 ){
553                return;
554            }
555            hidePopup();
556        });*/
557        elm('czdPopupContainer').on('keydown', '.czd-popup-content :focusable', function(event){
558            me.menu.eh.onKeydown(event);
559        });
560        elm('czdPopupContainer').on('keyup', '.czd-popup-content :focusable', function(event){
561            me.menu.eh.onKeyup(event);
562        });
563        document.addEventListener('scroll', me.offPopupOnScroll, true);
564    }
565
566    function unbindEvents(id){
567        // console.log('de-initiated event ' + id);
568        $(window).off('resize.' + id);
569        $(window).off('czd_onboard_close.' + id);
570        $(document).off('mousedown.' + id);
571        if ( me.popup ){
572            me.popup.off('focusout');
573        };
574        document.removeEventListener('scroll', me.offPopupOnScroll, true);
575    }
576
577     /**
578     * initialize function
579     * @return {None}
580     */
581    function init() {
582
583        if (!options['id']){
584            return;
585        }
586        var czdPopupWrapper = elm('czdPopupWrapper').filter('[js-id="'+ options['id']+'"]');
587        me.popup = czdPopupWrapper;
588        $.map( options, function( val, i ) {
589            me.options[i] = val;
590        });
591        if ( czdPopupWrapper.length > 0 ){ //because the popup get destroyed everytime it hides, if it is here then it's up
592            //if it is up then we hide it
593            hidePopup(me.options['id']);
594        }else{
595            // console.log("czdOnboard: " + me.options['id']);
596            showPopup();
597            bindEvents(me.options['id']);
598        }
599    }
600
601    //run initialization function
602    init();
603}

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.