1 2// polyfill indexOf for IE8 3if (!Array.prototype.indexOf) { 4 Array.prototype.indexOf = function(elt /*, from*/) { 5 var len = this.length >>> 0; 6 7 var from = Number(arguments[1]) || 0; 8 from = (from < 0) 9 ? Math.ceil(from) 10 : Math.floor(from); 11 if (from < 0) 12 from += len; 13 14 for (; from < len; from++) { 15 if (from in this && 16 this[from] === elt) 17 return from; 18 } 19 return -1; 20 }; 21} 22 23HTMLWidgets.widget({ 24 25 name: "dygraphs", 26 27 type: "output", 28 29 factory: function(el, width, height) { 30 31 // reference to dygraph 32 var dygraph = null; 33 34 // reference to widget global groups 35 var groups = this.groups; 36 37 // add qt style if we are running under Qt 38 if (window.navigator.userAgent.indexOf(" Qt/") > 0) 39 el.className += " qt"; 40 41 return { 42 43 renderValue: function(x) { 44 45 // reference to this for closures 46 var thiz = this; 47 48 // get dygraph attrs and populate file field 49 var attrs = x.attrs; 50 attrs.file = x.data; 51 52 // disable zoom interaction except for clicks 53 if (attrs.disableZoom) { 54 attrs.interactionModel = Dygraph.Interaction.nonInteractiveModel_; 55 } 56 57 // convert non-arrays to arrays 58 for (var index = 0; index < attrs.file.length; index++) { 59 if (!$.isArray(attrs.file[index])) 60 attrs.file[index] = [].concat(attrs.file[index]); 61 } 62 63 // resolve "auto" legend behavior 64 if (x.attrs.legend == "auto") { 65 if (x.data.length <= 2) 66 x.attrs.legend = "onmouseover"; 67 else 68 x.attrs.legend = "always"; 69 } 70 71 if (x.format == "date") { 72 73 // set appropriated function in case of fixed tz 74 if ((attrs.axes.x.axisLabelFormatter === undefined) && x.fixedtz) 75 attrs.axes.x.axisLabelFormatter = this.xAxisLabelFormatterFixedTZ(x.tzone); 76 77 if ((attrs.axes.x.valueFormatter === undefined) && x.fixedtz) 78 attrs.axes.x.valueFormatter = this.xValueFormatterFixedTZ(x.scale, x.tzone); 79 80 if ((attrs.axes.x.ticker === undefined) && x.fixedtz) 81 attrs.axes.x.ticker = this.customDateTickerFixedTZ(x.tzone); 82 83 // provide an automatic x value formatter if none is already specified 84 if ((attrs.axes.x.valueFormatter === undefined) && (x.fixedtz != true)) 85 attrs.axes.x.valueFormatter = this.xValueFormatter(x.scale); 86 87 // convert time to js time 88 attrs.file[0] = attrs.file[0].map(function(value) { 89 return thiz.normalizeDateValue(x.scale, value, x.fixedtz); 90 }); 91 if (attrs.dateWindow != null) { 92 attrs.dateWindow = attrs.dateWindow.map(function(value) { 93 var date = thiz.normalizeDateValue(x.scale, value, x.fixedtz); 94 return date.getTime(); 95 }); 96 } 97 } 98 99 100 // transpose array 101 attrs.file = HTMLWidgets.transposeArray2D(attrs.file); 102 103 // add drawCallback for group 104 if (x.group != null) 105 this.addGroupDrawCallback(x); 106 107 // add shading and event callback if necessary 108 this.addShadingCallback(x); 109 this.addEventCallback(x); 110 this.addZoomCallback(x); 111 112 // disable y-axis touch events on mobile phones 113 if (attrs.mobileDisableYTouch !== false && this.isMobilePhone()) { 114 // create default interaction model if necessary 115 if (!attrs.interactionModel) 116 attrs.interactionModel = Dygraph.Interaction.defaultModel; 117 // disable y touch direction 118 attrs.interactionModel.touchstart = function(event, dygraph, context) { 119 Dygraph.defaultInteractionModel.touchstart(event, dygraph, context); 120 context.touchDirections = { x: true, y: false }; 121 }; 122 } 123 124 // create plugins 125 if (x.plugins) { 126 attrs.plugins = []; 127 for (var plugin in x.plugins) { 128 if (x.plugins.hasOwnProperty(plugin)) { 129 130 // get plugin options 131 var options = x.plugins[plugin]; 132 133 // create plugin and add to dygraph 134 var p = new Dygraph.Plugins[plugin](options); 135 attrs.plugins.push(p); 136 } 137 } 138 } 139 140 // custom plotter 141 if (x.plotter) { 142 attrs.plotter = Dygraph.Plotters[x.plotter]; 143 } 144 145 // custom data handler 146 if (x.dataHandler) { 147 attrs.dataHandler = Dygraph.DataHandlers[x.dataHandler]; 148 } 149 150 // custom circles 151 if (x.pointShape) { 152 if (typeof x.pointShape === 'string') { 153 attrs.drawPointCallback = Dygraph.Circles[x.pointShape.toUpperCase()]; 154 attrs.drawHighlightPointCallback = Dygraph.Circles[x.pointShape.toUpperCase()]; 155 } else { 156 for (var s in x.pointShape) { 157 if (x.pointShape.hasOwnProperty(s)) { 158 attrs.series[s].drawPointCallback = Dygraph.Circles[x.pointShape[s].toUpperCase()]; 159 attrs.series[s].drawHighlightPointCallback = Dygraph.Circles[x.pointShape[s].toUpperCase()]; 160 } 161 } 162 } 163 } 164 165 // if there is no existing dygraph perform initialization 166 if (!dygraph) { 167 168 // subscribe to custom shown event (fired by ioslides to trigger 169 // shiny reactivity but we can use it as well). this is necessary 170 // because if a dygraph starts out as display:none it has height 171 // and width == 0 and this doesn't change when it becomes visible 172 $(el).closest('slide').on('shown', function() { 173 if (dygraph) 174 dygraph.resize(); 175 }); 176 177 // do the same for reveal.js 178 $(el).closest('section.slide').on('shown', function() { 179 if (dygraph) 180 dygraph.resize(); 181 }); 182 183 // redraw on R Markdown {.tabset} tab visibility changed 184 var tab = $(el).closest('div.tab-pane'); 185 if (tab !== null) { 186 var tabID = tab.attr('id'); 187 var tabAnchor = $('a[data-toggle="tab"][href="#' + tabID + '"]'); 188 if (tabAnchor !== null) { 189 tabAnchor.on('shown.bs.tab', function() { 190 if (dygraph) 191 dygraph.resize(); 192 }); 193 } 194 } 195 // add default font for viewer mode 196 if (this.queryVar("viewer_pane") === "1") 197 document.body.style.fontFamily = "Arial, sans-serif"; 198 199 // inject css if necessary 200 if (x.css != null) { 201 var style = document.createElement('style'); 202 style.type = 'text/css'; 203 if (style.styleSheet) 204 style.styleSheet.cssText = x.css; 205 else 206 style.appendChild(document.createTextNode(x.css)); 207 document.getElementsByTagName("head")[0].appendChild(style); 208 } 209 210 } else { 211 212 // retain the userDateWindow if requested 213 if (dygraph.userDateWindow != null 214 && attrs.retainDateWindow == true) { 215 attrs.dateWindow = dygraph.xAxisRange(); 216 } 217 218 // remove it from groups if it's there 219 if (x.group != null && groups[x.group] != null) { 220 var index = groups[x.group].indexOf(dygraph); 221 if (index != -1) 222 groups[x.group].splice(index, 1); 223 } 224 225 // destroy the existing dygraph 226 dygraph.destroy(); 227 dygraph = null; 228 } 229 230 // create the dygraph and add it to it's group (if any) 231 dygraph = thiz.dygraph = new Dygraph(el, attrs.file, attrs); 232 dygraph.userDateWindow = attrs.dateWindow; 233 if (x.group != null) 234 groups[x.group].push(dygraph); 235 236 // add shiny inputs for date window and click 237 if (HTMLWidgets.shinyMode) { 238 var isDate = x.format == "date"; 239 this.addClickShinyInput(el.id, isDate); 240 this.addDateWindowShinyInput(el.id, isDate); 241 } 242 243 // set annotations 244 if (x.annotations != null) { 245 dygraph.ready(function() { 246 if (x.format == "date") { 247 x.annotations.map(function(annotation) { 248 var date = thiz.normalizeDateValue(x.scale, annotation.x, x.fixedtz); 249 annotation.x = date.getTime(); 250 }); 251 } 252 dygraph.setAnnotations(x.annotations); 253 }); 254 } 255 256 }, 257 258 customDateTickerFixedTZ : function(tz){ 259 return function(t,e,a,i,r) { 260 var a=Dygraph.pickDateTickGranularity(t,e,a,i); 261 if(a >= 0){ 262 263 var n=i("axisLabelFormatter"), 264 o=i("labelsUTC"), 265 s=o?Dygraph.DateAccessorsUTC:Dygraph.DateAccessorsLocal; 266 l=Dygraph.TICK_PLACEMENT[a].datefield; 267 h=Dygraph.TICK_PLACEMENT[a].step; 268 p=Dygraph.TICK_PLACEMENT[a].spacing; 269 270 var y = []; 271 var d = moment(t); 272 d.tz(tz); 273 d.millisecond(0); 274 275 if(l > Dygraph.DATEFIELD_M){ 276 var x; 277 if (l === Dygraph.DATEFIELD_SS) { // seconds 278 x = d.second(); 279 d.second(x - x % h); 280 } else if(l === Dygraph.DATEFIELD_MM){ 281 d.second(0) 282 x = d.minute(); 283 d.minute(x - x % h); 284 } else if(l === Dygraph.DATEFIELD_HH){ 285 d.second(0); 286 d.minute(0); 287 x = d.hour(); 288 d.hour(x - x % h); 289 } else if(l === Dygraph.DATEFIELD_D){ 290 d.second(0); 291 d.minute(0); 292 d.hour(0); 293 if (h == 7) { // one week 294 d.startOf('week'); 295 } 296 } 297 298 v = d.valueOf(); 299 _=moment(v).tz(tz); 300 301 // For spacings coarser than two-hourly, we want to ignore daylight 302 // savings transitions to get consistent ticks. For finer-grained ticks, 303 // it's essential to show the DST transition in all its messiness. 304 var start_offset_min = moment(v).tz(tz).zone(); 305 var check_dst = (p >= Dygraph.TICK_PLACEMENT[Dygraph.TWO_HOURLY].spacing); 306 307 if(a<=Dygraph.HOURLY){ 308 for(t>v&&(v+=p,_=moment(v).tz(tz));e>=v;){ 309 y.push({v:v,label:n(_,a,i,r)}); 310 v+=p; 311 _=moment(v).tz(tz); 312 } 313 }else{ 314 for(t>v&&(v+=p,_=moment(v).tz(tz));e>=v;){ 315 316 // This ensures that we stay on the same hourly "rhythm" across 317 // daylight savings transitions. Without this, the ticks could get off 318 // by an hour. See tests/daylight-savings.html or issue 147. 319 if (check_dst && _.zone() != start_offset_min) { 320 var delta_min = _.zone() - start_offset_min; 321 v += delta_min * 60 * 1000; 322 _= moment(v).tz(tz); 323 start_offset_min = _.zone(); 324 325 // Check whether we've backed into the previous timezone again. 326 // This can happen during a "spring forward" transition. In this case, 327 // it's best to skip this tick altogether (we may be shooting for a 328 // non-existent time like the 2AM that's skipped) and go to the next 329 // one. 330 if (moment(v + p).tz(tz).zone() != start_offset_min) { 331 v += p; 332 _= moment(v).tz(tz); 333 start_offset_min = _.zone(); 334 } 335 } 336 337 (a>=Dygraph.DAILY||_.get('hour')%h===0)&&y.push({v:v,label:n(_,a,i,r)}); 338 v+=p; 339 _=moment(v).tz(tz); 340 } 341 } 342 }else{ 343 var start_year = moment(t).tz(tz).year(); 344 var end_year = moment(e).tz(tz).year(); 345 var start_month = moment(t).tz(tz).month(); 346 347 if(l === Dygraph.DATEFIELD_M){ 348 var step_month = h; 349 for (var ii = start_year; ii <= end_year; ii++) { 350 for (var j = 0; j < 12;) { 351 var dt = moment(new Date(ii, j, 1)).tz(tz); 352 // fix some tz bug 353 dt.year(ii); 354 dt.month(j); 355 dt.date(1); 356 dt.hour(0); 357 v = dt.valueOf(); 358 y.push({v:v,label:n(moment(v).tz(tz),a,i,r)}); 359 j+=step_month; 360 } 361 } 362 }else{ 363 var step_year = h; 364 for (var ii = start_year; ii <= end_year;) { 365 var dt = moment(new Date(ii, 1, 1)).tz(tz); 366 // fix some tz bug 367 dt.year(ii); 368 dt.month(j); 369 dt.date(1); 370 dt.hour(0); 371 v = dt.valueOf(); 372 y.push({v:v,label:n(moment(v).tz(tz),a,i,r)}); 373 ii+=step_year; 374 } 375 } 376 } 377 return y; 378 }else{ 379 return []; 380 } 381 }; 382 }, 383 384 xAxisLabelFormatterFixedTZ : function(tz){ 385 386 return function dateAxisFormatter(date, granularity){ 387 var mmnt = moment(date).tz(tz); 388 if (granularity >= Dygraph.DECADAL){ 389 return mmnt.format('YYYY'); 390 }else{ 391 if(granularity >= Dygraph.MONTHLY){ 392 return mmnt.format('MMM YYYY'); 393 }else{ 394 var frac = mmnt.hour() * 3600 + mmnt.minute() * 60 + mmnt.second() + mmnt.millisecond(); 395 if (frac === 0 || granularity >= Dygraph.DAILY) { 396 return mmnt.format('DD MMM'); 397 } else { 398 if (mmnt.second()) { 399 return mmnt.format('HH:mm:ss'); 400 } else { 401 return mmnt.format('HH:mm'); 402 } 403 } 404 } 405 406 } 407 } 408 }, 409 410 xValueFormatterFixedTZ: function(scale, tz) { 411 412 return function(millis) { 413 var mmnt = moment(millis).tz(tz); 414 if (scale == "yearly") 415 return mmnt.format('YYYY') + ' (' + mmnt.zoneAbbr() + ')'; 416 else if (scale == "quarterly") 417 return mmnt.fquarter(1) + ' (' + mmnt.zoneAbbr() + ')'; 418 else if (scale == "monthly") 419 return mmnt.format('MMM, YYYY')+ ' (' + mmnt.zoneAbbr() + ')'; 420 else if (scale == "daily" || scale == "weekly") 421 return mmnt.format('MMM, DD, YYYY')+ ' (' + mmnt.zoneAbbr() + ')'; 422 else 423 return mmnt.format('dddd, MMMM DD, YYYY HH:mm:ss')+ ' (' + mmnt.zoneAbbr() + ')'; 424 } 425 }, 426 427 xValueFormatter: function(scale) { 428 429 var monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", 430 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; 431 432 return function(millis) { 433 var date = new Date(millis); 434 if (scale == "yearly") 435 return date.getFullYear(); 436 else if (scale == "quarterly") 437 return moment(millis).fquarter(1); 438 else if (scale == "monthly") 439 return monthNames[date.getMonth()] + ', ' + date.getFullYear(); 440 else if (scale == "daily" || scale == "weekly") 441 return monthNames[date.getMonth()] + ', ' + 442 date.getDate() + ', ' + 443 date.getFullYear(); 444 else
445 return date.toLocaleString(); 446 } 447 }, 448 449 addZoomCallback: function(x) { 450 451 // alias this 452 var thiz = this; 453 454 // get attrs 455 var attrs = x.attrs; 456 457 // check for an existing zoomCallback 458 var prevZoomCallback = attrs["zoomCallback"]; 459 460 attrs.zoomCallback = function(minDate, maxDate, yRanges) { 461 462 // call existing 463 if (prevZoomCallback) 464 prevZoomCallback(minDate, maxDate, yRanges); 465 466 // record user date window (or lack thereof) 467 if (dygraph.xAxisExtremes()[0] != minDate || 468 dygraph.xAxisExtremes()[1] != maxDate) { 469 dygraph.userDateWindow = [minDate, maxDate]; 470 } else { 471 dygraph.userDateWindow = null; 472 } 473 474 // record in group if necessary 475 if (x.group != null && groups[x.group] != null) { 476 var group = groups[x.group]; 477 for(var i = 0; i<group.length; i++) 478 group[i].userDateWindow = dygraph.userDateWindow; 479 } 480 }; 481 }, 482 483 addGroupDrawCallback: function(x) { 484 485 // get attrs 486 var attrs = x.attrs; 487 488 // check for an existing drawCallback 489 var prevDrawCallback = attrs["drawCallback"]; 490 491 groups[x.group] = groups[x.group] || []; 492 var group = groups[x.group]; 493 var blockRedraw = false; 494 attrs.drawCallback = function(me, initial) { 495 496 // call existing 497 if (prevDrawCallback) 498 prevDrawCallback(me, initial); 499 500 // sync peers in group 501 if (blockRedraw || initial) return; 502 blockRedraw = true; 503 var range = dygraph.xAxisRange(); 504 for (var j = 0; j < group.length; j++) { 505 if (group[j] == me) continue; 506 // update group range only if it's different (prevents 507 // infinite recursion in updateOptions) 508 var peerRange = group[j].xAxisRange(); 509 if (peerRange[0] != range[0] || peerRange[1] != range[1]) { 510 group[j].updateOptions({ 511 dateWindow: range 512 }); 513 } 514 } 515 blockRedraw = false; 516 }; 517 }, 518 519 addShadingCallback: function(x) { 520 521 // bail if no shadings 522 if (x.shadings.length == 0) 523 return; 524 525 // alias this 526 var thiz = this; 527 528 // get attrs 529 var attrs = x.attrs; 530 531 // check for an existing underlayCallback 532 var prevUnderlayCallback = attrs["underlayCallback"]; 533 534 // install callback 535 attrs.underlayCallback = function(canvas, area, g) { 536 537 // call existing 538 if (prevUnderlayCallback) 539 prevUnderlayCallback(canvas, area, g); 540 541 for (var i = 0; i < x.shadings.length; i++) { 542 var shading = x.shadings[i]; 543 canvas.save(); 544 canvas.fillStyle = shading.color; 545 if (shading.axis == "x") { 546 var x1 = shading.from; 547 var x2 = shading.to; 548 if (x.format == "date") { 549 x1 = thiz.normalizeDateValue(x.scale, x1, x.fixedtz).getTime(); 550 x2 = thiz.normalizeDateValue(x.scale, x2, x.fixedtz).getTime(); 551 } 552 var left = g.toDomXCoord(x1); 553 var right = g.toDomXCoord(x2); 554 555 canvas.fillRect(left, area.y, right - left, area.h); 556 } else if (shading.axis == "y") { 557 var bottom = g.toDomYCoord(shading.from); 558 var top = g.toDomYCoord(shading.to); 559 560 canvas.fillRect(area.x, bottom, area.w, top - bottom); 561 } 562 canvas.restore(); 563 } 564 }; 565 }, 566 567 addEventCallback: function(x) { 568 569 // bail if no evets 570 if (x.events.length == 0) 571 return; 572 573 // alias this 574 var thiz = this; 575 576 // get attrs 577 var attrs = x.attrs; 578 579 // check for an existing underlayCallback 580 var prevUnderlayCallback = attrs["underlayCallback"]; 581 582 // install callback 583 attrs.underlayCallback = function(canvas, area, g) { 584 585 // call existing 586 if (prevUnderlayCallback) 587 prevUnderlayCallback(canvas, area, g); 588 589 for (var i = 0; i < x.events.length; i++) { 590 591 // get event and x-coordinate 592 var event = x.events[i]; 593 594 // draw line 595 canvas.save(); 596 canvas.strokeStyle = event.color; 597 if (event.axis == "x") { 598 var xPos; 599 if (jQuery.isNumeric(event.pos)) { 600 xPos = g.toDomXCoord(event.pos); 601 } else { 602 xPos = thiz.normalizeDateValue(x.scale, event.pos, x.fixedtz).getTime(); 603 xPos = g.toDomXCoord(xPos); 604 } 605 606 // draw line 607 thiz.dashedLine(canvas, 608 xPos, 609 area.y, 610 xPos, 611 area.y + area.h, 612 event.strokePattern); 613 } else if (event.axis == "y") { 614 yPos = g.toDomYCoord(event.pos); 615 616 thiz.dashedLine(canvas, 617 area.x, 618 yPos, 619 area.x + area.w, 620 yPos, 621 event.strokePattern); 622 }
623 canvas.restore(); 624 625 // draw label 626 if (event.label != null) { 627 canvas.save(); 628 thiz.setFontSize(canvas, 12); 629 var size = canvas.measureText(event.label); 630 if (event.axis == "x") { 631 var tx = xPos - 4; 632 var ty; 633 if (event.labelLoc == "top") 634 ty = area.y + size.width + 10; 635 else 636 ty = area.y + area.h - 10; 637 canvas.translate(tx, ty); 638 canvas.rotate(3 * Math.PI / 2); 639 canvas.translate(-tx,-ty); 640 } else if (event.axis == "y") { 641 var ty = yPos - 4; 642 var tx; 643 if (event.labelLoc == "right") 644 tx = area.x + area.w - size.width - 10; 645 else 646 tx = area.x + 10; 647 } 648 canvas.fillStyle = event.color; 649 canvas.fillText(event.label, tx, ty); 650 canvas.restore(); 651 } 652 } 653 }; 654 }, 655 656 addDateWindowShinyInput: function(id, isDate) { 657 658 // check for an existing drawCallback 659 var prevDrawCallback = dygraph.getOption("drawCallback"); 660 661 // install the callback 662 dygraph.updateOptions({ 663 drawCallback: function(me, initial) { 664 // call existing 665 if (prevDrawCallback) 666 prevDrawCallback(me, initial); 667 // fire input change 668 var range = dygraph.xAxisRange(); 669 if (isDate) 670 range = [new Date(range[0]), new Date(range[1])]; 671 if (Shiny.onInputChange) // may note be ready yet in case of static render 672 Shiny.onInputChange(id + "_date_window", range); 673 } 674 }); 675 }, 676 677 addClickShinyInput: function(id, isDate) { 678 679 var prevClickCallback = dygraph.getOption("clickCallback") 680 681 dygraph.updateOptions({ 682 clickCallback: function(e, x, points) { 683 684 // call existing 685 if (prevClickCallback) 686 prevClickCallback(e, x, points); 687 688 // fire input change 689 if (Shiny.onInputChange) { // may note be ready yet in case of static render 690 Shiny.onInputChange(el.id + "_click", { 691 x: isDate ? new Date(x) : x, 692 x_closest_point: isDate ? new Date(points[0].xval) : points[0].xval, 693 y_closest_point: points[0].yval, 694 series_name: points[0].name, 695 '.nonce': Math.random() // Force reactivity if click hasn't changed 696 }); 697 } 698 } 699 }); 700 }, 701 702 // Add dashed line support to canvas rendering context 703 // See: http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas 704 dashedLine: function(canvas, x, y, x2, y2, dashArray) { 705 canvas.beginPath(); 706 if (!dashArray) dashArray=[10,5]; 707 if (dashLength==0) dashLength = 0.001; // Hack for Safari 708 var dashCount = dashArray.length; 709 canvas.moveTo(x, y); 710 var dx = (x2-x), dy = (y2-y); 711 var slope = dx ? dy/dx : 1e15; 712 var distRemaining = Math.sqrt( dx*dx + dy*dy ); 713 var dashIndex=0, draw=true; 714 while (distRemaining>=0.1){ 715 var dashLength = dashArray[dashIndex++%dashCount]; 716 if (dashLength > distRemaining) dashLength = distRemaining; 717 var xStep = Math.sqrt( dashLength*dashLength / (1 + slope*slope) ); 718 if (dx<0) xStep = -xStep; 719 x += xStep 720 y += slope*xStep; 721 canvas[draw ? 'lineTo' : 'moveTo'](x,y); 722 distRemaining -= dashLength; 723 draw = !draw; 724 } 725 canvas.stroke(); 726 }, 727 728 setFontSize: function(canvas, size) { 729 var cFont = canvas.font; 730 var parts = cFont.split(' '); 731 if (parts.length === 2) 732 canvas.font = size + 'px ' + parts[1]; 733 else if (parts.length === 3) 734 canvas.font = parts[0] + ' ' + size + 'px ' + parts[2]; 735 }, 736 737 // Returns the value of a GET variable 738 queryVar: function(name) { 739 return decodeURI(window.location.search.replace( 740 new RegExp("^(?:.*[&\\?]" + 741 encodeURI(name).replace(/[\.\+\*]/g, "\\$&") + 742 "(?:\\=([^&]*))?)?.*$", "i"), 743 "$1")); 744 }, 745 746 // We deal exclusively in UTC dates within R, however dygraphs deal
746s 747 // exclusively in the local time zone. Therefore, in order to plot date 748 // labels that make sense to the user when we are dealing with days, 749 // months or years we need to convert the UTC date value to a local time 750 // value that "looks like" the equivilant UTC value. To do this we add the 751 // timezone offset to the UTC date. 752 // Don't use in case of fixedtz 753 normalizeDateValue: function(scale, value, fixedtz) { 754 var date = new Date(value); 755 if (scale != "minute" && scale != "hourly" && scale != "seconds" && !fixedtz) { 756 var localAsUTC = date.getTime() + (date.getTimezoneOffset() * 60000); 757 date = new Date(localAsUTC); 758 } 759 return date; 760 }, 761 762 // safely detect rendering on a mobile phone 763 isMobilePhone: function() { 764 try 765 { 766 return ! window.matchMedia("only screen and (min-width: 768px)").matches; 767 } 768 catch(e) { 769 return false; 770 } 771 }, 772 773 774 resize: function(width, height) { 775 if (dygraph) 776 dygraph.resize(); 777 }, 778 779 // export dygraph so other code can get a hold of it 780 dygraph: null 781 782 }; 783 }, 784 785 // track groups globally 786 groups: {} 787 788}); 789
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.