PageSourceSearch

https://www.ilincs.org/apps/grein/shinyjs/shinyjs-default-funcs.js

js ilincs.org collected 2026-09-28 05:51:39 UTC 27,645 bytes, 831 lines download raw bytes

1// shinyjs 1.0 by Dean Attali
2// Perform common JavaScript operations in Shiny apps using plain R code
3
4shinyjs = function() {
5
6  // --------------------------------
7  // ------ Private variables -------
8
9  // store the event handlers for the onevent function when it's called on
10  // elements that do not exist yet in the document
11  var _oneventData = {};
12
13  // some functions need to work with dynamically generated elements, so they
14  // can add themselves to this list and will be called whenever the DOM is mutated
15  var _mutationSubscribers = [];
16
17  // ---------------------------------------
18  // ------ General helper functions -------
19
20  // get an element by id using JQuery (escape chars that have special
21  // selector meaning)
22  var _jqid = function(id) {
23    return $("#" + id.replace( /(:|\.|\[|\]|,)/g, "\\$1" ));
24  };
25
26  // listen to DOM changes and whenever there are new nodes added, let all
27  // the mutation subscribers know about the new elements
28  var _initMutations = function() {
29    var canObserveMutation = 'MutationObserver' in window;
30    if (canObserveMutation) {
31      var mutationCallback = function(mutations) {
32        mutations.forEach(function(mutation) {
33          $.each(mutation.addedNodes, function(idx, node) {
34            $.each(_mutationSubscribers, function(idx, fxn) {
35              // call the subscriber on each new node
36              fxn(node);
37            });
38          });
39        });
40      };
41      var observer = new MutationObserver(mutationCallback);
42      observer.observe(document.body, { childList : true, subtree : true });
43    }
44  };
45
46  // if the given HTML tag is a shiny input, return the input container.
47  // otherwise, return the original tag
48  var _getContainer = function(els) {
49    return $.map(els, function(el) {
50      el = $(el);
51      var inputContainer = el.closest(".shiny-input-container");
52      if (inputContainer.length > 0) {
53        el = inputContainer;
54      }
55      return el;
56    });
57  };
58
59  // given a function parameters with several different ways to get DOM
60  // elements, retrieve the correct ones
61  var _getElements = function(params) {
62    var $els = null;
63    if (params.elements !== null && typeof params.elements !== "undefined") {
64      $els = params.elements;
65    } else if (params.id !== null && typeof params.id !== "undefined") {
66      $els = _jqid(params.id);
67    } else if (params.selector !== null && typeof params.selector !== "undefined") {
68      $els = $(params.selector);
69    }
70    if ($els === null || $els === undefined || $els.length == 0) {
71      shinyjs.debugMessage("shinyjs: Could not find DOM element using these parameters:");
72      shinyjs.debugMessage(params);
73      $els = null;
74    }
75    return $els;
76  };
77
78  // -----------------------------------
79  // ------ Helpers for `toggle` -------
80
81  // is an element currently hidden?
82  var _isHidden = function(el) {
83    return el.css("display") === "none";
84  };
85
86  // ----------------------------------------
87  // ------ Helpers for `toggleState` -------
88
89  // is an element currently disabled?
90  var _isDisabled = function(el) {
91    return el.prop('disabled') === true;
92  };
93
94  // -------------------------------------
95  // ------ Helpers for `disabled` -------
96
97  // disable all the elements that were initialized as disabled
98  var _initDisabled = function() {
99    // disable elements on page load
100    _initDisabledHelper($(".shinyjs-disabled"));
101
102    // disable new elements being added to the document
103    _mutationSubscribers.push(function(node) {
104      _initDisabledHelper($(node).find(".shinyjs-disabled"));
105      if ($(node).is(".shinyjs-disabled")) {
106        _initDisabledHelper($(node));
107      }
108    });
109  };
110
111  var _initDisabledHelper = function(els) {
112    if (els.length == 0) return;
113
114    // use a tiny delay because some input elements (sliders, selectize) need
115    // to first be initialized, and I don't know how to tell when they're ready
116    setTimeout(function() {
117      // disable elements
118      shinyjs.disable({ elements : els });
119    }, 10);
120  };
121
122  // ----------------------------------------
123  // ------ Helpers for `show`/`hide` -------
124
125  var _showHide = function(method, params) {
126    var defaultParams = {
127      id       : null,
128      anim     : false,
129      animType : "slide",
130      time     : 0.5,
131      selector : null,
132      elements : null
133    };
134    params = shinyjs.getParams(params, defaultParams);
135
136    var $els = _getElements(params);
137    if ($els === null) return;
138
139    // for input elements, hide the whole container, not just the input
140    $els = _getContainer($els);
141
142    // if an element was hidden on page load, remove that flag
143    $.map($els, function(el) {
144      if ($(el).hasClass("shinyjs-hide")) {
145        $(el).removeClass("shinyjs-hide");
146        $(el).hide();
147      }
148    });
149
150    if (!params.anim) {
151      $.map($els, function(el) {
152        (method == "show") ?
153          $(el).show() :
154          $(el).hide();
155      });
156    } else {
157      if (params.animType == "fade") {
158        $.map($els, function(el) {
159          (method == "show") ?
160            $(el).fadeIn(params.time * 1000) :
161            $(el).fadeOut(params.time * 1000);
162        });
163      } else {
164        $.map($els, function(el) {
165          (method == "show") ?
166            $(el).slideDown(params.time * 1000) :
167            $(el).slideUp(params.time * 1000);
168        });
169      }
170    }
171
172    // If an element was initially hidden when app started, tell shiny that
173    // it's now visible so that it can properly render dynamic elements
174    $.map($els, function(el) {
175      $(el).trigger(method == "show" ? "shown" : "hidden");
176    });
177  };
178
179  // ---------------------------------------------------
180  // ------ Helpers for `addClass`/`removeClass` -------
181
182  var _addRemoveClass = function(method, params) {
183    var defaultParams = {
184      id       : null,
185      class    : null,
186      selector : null,
187      elements : null
188    };
189    params = shinyjs.getParams(params, defaultParams);
190
191    var $els = _getElements(params);
192    if ($els === null) return;
193
194    (method == "add") ?
195      $els.addClass(params.class) :
196      $els.removeClass(params.class);
197  };
198
199  // ---------------------------------------------------
200  // ------ Helpers for `enable`/`disable` -------------
201
202  var _enableDisable = function(method, params) {
203    var defaultParams = {
204      id       : null,
205      selector : null,
206      elements : null
207    };
208    params = shinyjs.getParams(params, defaultParams);
209
210    var $els = _getElements(params);
211    if ($els === null) return;
212
213    // make sure we take special care of elements that need to be specifically
214    // enabled with special javascript
215    var toadd = $els.find(".selectized, .js-range-slider");
216    $els = $($els.toArray().concat(toadd.toArray()));
217
218    $.map($els, function(el) {
219      var $el = $(el);
220
221      // selectize and slider inputs need special javascript
222      if ($el.hasClass("selectized")) {
223        (method == "enable") ?
224          $el.selectize()[0].selectize.enable() :
225          $el.selectize()[0].selectize.disable();
226      } else if ($el.hasClass("js-range-slider")) {
227        $el.data("ionRangeSlider").update({ disable : (method == "disable") });
228      }
229      // for colour inputs, we want to enable/disable all input fields
230      else if ($el.hasClass("shiny-colour-input")) {
231        $el = $(_getContainer($el)[0]);
232      }
233
234      // enable/disable the container as well as all individual inputs inside
235      // (this is needed for grouped inputs such as radio and checkbox groups)
236      var toadd = $el.find("input, button, textarea");
237      $el = $($el.toArray().concat(toadd.toArray()));
238      $el.attr('disabled', (method == "disable"));
239      $el.prop('disabled', (method == "disable"));
240      method == "disable" ? $el.addClass("disabled") : $el.removeClass("disabled");
241    });
242  };
243
244  // ----------------------------------
245  // ------ Helpers for `reset` -------
246
247  // find all shiny input elements and set them up to allow them to be reset
248  var _initResettables = function() {
249    // grab all the shiny input containers that exist when the app loads
250    _initResettablesHelper($(".shiny-input-container"));
251
252    // observer new elements added to the document so that dynamically generated
253    // elements can also be resettable
254    _mutationSubscribers.push(function(node) {
255      _initResettablesHelper($(node).find(".shiny-input-container"));
256      if ($(node).is(".shiny-input-container")) {
257        _initResettablesHelper($(node));
258      }
259    });
260  };
261
262  // helper function to get the initial date from a bootstrap date element
263  // if there is no initial date, return the current date
264  var _getInputDate = function(el) {
265    if (el[0].hasAttribute('data-initial-date')) {
266      if (el.attr('data-initial-date') === "") {
267        return 'NA';
268      } else {
269        return el.attr('data-initial-date');
270      }
271    }
272    var today = new Date();
273    var yyyy = today.getFullYear().toString();
274    var mm = (today.getMonth() + 1).toString();
275    var dd  = today.getDate().toString();
276    return yyyy + '-' + (mm[1]?mm:"0"+mm[0]) + '-' + (dd[1]?dd:"0"+dd[0]);
277  };
278
279  // go through every Shiny input and based on what kind of input it is,
280  // add some information to the HTML tag so that we can know how to
281  // update it back to its original value
282  var _initResettablesHelper = function(els) {
283     for (var j = 0; j < els.length; j++) {
284        var inputContainer = $(els[j]);
285        var input = inputContainer;
286        var foundInput = true;
287        var inputType = null,
288            inputValue = null,
289            inputId = null;
290
291        // dateInput
292        if (input.hasClass("shiny-date-input")) {
293          input = input.children("input");
294          inputType = "Date";
295          inputValue = _getInputDate(input);
296          inputId = inputContainer.attr('id');
297        }
298        // dateRangeInput
299        else if (input.hasClass("shiny-date-range-input")) {
300          inputType = "DateRange";
301          inputValue = _getInputDate($(input.find("input")[0])) + "," +
302                       _getInputDate($(input.find("input")[1]));
303        }
304        // checkboxGroupInput
305        else if (input.hasClass("shiny-input-checkboxgroup")) {
306          inputType = "CheckboxGroup";
307          var selected = [];
308          var selectedEls = input.find("input[type='checkbox']:checked");
309          selectedEls.each(function() {
310            selected.push($(this).val());
311          });
312          inputValue = JSON.stringify(selected);
313        }
314        // radioButtons
315        else if (input.hasClass("shiny-input-radiogroup")) {
316          inputType = "RadioButtons";
317          inputValue = input.find("input[type='radio']:checked").val();
318        }
319        // sliderInput
320        else if (input.children(".js-range-slider").length > 0) {
321          input = input.children(".js-range-slider");
322          inputType = "Slider";
323          inputValue = input.attr('data-from');
324          if (typeof input.attr('data-to') !== "undefined") {
325            inputValue = inputValue + "," + input.attr('data-to');
326          }
327        }
328        // selectInput / selectizeInput
329        else if (input.find("select").length > 0) {
330          input = input.find("select");
331          inputType = "Select";
332          inputValue = input.val();
333          if (inputValue === null) {
334            inputValue = "";
335          }
336          inputValue = JSON.stringify(inputValue);
337        }
338        // colourInput
339        else if (input.children("input.shiny-colour-input").length > 0) {
340          input = input.children("input.shiny-colour-input");
341          inputType = "Colour";
342          inputValue = input.attr('data-init-value');
343        }
344        // numericInput
345        else if (input.children("input[type='number']").length > 0) {
346          input = input.children("input[type='number']");
347          inputType = "Numeric";
348        }
349        // textInput
350        else if (input.children("input[type='text']").length > 0) {
351          input = input.children("input[type='text']");
352          inputType = "Text";
353        }
354        // checkboxInput
355        else if (input.find("input[type='checkbox']").length > 0) {
356          input = input.find("input[type='checkbox']");
357          inputType = "Checkbox";
358          inputValue = input.prop('checked');
359        }
360        // passwordInput
361        else if (input.children("input[type='password']").length > 0) {
362          input = input.children("input[type='password']");
363          inputType = "Password";
364        }
365        // textAreaInput
366        else if (input.children("textarea").length > 0) {
367          input = input.children("textarea");
368          inputType = "TextArea";
369        }
370        else if (input.find("input[type='file']").length > 0) {
371          input = input.find("input[type='file']");
372          inputType = "File"
373        }
374        // if none of the above, no supported Shiny input was found
375        else {
376          foundInput = false;
377        }
378
379        // if we found a Shiny input, set all the info on it
380        if (foundInput) {
381          if (inputId === null) {
382            inputId = input.attr('id');
383          }
384          if (inputValue === null) {
385            inputValue = input.val();
386          }
387          input.attr('data-shinyjs-resettable-id', inputId).
388                attr('data-shinyjs-resettable-type', inputType).
389                attr('data-shinyjs-resettable-value', inputValue).
390                addClass('shinyjs-resettable');
391        }
392      }
393  };
394
395  // ------------------------------------
396  // ------ Helpers for `onevent` -------
397
398  // ensure that the onevent function works for dynamic elements
399  var _initOnevent = function() {
400    // for every new node in the DOM, check if there is an ID that was registered
401    // with `onevent` for a dynamic element that corresponds to a node that was
402    // just created. If so, find out what events were registered to it and the
403    // shiny event handlers for it, and attach them
404    _mutationSubscribers.push(function(node) {
405      $node = $(node);
406      $.each(_oneventData, function(id) {
407        var elementData = null;
408        if ($node.attr("id") == id) {
409          elementData = _oneventData[id];
410        } else if ($node.find("#" + id).length > 0) {
411          elementData = _oneventData[id];
412        }
413        if (elementData !== null) {
414          $.each(elementData, function(event, eventData) {
415            $.each(eventData, function(idx, shinyInputId) {
416              _oneventAttach({
417                event : event,
418                id : id,
419                shinyInputId : shinyInputId,
420                add : true
421              });
422            });
423          });
424        }
425      });
426    });
427  };
428
429  // attach an event listener to a DOM element that will trigger a call to Shiny
430  var _oneventAttach = function(params) {
431    var el = _jqid(params.id);
432
433    // for shiny inputs, perform the action when the event happens in any
434    // section of the input widget
435    el = $(_getContainer(el)[0]);
436
437    var shinyInputId = params.shinyInputId;
438    var attrName = "data-shinyjs-" + params.event;
439
440    // if this is the first event handler of this event type we attach to this
441    // element, initialize the data attribute and add the event handler
442    var first = !(el[0].hasAttribute(attrName));
443    if (first) {
444      el.attr(attrName, JSON.stringify(Object()));
445
446      el[params.event](function(event) {
447        // Store a subset of the event properties (many are non-serializeable)
448        var props = ['altKey', 'button', 'buttons', 'clientX', 'clienty',
449          'ctrlKey', 'pageX', 'pageY', 'screenX', 'screenY', 'shiftKey',
450          'which', 'charCode', 'key', 'keyCode'];
451        var eventSimple = {};
452        $.each(props, function(idx, prop) {
453          if (prop in event) {
454            eventSimple[[prop]] = event[[prop]];
455          }
456        });
457        eventSimple.shinyjsRandom = Math.random();
458
459        var oldValues = JSON.parse(el.attr(attrName));
460        var newValues = Object();
461        $.each(oldValues, function(key, value) {
462          var newValue = value + 1;
463          newValues[key] = newValue;
464          Shiny.onInputChange(key, eventSimple);
465        });
466
467        el.attr(attrName, JSON.stringify(newValues));
468      });
469    }
470
471    // if we want this action to overwrite existing ones, unbind event handler
472    if (params.add) {
473      var attrValue = JSON.parse(el.attr(attrName));
474    } else {
475      var attrValue = {};
476    }
477    attrValue[shinyInputId] = 0;
478    el.attr(attrName, JSON.stringify(attrValue));
479  };
480
481  return {
482
483    // by default, debug mode is off. If shinyjs is initialized with debug mode,
484    // then debugging messages will be printed to the console
485    debug : false,
486
487    // write a message to the console for debugging purposes if debug mode is on
488    debugMessage : function(text) {
489      if (shinyjs.debug) {
490        console.info(text);
491      }
492    },
493
494    // Given a set of user-provided parameters and some default parameters,
495    // return a dictionary of key-value parameter pairs.
496    // The user parameters can either be an (unnamed) array, in which case
497    // we assume the order of the parameters, or it can be a dictionary with
498    // key-value parameter pairs.
499    getParams : function (params, defaultParams) {
500      var finalParams = defaultParams;
501      if (typeof params == "string") {
502        params = Array(params);
503      }
504      if (params instanceof Array) {
505        for (var i = 0; i < params.length; i++) {
506          finalParams[Object.keys(finalParams)[i]] = params[i];
507        }
508      } else {
509        $.extend(finalParams, params);
510      }
511
512      return finalParams;
513    },
514
515    // this function gets called once (automatically) to initialize shinyjs
516    initShinyjs : function() {
517      _initMutations();
518      _initResettables();
519      _initDisabled();
520      _initOnevent();
521      shinyjs.init();
522    },
523
524    // the init function should not be implemented here, it is a placeholder
525    // so that users can define their own `shinyjs.init` function (using extendShinyjs)
526    // that will be run when the page is initialized
527    init : function() {},
528
529    // -----------------------------------------------------------------
530    // ------ All functions below are exported shinyjs function --------
531    // The documentation for function foo is available in R via ?shinyjs::foo
532
533    show : function (params) {
534      _showHide("show", params);
535    },
536
537    hide : function (params) {
538      _showHide("hide", params);
539    },
540
541    toggle : function (params) {
542      var defaultParams = {
543        id        : null,
544        anim      : false,
545        animType  : "slide",
546        time      : 0.5,
547        selector  : null,
548        condition : null
549      };
550      params = shinyjs.getParams(params, defaultParams);
551
552      // if there is no condition, then hide/show each element based on whether
553      // it is currently shown or hidden
554      if (params.condition === null) {
555        var $els = _getElements(params);
556        if ($els === null) return;
557
558        // for input elements, toggle the whole container, not just the input
559        $els = _getContainer($els);
560
561        $.map($els, function(el) {
562          params.elements = $(el);
563          _isHidden($(el)) ? shinyjs.show(params) : shinyjs.hide(params);
564        });
565      }
566      else if (params.condition) {
567        shinyjs.show(params);
568      } else {
569        shinyjs.hide(params);
570      }
571    },
572
573    addClass : function (params) {
574      _addRemoveClass("add", params);
575    },
576
577    removeClass : function (params) {
578      _addRemoveClass("remove", params);
579    },
580
581    toggleClass : function (params) {
582      var defaultParams = {
583        id        : null,
584        class     : null,
585        condition : null,
586        selector  : null
587      };
588      params = shinyjs.getParams(params, defaultParams);
589
590      // it there is no condition, add/remove class based on current state
591      if (params.condition === null) {
592        var $els = _getElements(params);
593        if ($els === null) return;
594
595        $.map($els, function(el) {
596          params.elements = $(el);
597          $(el).hasClass(params.class) ? shinyjs.removeClass(params) : shinyjs.addClass(params);
598        });
599      }
600      else if (params.condition) {
601        shinyjs.addClass(params);
602      } else {
603        shinyjs.removeClass(params);
604      }
605    },
606
607    enable : function (params) {
608      _enableDisable("enable", params);
609    },
610
611    disable : function (params) {
612      _enableDisable("disable", params);
613    },
614
615    toggleState : function (params) {
616      var defaultParams = {
617        id        : null,
618        condition : null,
619        selector  : null
620      };
621      params = shinyjs.getParams(params, defaultParams);
622
623      // it there is no condition, enable/disable based on current state
624      if (params.condition === null) {
625        var $els = _getElements(params);
626        if ($els === null) return;
627
628        $.map($els, function(el) {
629          params.elements = $(el);
630          _isDisabled($(el)) ? shinyjs.enable(params) : shinyjs.disable(params);
631        });
632      }
633      else if (params.condition) {
634        shinyjs.enable(params);
635      } else {
636        shinyjs.disable(params);
637      }
638    },
639
640    html : function (params) {
641      var defaultParams = {
642        id       : null,
643        html     : null,
644        add      : false,
645        selector : null
646      };
647      params = shinyjs.getParams(params, defaultParams);
648
649      var $els = _getElements(params);
650      if ($els === null) return;
651
652      $.each($els, function(idx, node) {
653        if (params.add) {
654          node.innerHTML += params.html;
655        } else {
656          node.innerHTML = params.html;
657        }
658      });
659    },
660
661    alert : function (params) {
662      var defaultParams = {
663        text : null
664      }
665      params = shinyjs.getParams(params, defaultParams);
666
667      if (typeof params.text == "object") {
668        alert(JSON.stringify(params.text, null, 4));
669      } else {
670        alert(params.text);
671      }
672
673    },
674
675    logjs : function (params) {
676      var defaultParams = {
677        text : null
678      }
679      params = shinyjs.getParams(params, defaultParams);
680
681      console.log(params.text);
682    },
683
684    runjs : function (params) {
685      var defaultParams = {
686        code : null
687      }
688      params = shinyjs.getParams(params, defaultParams);
689
690      eval(params.code);
691    },
692
693    // onevent function is more complicated than the rest of the shinyjs functions
694    // we attach an event handler (onclick/onkeydown/onmouseup/etc) to an element
695    // and when it happens we call Shiny
696    onevent : function (params) {
697      var defaultParams = {
698        event        : null,
699        id           : null,
700        shinyInputId : null,
701        add          : false
702      }
703      params = shinyjs.getParams(params, defaultParams);
704
705      var el = _jqid(params.id);
706
707      // if element does not exist in the document, save the information so that
708      // if the element is created dynamically later, we can add the handlers
709      if (el.length == 0) {
710        if (!(params.id in _oneventData)) {
711          _oneventData[params.id] = {};
712        }
713        var elementData = _oneventData[params.id];
714        if (!(params.event in elementData) || !params.add) {
715          elementData[params.event] = [];
716        }
717        elementData[params.event].push(params.shinyInputId);
718      }
719      // if the element does exist, add the event handler
720      else {
721        _oneventAttach(params);
722      }
723    },
724
725    // the reset function is also complicated because we need R to tell us
726    // what input element or form to reset, then the javascript needs to
727    // figure out the correct type and initial value for each input and pass
728    // that info back to R so that R can call the correct shiny::updateFooInput()
729    reset : function(params) {
730      var defaultParams = {
731        id : null,
732        shinyInputId : null
733      }
734      params = shinyjs.getParams(params, defaultParams);
735
736      var el = _jqid(params.id);
737
738      // find all the resettable input elements
739      var resettables;
740      if (el.hasClass("shinyjs-resettable")) {
741        resettables = el;
742      } else {
743        resettables = el.find(".shinyjs-resettable");
744      }
745
746      // go through each input and record its id, type, and initial value
747      var messages = Object();
748      for (var i = 0; i < resettables.length; i++) {
749        var resettable = $(resettables[i]);
750        var type = resettable.attr('data-shinyjs-resettable-type');
751        var value = resettable.attr('data-shinyjs-resettable-value');
752        var id = resettable.attr('data-shinyjs-resettable-id');
753        if (id !== undefined) {
754          // file inputs need to be reset manually since shiny doesn't have an
755          // update function for them
756          if (type == "File") {
757            _jqid(id).val('');
758            _jqid(id + "_progress").css("visibility", "hidden");
759            _jqid(id + "_progress").find(".progress-bar").css("width", "0");
760            _jqid(id).closest(".input-group").find("input[type='text']").val('');
761          } else {
762            messages[id] = { 'type' : type, 'value' : value };
763          }
764        }
765      }
766
767      // send a message back to R with the info for each input element
768      Shiny.onInputChange(params.shinyInputId, messages);
769    },
770
771    // run an R function after an asynchronous delay
772    delay : function(params) {
773      var defaultParams = {
774        ms : null,
775        shinyInputId : null
776      }
777      params = shinyjs.getParams(params, defaultParams);
778
779      // send a message back to R when the delay is up
780      setTimeout(function() {
781        Shiny.onInputChange(params.shinyInputId, params.ms);
782      }, params.ms);
783    },
784
785    // click on a button
786    click : function(params) {
787      var defaultParams = {
788        id : null
789      };
790      params = shinyjs.getParams(params, defaultParams);
791
792      var $el = _getElements(params);
793      if ($el === null) return;
794      $el.click();
795    }
796  };
797}();
798
799// Initialize shinyjs on the JS side
800$(function() { shinyjs.initShinyjs(); });
801
802// ShinySenderQueue code taken from Joe Cheng
803// https://github.com/rstudio/shiny/issues/1476
804function ShinySenderQueue() {
805  this.readyToSend = true;
806  this.queue = [];
807  this.timer = null;
808}
809ShinySenderQueue.prototype.send = function(name, value) {
810  var self = this;
811  function go() {
812    self.timer = null;
813    if (self.queue.length) {
814      var msg = self.queue.shift();
815      Shiny.onInputChange(msg.name, msg.value);
816      self.timer = setTimeout(go, 0);
817    } else {
818      self.readyToSend = true;
819    }
820  }
821  if (this.readyToSend) {
822    this.readyToSend = false;
823    Shiny.onInputChange(name, value);
824    this.timer = setTimeout(go, 0);
825  } else {
826    this.queue.push({name: name, value: value});
827    if (!this.timer) {
828      this.timer = setTimeout(go, 0);
829    }
830  }
831};

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.