Please review changes against upstream code using SCM,
see the Vcs-* tags in debian/control for its location.

--- dygraphs-2.2.1.orig/.github/workflows/build.yml
+++ dygraphs-2.2.1/.github/workflows/build.yml
@@ -10,20 +10,30 @@
         "url": "${{ steps.deployment.outputs.page_url }}"
       },
       "if": "github.repository == 'mirabilos/dygraphs'",
-      "runs-on": "ubuntu-latest",
+      "runs-on": "ubuntu-24.04",
       "steps": [
         {
-          "uses": "actions/checkout@v3.3.0"
+          "uses": "actions/checkout@v4.2.2"
         },
         {
           "run": "./.pages.sh"
         },
         {
-          "uses": "actions/upload-pages-artifact@v1.0.7"
+          "uses": "actions/upload-artifact@v4.6.0",
+          "with": {
+            "name": "package-lock.json",
+            "path": "package-lock.json"
+          }
+        },
+        {
+          "uses": "actions/upload-pages-artifact@v3.0.1"
+        },
+        {
+          "run": "./.post.sh"
         },
         {
           "id": "deployment",
-          "uses": "actions/deploy-pages@v1.2.3"
+          "uses": "actions/deploy-pages@v4.0.5"
         }
       ]
     },
@@ -33,16 +43,26 @@
         "url": "${{ steps.deployment.outputs.page_url }}"
       },
       "if": "github.repository == 'danvk/dygraphs'",
-      "runs-on": "ubuntu-latest",
+      "runs-on": "ubuntu-24.04",
       "steps": [
         {
-          "uses": "actions/checkout@v3.3.0"
+          "uses": "actions/checkout@v4.2.2"
         },
         {
           "run": "./.pages.sh"
         },
         {
-          "uses": "actions/upload-pages-artifact@v1.0.7"
+          "uses": "actions/upload-artifact@v4.6.0",
+          "with": {
+            "name": "package-lock.json",
+            "path": "package-lock.json"
+          }
+        },
+        {
+          "uses": "actions/upload-pages-artifact@v3.0.1"
+        },
+        {
+          "run": "./.post.sh"
         }
       ]
     }
--- dygraphs-2.2.1.orig/.github/workflows/gha-update.yml
+++ dygraphs-2.2.1/.github/workflows/gha-update.yml
@@ -4,13 +4,13 @@
       "runs-on": "ubuntu-latest",
       "steps": [
         {
-          "uses": "actions/checkout@v3.3.0",
+          "uses": "actions/checkout@v4.2.2",
           "with": {
             "token": "${{ secrets.WORKFLOW_SECRET }}"
           }
         },
         {
-          "uses": "saadmk11/github-actions-version-updater@v0.7.3",
+          "uses": "saadmk11/github-actions-version-updater@v0.8.1",
           "with": {
             "pull_request_user_reviewers": "mirabilos",
             "token": "${{ secrets.WORKFLOW_SECRET }}"
--- dygraphs-2.2.1.orig/.pages.sh
+++ dygraphs-2.2.1/.pages.sh
@@ -22,13 +22,16 @@ export LC_ALL=C
 unset LANGUAGE
 
 set -o pipefail
+rm -f .post.state
+if test -h .post.state || test -e .post.state; then exit 255; fi
+state=''
 
 $sudoagi eatmydata
 sudoapt clean
 sudoapt update
 #sudoapt --purge dist-upgrade -y
 $sudoagi eatmydata git \
-    ed jq jsdoc-toolkit \
+    ed jq node-jsdoc2 \
     libjs-bootstrap libjs-jquery libjs-jquery-ui \
     mksh pax python3
 
@@ -40,12 +43,12 @@ eatmydata env TMPDIR=/tmp npm install
 
 (eatmydata npm run clean || :)
 eatmydata npm run build
-eatmydata npm run test
-eatmydata npm run test-min
+eatmydata npm run test || state="$state test"
+eatmydata npm run test-min || state="$state test-min"
 if [[ $GITHUB_REPOSITORY = danvk/dygraphs ]]; then
-	eatmydata npm run coverage
-	eatmydata scripts/post-coverage.sh
-	eatmydata scripts/weigh-in.sh
+	eatmydata npm run coverage || state="$state coverage"
+	eatmydata scripts/post-coverage.sh || state="$state post-coverage"
+	eatmydata scripts/weigh-in.sh || state="$state weigh-in"
 fi
 
 rm -rf _site
@@ -65,3 +68,5 @@ if [[ -n $imprint_text ]]; then
 	    '
 fi
 cd ..
+set -o noglob
+echo $state >.post.state
--- /dev/null
+++ dygraphs-2.2.1/.post.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+echo ::group::Check whether .pages.sh failed anywhere
+state=$(cat .post.state || echo state-file-missing)
+if test -n "$state"; then
+	echo "E: .pages.sh failed in: $state"
+	exit 1
+fi
+echo "I: nope, ok"
+echo ::endgroup::
--- dygraphs-2.2.1.orig/CHANGES.md
+++ dygraphs-2.2.1/CHANGES.md
@@ -1,3 +1,29 @@
+# next (git)
+
+## Breaking changes
+- …
+
+## New features
+- Define `Dygraph.integerTicks` (#925)
+- Warn in console if points are out of range (#1050)
+- …
+
+## Bugfixes
+- Re-add missing `Dygraph.DOTTED_LINE` export
+- Some edge cases in the crosshair plugin (#1034, #1038, #1039, #1040)
+- Update list of exported symbols
+- …
+
+## Other user-visible changes
+- Add talk link to `tutorial.html`
+- …
+
+## Internal refactors/fixes
+- Switch from `jsdoc-toolkit` to `node-jsdoc2` (Debian #1074595)
+- Bump some package versions matching Debian bookworm and \*buntu 24.04
+- Add support for newer babeljs and uglifyjs3
+- …
+
 # v2.2.1 (2023-02-16)
 
 ## Future incompatibilities
--- dygraphs-2.2.1.orig/DEVELOP.md
+++ dygraphs-2.2.1/DEVELOP.md
@@ -26,7 +26,7 @@ To run the tests, run:
 The prerequisites for a full “npm run build” are:
 
     # for building
-    apt-get install ed jsdoc-toolkit mksh pax python3
+    apt-get install ed node-jsdoc2 mksh pax python3
     # for docs
     apt-get install libjs-bootstrap libjs-jquery libjs-jquery-ui
 
--- dygraphs-2.2.1.orig/LICENSE.txt
+++ dygraphs-2.2.1/LICENSE.txt
@@ -10,6 +10,7 @@ Copyright (c) 2014 mirabilos <m@mirbsd.o
 Copyright (c) 2015 Petr Shevtsov <petr.shevtsov@gmail.com>
 Copyright (c) 2022, 2023 mirabilos <t.glaser@tarent.de>
                    Deutsche Telekom LLCTO
+Copyright (c) 2025 mirabilos <tg@debian.org>
 and numerous contributors (see git log)
 
 Some tests additionally are:
@@ -33,6 +34,9 @@ Parts of the documentation are or make u
 Copyright (c) 2012 Google, Inc.
 - Robert Konigsberg <konigsberg@google.com>
 
+Further contributors can be found in the git history or on:
+https://github.com/danvk/dygraphs/graphs/contributors
+
 The automatically added browser-pack shim is:
 
 Copyright (c) 2013, 2014 James Halliday <mail@substack.net>
--- dygraphs-2.2.1.orig/auto_tests/tests/PixelSampler.js
+++ dygraphs-2.2.1/auto_tests/tests/PixelSampler.js
@@ -1,3 +1,5 @@
+'use strict';
+
 // Copyright 2012 Google Inc. All Rights Reserved.
 
 /**
@@ -7,8 +9,6 @@
  * @license MIT
  */
 
-'use strict';
-
 /**
  * @constructor
  */
--- dygraphs-2.2.1.orig/common/textarea.js
+++ dygraphs-2.2.1/common/textarea.js
@@ -1,3 +1,5 @@
+'use strict';
+
 // Copyright (c) 2012 Google, Inc.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -23,7 +25,6 @@
  *
  * @author konigsberg@google.com (Robert Konigsberg)
  */
-"use strict";
 
 function TextArea(parent) {
   var body = document.getElementsByTagName("body")[0];
--- dygraphs-2.2.1.orig/docs/tutorial.html
+++ dygraphs-2.2.1/docs/tutorial.html
@@ -449,4 +449,9 @@ perl -ne 'BEGIN{print "Month,Nominal,Rea
 
 <p>To get some inspiration, look at how the <a href="gallery/">charts in our gallery</a> are built.</p>
 
+<p>The original author Dan Vanderkam gave a <a
+ href="https://www.youtube.com/watch?v=I6E5e1HBFi0">42-minute talk
+ about dygraphs</a> in 2017 which might be helpful background for
+ anyone interested in using or contributing to the project.</p>
+
 <!--#include virtual="footer.html" -->
--- dygraphs-2.2.1.orig/dygraph-exports.js
+++ dygraphs-2.2.1/dygraph-exports.js
@@ -6,18 +6,30 @@
 
 goog.exportSymbol('Dygraph', Dygraph);
 
+goog.exportSymbol('Dygraph.prototype.addAndTrackEvent', Dygraph.prototype.addAndTrackEvent);
 goog.exportSymbol('Dygraph.prototype.adjustRoll', Dygraph.prototype.adjustRoll);
 goog.exportSymbol('Dygraph.prototype.annotations', Dygraph.prototype.annotations);
+goog.exportSymbol('Dygraph.prototype.axisPropertiesForSeries', Dygraph.prototype.axisPropertiesForSeries);
 goog.exportSymbol('Dygraph.prototype.clearSelection', Dygraph.prototype.clearSelection);
 goog.exportSymbol('Dygraph.prototype.destroy', Dygraph.prototype.destroy);
+goog.exportSymbol('Dygraph.prototype.doAnimatedZoom', Dygraph.prototype.doAnimatedZoom);
 goog.exportSymbol('Dygraph.prototype.eventToDomCoords', Dygraph.prototype.eventToDomCoords);
+goog.exportSymbol('Dygraph.prototype.findClosestPoint', Dygraph.prototype.findClosestPoint);
+goog.exportSymbol('Dygraph.prototype.findClosestRow', Dygraph.prototype.findClosestRow);
+goog.exportSymbol('Dygraph.prototype.findStackedPoint', Dygraph.prototype.findStackedPoint);
 goog.exportSymbol('Dygraph.prototype.getArea', Dygraph.prototype.getArea);
+goog.exportSymbol('Dygraph.prototype.getBooleanOption', Dygraph.prototype.getBooleanOption);
 goog.exportSymbol('Dygraph.prototype.getColors', Dygraph.prototype.getColors);
+goog.exportSymbol('Dygraph.prototype.getFunctionOption', Dygraph.prototype.getFunctionOption);
 goog.exportSymbol('Dygraph.prototype.getHighlightSeries', Dygraph.prototype.getHighlightSeries);
 goog.exportSymbol('Dygraph.prototype.getLabels', Dygraph.prototype.getLabels);
+goog.exportSymbol('Dygraph.prototype.getNumericOption', Dygraph.prototype.getNumericOption);
 goog.exportSymbol('Dygraph.prototype.getOption', Dygraph.prototype.getOption);
+goog.exportSymbol('Dygraph.prototype.getOptionForAxis', Dygraph.prototype.getOptionForAxis);
 goog.exportSymbol('Dygraph.prototype.getPropertiesForSeries', Dygraph.prototype.getPropertiesForSeries);
+goog.exportSymbol('Dygraph.prototype.getRowForX', Dygraph.prototype.getRowForX);
 goog.exportSymbol('Dygraph.prototype.getSelection', Dygraph.prototype.getSelection);
+goog.exportSymbol('Dygraph.prototype.getStringOption', Dygraph.prototype.getStringOption);
 goog.exportSymbol('Dygraph.prototype.getValue', Dygraph.prototype.getValue);
 goog.exportSymbol('Dygraph.prototype.indexFromSetName', Dygraph.prototype.indexFromSetName);
 goog.exportSymbol('Dygraph.prototype.isSeriesLocked', Dygraph.prototype.isSeriesLocked);
@@ -32,6 +44,7 @@ goog.exportSymbol('Dygraph.prototype.rol
 goog.exportSymbol('Dygraph.prototype.setAnnotations', Dygraph.prototype.setAnnotations);
 goog.exportSymbol('Dygraph.prototype.setSelection', Dygraph.prototype.setSelection);
 goog.exportSymbol('Dygraph.prototype.setVisibility', Dygraph.prototype.setVisibility);
+goog.exportSymbol('Dygraph.prototype.size', Dygraph.prototype.size);
 goog.exportSymbol('Dygraph.prototype.toDataCoords', Dygraph.prototype.toDataCoords);
 goog.exportSymbol('Dygraph.prototype.toDataXCoord', Dygraph.prototype.toDataXCoord);
 goog.exportSymbol('Dygraph.prototype.toDataYCoord', Dygraph.prototype.toDataYCoord);
@@ -45,6 +58,7 @@ goog.exportSymbol('Dygraph.prototype.upd
 goog.exportSymbol('Dygraph.prototype.visibility', Dygraph.prototype.visibility);
 goog.exportSymbol('Dygraph.prototype.xAxisExtremes', Dygraph.prototype.xAxisExtremes);
 goog.exportSymbol('Dygraph.prototype.xAxisRange', Dygraph.prototype.xAxisRange);
+goog.exportSymbol('Dygraph.prototype.yAxisExtremes', Dygraph.prototype.yAxisExtremes);
 goog.exportSymbol('Dygraph.prototype.yAxisRange', Dygraph.prototype.yAxisRange);
 goog.exportSymbol('Dygraph.prototype.yAxisRanges', Dygraph.prototype.yAxisRanges);
 
--- dygraphs-2.2.1.orig/package.json
+++ dygraphs-2.2.1/package.json
@@ -32,10 +32,10 @@
   },
   "homepage": "https://github.com/danvk/dygraphs",
   "devDependencies": {
-    "@babel/cli": "^7.19.3",
-    "@babel/core": "^7.20.2",
-    "@babel/plugin-transform-strict-mode": "^7.18.6",
-    "@babel/preset-env": "^7.20.2",
+    "@babel/cli": "^7.20.15",
+    "@babel/core": ">=7.20.15 <7.21.4 || ^7.21.5",
+    "@babel/plugin-transform-strict-mode": "^7.20.15",
+    "@babel/preset-env": "^7.20.15",
     "babel-plugin-add-module-exports": "^1.0.4",
     "browser-pack": "^6.1.0",
     "browserify": "^17.0.0",
@@ -58,7 +58,7 @@
     "phantomjs-function-bind-polyfill": "^1.0.0",
     "pre-commit": "^1.0.6",
     "source-map": "^0.6.1",
-    "uglify-js": "^3.17.3",
+    "uglify-js": "^3.17.4",
     "watchify": "^4.0.0"
   },
   "scripts": {
--- dygraphs-2.2.1.orig/releases.json
+++ dygraphs-2.2.1/releases.json
@@ -5,6 +5,14 @@
       "dygraph.js",
       "dygraph.css"
     ],
+    "version": "2.2.1"
+  },
+  {
+    "files": [
+      "dygraph.min.js",
+      "dygraph.js",
+      "dygraph.css"
+    ],
     "version": "2.2.0"
   },
   {
--- dygraphs-2.2.1.orig/scripts/build-docs.sh
+++ dygraphs-2.2.1/scripts/build-docs.sh
@@ -7,8 +7,9 @@ test -n "$v" || {
   exit 1
 }
 
-if [[ -d debian ]]; then
-  dv=$v
+if [[ -n $IS_ACTUAL_DEBIAN_BUILD ]]; then
+  dv=$(dpkg-parsechangelog -S Version)
+  dv="$dv ($v)"
 else
   dv=
 fi
--- dygraphs-2.2.1.orig/scripts/build-js.sh
+++ dygraphs-2.2.1/scripts/build-js.sh
@@ -109,7 +109,12 @@ browserify \
 rm -rf src
 ../scripts/smap-out.py dygraph.min.tmp.js /dev/null dygraph.min.tmp.js.map
 
+uglifyjs=$(uglifyjs --help 2>&1)
+set -A compatopts -- --no-module --v8 --webkit
+[[ $uglifyjs = *--no-module* ]] || unset compatopts[0]
+
 uglifyjs \
+    "${compatopts[@]}" \
     --compress \
     --mangle \
     --output-opts "preamble='$header'" \
--- dygraphs-2.2.1.orig/scripts/build.sh
+++ dygraphs-2.2.1/scripts/build.sh
@@ -13,7 +13,7 @@ mkdir site
 cd docroot
 pax -rw . ../site/
 rm ../site/.jslibs/* ../site/LICENSE.txt ../site/dist
-cp -L .jslibs/* ../site/.jslibs/
+test -n "$IS_ACTUAL_DEBIAN_BUILD" || cp -L .jslibs/* ../site/.jslibs/
 cd ..
 pax -rw LICENSE.txt dist site/
 rm -f site/dist/tests.js
--- dygraphs-2.2.1.orig/scripts/generate-coverage.sh
+++ dygraphs-2.2.1/scripts/generate-coverage.sh
@@ -54,7 +54,7 @@ phantomjs \
   http://localhost:8082/auto_tests/coverage.html \
   spec '{"hooks": "mocha-phantomjs-istanbul", "coverageFile": "coverage/coverage.json"}'
 
-if [ $CI ]; then
+if test -n "$CI"; then
   # Convert the JSON coverage to LCOV for coveralls.
   istanbul report --include coverage/*.json lcovonly
 
--- dygraphs-2.2.1.orig/scripts/generate-jsdoc.sh
+++ dygraphs-2.2.1/scripts/generate-jsdoc.sh
@@ -10,12 +10,20 @@ test -n "$v" || {
   exit 1
 }
 
+if [[ -n $IS_ACTUAL_DEBIAN_BUILD ]]; then
+  dv=$(dpkg-parsechangelog -S Version)
+  v="$v (Debian $dv)"
+elif [[ $GITHUB_REF = refs/heads/debian && $GITHUB_REPOSITORY = mirabilos/dygraphs ]]; then
+  v="$v+WIP"
+fi
+
 rm -rf jsdoc jsdoc.tmp
 mkdir jsdoc.tmp
 t=$PWD/jsdoc.tmp
-(cd /usr/share/jsdoc-toolkit/templates/jsdoc && pax -rw . "$t/")
+(cd /usr/share/nodejs/jsdoc2/templates/jsdoc && pax -rw . "$t/")
 find jsdoc.tmp -type f -print0 | xargs -0r perl -pi -e \
-  "s! on [{][+]new Date[(][)][+][}]! for dygraph $v!g"
+  "s/\r+$//;" -e \
+  "s! on [{][+]new Date[(][)][+][}]! for dygraph $v!g;"
 
 echo Generating JSDoc...
 srcfiles=src/dygraph.js
@@ -28,7 +36,6 @@ srcfiles+=\ src/dygraph-internal.externs
 srcfiles+=\ src/dygraph-layout.js
 srcfiles+=\ src/dygraph-options-reference.js
 srcfiles+=\ src/dygraph-options.js
-srcfiles+=\ src/dygraph-plugin-install.js
 srcfiles+=\ src/dygraph-tickers.js
 srcfiles+=\ src/dygraph-types.js
 #srcfiles+=\ src/dygraph-utils.js
@@ -53,17 +60,12 @@ srcfiles+=\ src/plugins/grid.js
 #srcfiles+=\ src/extras/super-annotations.js
 #srcfiles+=\ src/extras/synchronizer.js
 #srcfiles+=\ src/extras/unzoom.js
-jsdoc \
+jsdoc2 \
   -t="$t" \
   -d=jsdoc \
   $srcfiles \
 2>&1 | tee jsdoc.tmp/.errs
 
-ed -s jsdoc.tmp/.errs <<-\EOF
-	1g/java .*jsrun.jar/d
-	w
-	q
-EOF
 if test -s jsdoc.tmp/.errs; then errs=true; else errs=false; fi
 rm -rf jsdoc.tmp
 
--- dygraphs-2.2.1.orig/scripts/post-coverage.sh
+++ dygraphs-2.2.1/scripts/post-coverage.sh
@@ -4,8 +4,9 @@
 # comment the line out to post
 #exit 0
 
-if [ $CI ]; then
-  <coverage/lcov.info ./node_modules/.bin/coveralls
+if test -n "$CI"; then
+  <coverage/lcov.info ./node_modules/.bin/coveralls || \
+    echo "N: above errors posting to coveralls.io are ignored"
 fi
 
 true  # reset exit code -- failure to post coverage shouldn't be an error.
--- dygraphs-2.2.1.orig/src/datahandler/bars-custom.js
+++ dygraphs-2.2.1/src/datahandler/bars-custom.js
@@ -1,3 +1,5 @@
+'use strict';
+
 /**
  * @license
  * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
@@ -10,7 +12,6 @@
  */
 
 /*global Dygraph:false */
-"use strict";
 
 import BarsHandler from './bars';
 
--- dygraphs-2.2.1.orig/src/datahandler/bars-error.js
+++ dygraphs-2.2.1/src/datahandler/bars-error.js
@@ -1,3 +1,5 @@
+'use strict';
+
 /**
  * @license
  * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
@@ -10,7 +12,6 @@
  */
 
 /*global Dygraph:false */
-"use strict";
 
 import BarsHandler from './bars';
 
--- dygraphs-2.2.1.orig/src/datahandler/bars-fractions.js
+++ dygraphs-2.2.1/src/datahandler/bars-fractions.js
@@ -1,3 +1,5 @@
+'use strict';
+
 /**
  * @license
  * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
@@ -11,7 +13,6 @@
  */
 
 /*global Dygraph:false */
-"use strict";
 
 import BarsHandler from './bars';
 
--- dygraphs-2.2.1.orig/src/datahandler/bars.js
+++ dygraphs-2.2.1/src/datahandler/bars.js
@@ -1,3 +1,5 @@
+'use strict';
+
 /**
  * @license
  * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
@@ -13,7 +15,6 @@
 
 /*global Dygraph:false */
 /*global DygraphLayout:false */
-"use strict";
 
 import DygraphDataHandler from './datahandler';
 import DygraphLayout from '../dygraph-layout';
--- dygraphs-2.2.1.orig/src/datahandler/datahandler.js
+++ dygraphs-2.2.1/src/datahandler/datahandler.js
@@ -1,3 +1,5 @@
+'use strict';
+
 /**
  * @license
  * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
@@ -41,8 +43,6 @@
 /*global Dygraph:false */
 /*global DygraphLayout:false */
 
-"use strict";
-
 /**
  *
  * The data handler is responsible for all data specific operations. All of the
--- dygraphs-2.2.1.orig/src/datahandler/default-fractions.js
+++ dygraphs-2.2.1/src/datahandler/default-fractions.js
@@ -1,3 +1,5 @@
+'use strict';
+
 /**
  * @license
  * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
@@ -10,7 +12,6 @@
  */
 
 /*global Dygraph:false */
-"use strict";
 
 import DygraphDataHandler from './datahandler';
 import DefaultHandler from './default';
--- dygraphs-2.2.1.orig/src/datahandler/default.js
+++ dygraphs-2.2.1/src/datahandler/default.js
@@ -1,3 +1,5 @@
+'use strict';
+
 /**
  * @license
  * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
@@ -10,7 +12,6 @@
  */
 
 /*global Dygraph:false */
-"use strict";
 
 import DygraphDataHandler from './datahandler';
 
--- dygraphs-2.2.1.orig/src/dygraph-canvas.js
+++ dygraphs-2.2.1/src/dygraph-canvas.js
@@ -1,3 +1,5 @@
+'use strict';
+
 /**
  * @license
  * Copyright 2006 Dan Vanderkam (danvdk@gmail.com)
@@ -25,7 +27,6 @@
  */
 
 /*global Dygraph:false */
-"use strict";
 
 import * as utils from './dygraph-utils';
 import Dygraph from './dygraph';
--- dygraphs-2.2.1.orig/src/dygraph-default-attrs.js
+++ dygraphs-2.2.1/src/dygraph-default-attrs.js
@@ -1,4 +1,4 @@
-'use strict'
+'use strict';
 
 import * as DygraphTickers from './dygraph-tickers';
 import DygraphInteraction from './dygraph-interaction-model';
--- dygraphs-2.2.1.orig/src/dygraph-gviz.js
+++ dygraphs-2.2.1/src/dygraph-gviz.js
@@ -1,3 +1,5 @@
+'use strict';
+
 /**
  * @license
  * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)
@@ -18,7 +20,6 @@
  */
 
 /*global Dygraph:false */
-"use strict";
 
 import Dygraph from './dygraph';
 
--- dygraphs-2.2.1.orig/src/dygraph-interaction-model.js
+++ dygraphs-2.2.1/src/dygraph-interaction-model.js
@@ -1,3 +1,5 @@
+'use strict';
+
 /**
  * @license
  * Copyright 2011 Robert Konigsberg (konigsberg@google.com)
@@ -11,7 +13,6 @@
  */
 
 /*global Dygraph:false */
-"use strict";
 
 import * as utils from './dygraph-utils';
 
--- dygraphs-2.2.1.orig/src/dygraph-layout.js
+++ dygraphs-2.2.1/src/dygraph-layout.js
@@ -1,3 +1,5 @@
+'use strict';
+
 /**
  * @license
  * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)
@@ -10,7 +12,6 @@
  */
 
 /*global Dygraph:false */
-"use strict";
 
 import * as utils from './dygraph-utils';
 
@@ -247,12 +248,14 @@ DygraphLayout.prototype._evaluateLineCha
     var axis = this.dygraph_.axisPropertiesForSeries(setName);
     // TODO (konigsberg): use optionsForAxis instead.
     var logscale = this.dygraph_.attributes_.getForSeries("logscale", setName);
+    var outOfXBounds = 0, outOfYBounds = 0;
 
     for (var j = 0; j < points.length; j++) {
       var point = points[j];
 
       // Range from 0-1 where 0 represents left and 1 represents right.
       point.x = DygraphLayout.calcXNormal_(point.xval, this._xAxis, isLogscaleForX);
+      outOfXBounds += (point.x < 0) || (point.x > 1);
       // Range from 0-1 where 0 represents top and 1 represents bottom
       var yval = point.yval;
       if (isStacked) {
@@ -269,6 +272,14 @@ DygraphLayout.prototype._evaluateLineCha
         }
       }
       point.y = DygraphLayout.calcYNormal_(axis, yval, logscale);
+      outOfYBounds += (point.y < 0) || (point.y > 1);
+    }
+
+    if (outOfXBounds > 2) {
+      console.warn(outOfXBounds + ' points out of X bounds:' + this._xAxis.minval + ' - ' + this._xAxis.maxval);
+    }
+    if (outOfYBounds > 0) {
+      console.warn(outOfYBounds + ' points out of Y bounds:' + axis.minyval + ' - ' + axis.maxyval);
     }
 
     this.dygraph_.dataHandler_.onLineEvaluated(points, axis, logscale);
--- dygraphs-2.2.1.orig/src/dygraph-options-reference.js
+++ dygraphs-2.2.1/src/dygraph-options-reference.js
@@ -1,11 +1,11 @@
+'use strict';
+
 /**
  * @license
  * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)
  * MIT-licenced: https://opensource.org/licenses/MIT
  */
 
-"use strict";
-
 var OPTIONS_REFERENCE = null;
 
 if (typeof process !== 'undefined' && process.env.NODE_ENV != 'production') {
--- dygraphs-2.2.1.orig/src/dygraph-options.js
+++ dygraphs-2.2.1/src/dygraph-options.js
@@ -1,3 +1,5 @@
+'use strict';
+
 /**
  * @license
  * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)
@@ -11,7 +13,6 @@
 
 // TODO: remove this jshint directive & fix the warnings.
 /*jshint sub:true */
-"use strict";
 
 import * as utils from './dygraph-utils';
 import DEFAULT_ATTRS from './dygraph-default-attrs';
--- dygraphs-2.2.1.orig/src/dygraph-tickers.js
+++ dygraphs-2.2.1/src/dygraph-tickers.js
@@ -1,3 +1,5 @@
+'use strict';
+
 /**
  * @license
  * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)
@@ -62,7 +64,6 @@
 
 /*jshint sub:true */
 /*global Dygraph:false */
-"use strict";
 
 import * as utils from './dygraph-utils';
 
@@ -81,8 +82,8 @@ var TickList = undefined;  // the ' = un
 var Ticker = undefined;  // the ' = undefined' keeps jshint happy.
 
 /** @type {Ticker} */
-export var numericLinearTicks = function(a, b, pixels, opts, dygraph, vals) {
-  var nonLogscaleOpts = function(opt) {
+export var numericLinearTicks = function (a, b, pixels, opts, dygraph, vals) {
+  var nonLogscaleOpts = function (opt) {
     if (opt === 'logscale') return false;
     return opts(opt);
   };
@@ -90,7 +91,7 @@ export var numericLinearTicks = function
 };
 
 /** @type {Ticker} */
-export var numericTicks = function(a, b, pixels, opts, dygraph, vals) {
+export var numericTicks = function (a, b, pixels, opts, dygraph, vals) {
   var pixels_per_tick = /** @type{number} */(opts('pixelsPerLabel'));
   var ticks = [];
   var i, j, tickV, nTicks;
@@ -208,7 +209,15 @@ export var numericTicks = function(a, b,
 };
 
 /** @type {Ticker} */
-export var dateTicker = function(a, b, pixels, opts, dygraph, vals) {
+export var integerTicks = function (a, b, pixels, opts, dygraph, vals) {
+    var allTicks = numericTicks(a, b, pixels, opts, dygraph, vals);
+    return allTicks.filter(function (tick) {
+      return tick.v % 1 === 0;
+    });
+};
+
+/** @type {Ticker} */
+export var dateTicker = function (a, b, pixels, opts, dygraph, vals) {
   var chosen = pickDateTickGranularity(a, b, pixels, opts);
 
   if (chosen >= 0) {
@@ -317,7 +326,7 @@ TICK_PLACEMENT[Granularity.CENTENNIAL]
  * NOTE: this assumes that utils.LOG_SCALE = 10.
  * @type {Array.<number>}
  */
-var PREFERRED_LOG_TICK_VALUES = (function() {
+var PREFERRED_LOG_TICK_VALUES = (function () {
   var vals = [];
   for (var power = -39; power <= 39; power++) {
     var range = Math.pow(10, power);
@@ -339,7 +348,7 @@ var PREFERRED_LOG_TICK_VALUES = (functio
  * @return {number} The appropriate axis granularity for this chart. See the
  *     enumeration of possible values in dygraph-tickers.js.
  */
-export var pickDateTickGranularity = function(a, b, pixels, opts) {
+export var pickDateTickGranularity = function (a, b, pixels, opts) {
   var pixels_per_tick = /** @type{number} */(opts('pixelsPerLabel'));
   for (var i = 0; i < Granularity.NUM_GRANULARITIES; i++) {
     var num_ticks = numDateTicks(a, b, i);
@@ -357,7 +366,7 @@ export var pickDateTickGranularity = fun
  * @param {number} granularity (one of the granularities enumerated above)
  * @return {number} (Approximate) number of ticks that would result.
  */
-var numDateTicks = function(start_time, end_time, granularity) {
+var numDateTicks = function (start_time, end_time, granularity) {
   var spacing = TICK_PLACEMENT[granularity].spacing;
   return Math.round(1.0 * (end_time - start_time) / spacing);
 };
@@ -371,7 +380,7 @@ var numDateTicks = function(start_time,
  * @param {Dygraph=} dg
  * @return {!TickList}
  */
-export var getDateAxis = function(start_time, end_time, granularity, opts, dg) {
+export var getDateAxis = function (start_time, end_time, granularity, opts, dg) {
   var formatter = /** @type{AxisLabelFormatter} */(
       opts("axisLabelFormatter"));
   var utc = opts("labelsUTC");
--- dygraphs-2.2.1.orig/src/dygraph-utils.js
+++ dygraphs-2.2.1/src/dygraph-utils.js
@@ -1,3 +1,5 @@
+'use strict';
+
 /**
  * @license
  * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)
@@ -12,7 +14,6 @@
  */
 
 /*global Dygraph:false, Node:false */
-"use strict";
 
 import * as DygraphTickers from './dygraph-tickers';
 
--- dygraphs-2.2.1.orig/src/dygraph.js
+++ dygraphs-2.2.1/src/dygraph.js
@@ -1,3 +1,5 @@
+'use strict';
+
 /**
  * @license
  * Copyright 2006 Dan Vanderkam (danvdk@gmail.com)
@@ -73,8 +75,6 @@ import RangeSelectorPlugin from './plugi
 
 import GVizChart from './dygraph-gviz';
 
-"use strict";
-
 /**
  * @class Creates an interactive, zoomable chart.
  * @name Dygraph
@@ -3495,6 +3495,11 @@ Dygraph.prototype.removeTrackedEvents_ =
 };
 
 // Installed plugins, in order of precedence (most-general to most-specific).
+// This means that, in an event cascade, plugins which have registered
+// for that event will be called in reverse order.
+//
+// This is most relevant for plugins which register a layout event,
+// e.g. Axes, Legend and ChartLabels.
 Dygraph.PLUGINS = [
   LegendPlugin,
   AxesPlugin,
@@ -3507,6 +3512,7 @@ Dygraph.PLUGINS = [
 // There are many symbols which have historically been available through the
 // Dygraph class. These are exported here for backwards compatibility.
 Dygraph.GVizChart = GVizChart;
+Dygraph.DOTTED_LINE = utils.DOTTED_LINE;
 Dygraph.DASHED_LINE = utils.DASHED_LINE;
 Dygraph.DOT_DASH_LINE = utils.DOT_DASH_LINE;
 Dygraph.dateAxisLabelFormatter = utils.dateAxisLabelFormatter;
@@ -3546,6 +3552,7 @@ Dygraph.endZoom = DygraphInteraction.end
 
 Dygraph.numericLinearTicks = DygraphTickers.numericLinearTicks;
 Dygraph.numericTicks = DygraphTickers.numericTicks;
+Dygraph.integerTicks = DygraphTickers.integerTicks;
 Dygraph.dateTicker = DygraphTickers.dateTicker;
 Dygraph.Granularity = DygraphTickers.Granularity;
 Dygraph.getDateAxis = DygraphTickers.getDateAxis;
--- dygraphs-2.2.1.orig/src/extras/crosshair.js
+++ dygraphs-2.2.1/src/extras/crosshair.js
@@ -18,7 +18,7 @@ if (window.Dygraph) {
 /* end of loader wrapper header */
 
 Dygraph.Plugins.Crosshair = (function _extras_crosshair_closure() {
-  "use strict";
+  'use strict';
 
   /**
    * Creates the crosshair
@@ -33,6 +33,15 @@ Dygraph.Plugins.Crosshair = (function _e
     this.strokeStyle_ = opt_options.strokeStyle || "rgba(0, 0, 0, 0.3)";
   };
 
+  crosshair.prototype.updateCanvasSize = function updateCanvasSize(width, height) {
+    if (width === this.canvas_.width && height === this.canvas_.height)
+      return;
+    this.canvas_.width = width;
+    this.canvas_.height = height;
+    this.canvas_.style.width = width + 'px';    // for IE
+    this.canvas_.style.height = height + 'px';  // for IE
+  };
+
   crosshair.prototype.toString = function toString() {
     return "Crosshair Plugin";
   };
@@ -42,6 +51,7 @@ Dygraph.Plugins.Crosshair = (function _e
    * @return {object.<string, function(ev)>} Mapping of event names to callbacks.
    */
   crosshair.prototype.activate = function activate(g) {
+    this.updateCanvasSize(g.width_, g.height_);
     g.graphDiv.appendChild(this.canvas_);
 
     return {
@@ -57,28 +67,38 @@ Dygraph.Plugins.Crosshair = (function _e
 
     var width = e.dygraph.width_;
     var height = e.dygraph.height_;
-    this.canvas_.width = width;
-    this.canvas_.height = height;
-    this.canvas_.style.width = width + "px";    // for IE
-    this.canvas_.style.height = height + "px";  // for IE
+    this.updateCanvasSize(width, height);
 
     var ctx = this.canvas_.getContext("2d");
     ctx.clearRect(0, 0, width, height);
     ctx.strokeStyle = this.strokeStyle_;
     ctx.beginPath();
 
-    var canvasx = Math.floor(e.dygraph.selPoints_[0].canvasx) + 0.5; // crisper rendering
-
-    if (this.direction_ === "vertical" || this.direction_ === "both") {
-      ctx.moveTo(canvasx, 0);
-      ctx.lineTo(canvasx, height);
+    if (this.direction_ === "both" || this.direction_ === "vertical") {
+      if (e.dygraph.selPoints_.length !== 0) {
+        var p = e.dygraph.selPoints_[0];
+        if (p.x >= 0 && p.x <= 1) {
+          var canvasx = Math.floor(p.canvasx) + 0.5; // crisper rendering
+          if (canvasx > width)
+            canvasx = width - 0.5;
+
+          ctx.moveTo(canvasx, 0);
+          ctx.lineTo(canvasx, height);
+        }
+      }
     }
 
-    if (this.direction_ === "horizontal" || this.direction_ === "both") {
+    if (this.direction_ === "both" || this.direction_ === "horizontal") {
       for (var i = 0; i < e.dygraph.selPoints_.length; i++) {
-        var canvasy = Math.floor(e.dygraph.selPoints_[i].canvasy) + 0.5; // crisper rendering
-        ctx.moveTo(0, canvasy);
-        ctx.lineTo(width, canvasy);
+        var p = e.dygraph.selPoints_[i];
+        if (p.y >= 0 && p.y <= 1) {
+          var canvasy = Math.floor(p.canvasy) + 0.5; // crisper rendering
+          if (canvasy > height)
+            canvasy = height - 0.5;
+
+          ctx.moveTo(0, canvasy);
+          ctx.lineTo(width, canvasy);
+        }
       }
     }
 
--- dygraphs-2.2.1.orig/src/extras/hairlines.js
+++ dygraphs-2.2.1/src/extras/hairlines.js
@@ -23,7 +23,7 @@ if (window.Dygraph) {
 
 Dygraph.Plugins.Hairlines = (function _extras_hairlines_closure() {
 
-"use strict";
+'use strict';
 
 /**
  * @typedef {
--- dygraphs-2.2.1.orig/src/extras/super-annotations.js
+++ dygraphs-2.2.1/src/extras/super-annotations.js
@@ -23,7 +23,7 @@ if (window.Dygraph) {
 
 Dygraph.Plugins.SuperAnnotations = (function _extras_superAnnotations_closure() {
 
-"use strict";
+'use strict';
 
 /**
  * These are just the basic requirements -- annotations can have whatever other
--- dygraphs-2.2.1.orig/src/extras/unzoom.js
+++ dygraphs-2.2.1/src/extras/unzoom.js
@@ -37,8 +37,7 @@ if (window.Dygraph) {
  * @author konigsberg@google.com (Robert Konigsberg)
  */
 Dygraph.Plugins.Unzoom = (function _extras_unzoom_closure() {
-
-  "use strict";
+  'use strict';
 
   /**
    * Create a new instance.
--- dygraphs-2.2.1.orig/src/iframe-tarp.js
+++ dygraphs-2.2.1/src/iframe-tarp.js
@@ -1,3 +1,5 @@
+'use strict';
+
 /**
  * To create a "drag" interaction, you typically register a mousedown event
  * handler on the element where the drag begins. In that handler, you register a
--- dygraphs-2.2.1.orig/src/plugins/annotations.js
+++ dygraphs-2.2.1/src/plugins/annotations.js
@@ -1,3 +1,5 @@
+'use strict';
+
 /**
  * @license
  * Copyright 2012 Dan Vanderkam (danvdk@gmail.com)
@@ -6,8 +8,6 @@
 
 /*global Dygraph:false */
 
-"use strict";
-
 /**
 Current bits of jankiness:
 - Uses dygraph.layout_ to get the parsed annotations.
--- dygraphs-2.2.1.orig/src/plugins/axes.js
+++ dygraphs-2.2.1/src/plugins/axes.js
@@ -1,3 +1,5 @@
+'use strict';
+
 /**
  * @license
  * Copyright 2012 Dan Vanderkam (danvdk@gmail.com)
@@ -6,8 +8,6 @@
 
 /*global Dygraph:false */
 
-'use strict';
-
 /*
 Bits of jankiness:
 - Direct layout access
--- dygraphs-2.2.1.orig/src/plugins/chart-labels.js
+++ dygraphs-2.2.1/src/plugins/chart-labels.js
@@ -1,3 +1,5 @@
+'use strict';
+
 /**
  * @license
  * Copyright 2012 Dan Vanderkam (danvdk@gmail.com)
@@ -5,8 +7,6 @@
  */
 /*global Dygraph:false */
 
-"use strict";
-
 // TODO(danvk): move chart label options out of dygraphs and into the plugin.
 // TODO(danvk): only tear down & rebuild the DIVs when it's necessary.
 
--- dygraphs-2.2.1.orig/src/plugins/grid.js
+++ dygraphs-2.2.1/src/plugins/grid.js
@@ -1,3 +1,5 @@
+'use strict';
+
 /**
  * @license
  * Copyright 2012 Dan Vanderkam (danvdk@gmail.com)
@@ -13,8 +15,6 @@ Current bits of jankiness:
 
 */
 
-"use strict";
-
 /**
  * Draws the gridlines, i.e. the gray horizontal & vertical lines running the
  * length of the chart.
--- dygraphs-2.2.1.orig/src/plugins/legend.js
+++ dygraphs-2.2.1/src/plugins/legend.js
@@ -1,3 +1,5 @@
+'use strict';
+
 /**
  * @license
  * Copyright 2012 Dan Vanderkam (danvdk@gmail.com)
@@ -15,7 +17,6 @@ Current bits of jankiness:
 */
 
 /*global Dygraph:false */
-"use strict";
 
 import * as utils from '../dygraph-utils';
 
--- dygraphs-2.2.1.orig/src/plugins/range-selector.js
+++ dygraphs-2.2.1/src/plugins/range-selector.js
@@ -1,3 +1,5 @@
+'use strict';
+
 /**
  * @license
  * Copyright 2011 Paul Felix (paul.eric.felix@gmail.com)
@@ -11,7 +13,6 @@
  */
 
 /*global Dygraph:false */
-"use strict";
 
 import * as utils from '../dygraph-utils';
 import DygraphInteraction from '../dygraph-interaction-model';
--- dygraphs-2.2.1.orig/tests/exported-symbols.html
+++ dygraphs-2.2.1/tests/exported-symbols.html
@@ -17,7 +17,7 @@
     <ol id="list">
     </ol>
 
-    <p>It exports these symbols inside each of those symbols and <tt>Dygraph.{Circles,Plugins}</tt>:</p>
+    <p>It exports these symbols inside each of those symbols and <tt>Dygraph.{Circles,Plugins,prototype}</tt>:</p>
     <ol id="list2">
     </ol>
 
@@ -29,7 +29,7 @@
     <ol id="list4">
     </ol>
 
-    <p>New second-level and <tt>Dygraph.{Circles,Plugins}</tt> elements after loading extras:</p>
+    <p>New second-level and <tt>Dygraph.{Circles,Plugins,prototype}</tt> elements after loading extras:</p>
     <ol id="list5">
     </ol>
 
@@ -83,6 +83,10 @@
         sym = 'Dygraph.Plugins.' + k;
         level2props.push(sym);
       }
+      for (k in Dygraph.prototype) {
+        sym = 'Dygraph.prototype.' + k;
+        level2props.push(sym);
+      }
 
       level2props.sort();
       html = '';
@@ -159,6 +163,11 @@
 	  if (!level2props.includes(sym))
 	    newl2props.push(sym);
 	}
+	for (k in Dygraph.prototype) {
+	  sym = 'Dygraph.prototype.' + k;
+	  if (!level2props.includes(sym))
+	    newl2props.push(sym);
+	}
 	newl2props.sort();
 	html = '';
 	for (i = 0; i < newl2props.length; i++)
