hairlines.prototype.activate = function(g) {
this.dygraph_ = g;
- this.hairlines_ = [this.createHairline(0.55)];
+ this.hairlines_ = [];
return {
didDrawChart: this.didDrawChart,
hairlines.prototype.hairlineWasDragged = function(h, event, ui) {
var area = this.dygraph_.getArea();
+ var oldFrac = h.xFraction;
h.xFraction = (ui.position.left - area.x) / area.w;
this.moveHairlineToTop(h);
this.updateHairlineDivPositions();
this.updateHairlineInfo();
+ $(this).triggerHandler('hairlineMoved', {
+ oldXFraction: oldFrac,
+ newXFraction: h.xFraction
+ });
+ $(this).triggerHandler('hairlinesChanged', {});
};
// This creates the hairline object and returns it.
var that = this;
$infoDiv.on('click', '.hairline-kill-button', function() {
that.removeHairline(h);
+ $(that).triggerHandler('hairlineDeleted', {
+ xFraction: h.xFraction
+ });
+ $(that).triggerHandler('hairlinesChanged', {});
});
return h;
that.updateHairlineDivPositions();
that.updateHairlineInfo();
that.attachHairlinesToChart_();
+
+ $(that).triggerHandler('hairlineCreated', {
+ xFraction: xFraction
+ });
+ $(that).triggerHandler('hairlinesChanged', {});
}, CLICK_DELAY_MS);
};
this.detachLabels();
};
+
+// Public API
+
+/**
+ * This is a restricted view of this.hairlines_ which doesn't expose
+ * implementation details like the handle divs.
+ *
+ * @typedef {
+ * xFraction: number, // invariant across resize
+ * interpolated: bool // alternative is to snap to closest
+ * } PublicHairline
+ */
+
+/**
+ * @return {!Array.<!PublicHairline>} The current set of hairlines, ordered
+ * from back to front.
+ */
+hairlines.prototype.get = function() {
+ var result = [];
+ for (var i = 0; i < this.hairlines_.length; i++) {
+ var h = this.hairlines_[i];
+ result.push({
+ xFraction: h.xFraction,
+ interpolated: h.interpolated
+ });
+ }
+ return result;
+};
+
+/**
+ * Calling this will result in a hairlinesChanged event being triggered, no
+ * matter whether it consists of additions, deletions, moves or no changes at
+ * all.
+ *
+ * @param {!Array.<!PublicHairline>} hairlines The new set of hairlines,
+ * ordered from back to front.
+ */
+hairlines.prototype.set = function(hairlines) {
+ // Re-use divs from the old hairlines array so far as we can.
+ // They're already correctly z-ordered.
+ var anyCreated = false;
+ for (var i = 0; i < hairlines.length; i++) {
+ var h = hairlines[i];
+
+ if (this.hairlines_.length > i) {
+ this.hairlines_[i].xFraction = h.xFraction;
+ this.hairlines_[i].interpolated = h.interpolated;
+ } else {
+ // TODO(danvk): pass in |interpolated| value.
+ this.hairlines_.push(this.createHairline(h.xFraction));
+ anyCreated = true;
+ }
+ }
+
+ // If there are any remaining hairlines, destroy them.
+ while (hairlines.length < this.hairlines_.length) {
+ this.removeHairline(this.hairlines_[hairlines.length]);
+ }
+
+ this.updateHairlineDivPositions();
+ this.updateHairlineInfo();
+ if (anyCreated) {
+ this.attachHairlinesToChart_();
+ }
+
+ $(this).triggerHandler('hairlinesChanged', {});
+};
+
return hairlines;
})();
<div id="controls">
<input type="checkbox" id="update" checked=true><label for="update"> Update</label>
+
+ <button id="add-button">Add a Hairline</button>
+ <button id="remove-button">Remove a Hairline</button>
+ <button id="reset-button">Reset Hairlines</button>
</div>
<script type="text/javascript">
data.push([last_t, fn(last_t)]);
}
+ hairlines = new Dygraph.Plugins.Hairlines();
g = new Dygraph(
document.getElementById("demodiv"),
data,
// Set the plug-ins in the options.
plugins : [
- Dygraph.Plugins.Hairlines
+ hairlines
]
}
);
};
window.setInterval(update, 1000);
+ // Control handlers
$('#update').on('change', function() {
shouldUpdate = $(this).is(':checked');
});
+
+ $('#add-button').on('click', function(e) {
+ var h = hairlines.get();
+ h.push({xFraction: 0.75});
+ hairlines.set(h);
+ });
+ $('#remove-button').on('click', function(e) {
+ var h = hairlines.get();
+ if (h.length > 0) {
+ var idx = Math.floor(h.length / 2);
+ h.splice(idx, 1);
+ }
+ hairlines.set(h);
+ });
+ $('#reset-button').on('click', function(e) {
+ setDefaultHairlines();
+ });
+
+ // Hairline persistence
+ function loadFromStorage() {
+ hairlines.set(JSON.parse(localStorage.getItem('hairlines')));
+ }
+ function storeHairlines() {
+ localStorage.setItem('hairlines', JSON.stringify(hairlines.get()));
+ }
+ $(hairlines).on('hairlinesChanged', function(e) {
+ storeHairlines();
+ });
+ function setDefaultHairlines() {
+ // triggers 'hairlinesChanged' event, above.
+ hairlines.set([{xFraction: 0.55}]);
+ }
+
+ if (!localStorage.getItem('hairlines')) {
+ setDefaultHairlines();
+ } else {
+ loadFromStorage();
+ }
+
+
+ // Demonstration of how to use various other event listeners
+ $(hairlines).on('hairlineMoved', function(e, data) {
+ // console.log('hairline moved from', data.oldXFraction, ' to ', data.newXFraction);
+ });
+ $(hairlines).on('hairlineCreated', function(e, data) {
+ console.log('hairline created at ', data.xFraction);
+ });
+ $(hairlines).on('hairlineDeleted', function(e, data) {
+ console.log('hairline deleted at ', data.xFraction);
+ });
</script>
</body>
</html>