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.