A deep copy is overkill here.

The only destructive change to the supplied data array is to change its
ordering, so a shallow copy is enough to avoid clobbering it.
This commit is contained in:
Olly Smith 2012-05-14 07:37:45 +01:00
parent 2108baaf4e
commit 942e121caa
4 changed files with 25 additions and 85 deletions

View file

@ -25,10 +25,6 @@ class Morris.Line
# bail if there's no data # bail if there's no data
if @options.data is undefined or @options.data.length is 0 if @options.data is undefined or @options.data.length is 0
return return
else
# otherwise create a deep copy of the input data: we'll perform
# operations on it later so better not taint the user's data.
@options.data = clone(@options.data)
@el.addClass 'graph-initialised' @el.addClass 'graph-initialised'
@precalc() @precalc()
@redraw() @redraw()
@ -79,7 +75,8 @@ class Morris.Line
# Do any necessary pre-processing for a new dataset # Do any necessary pre-processing for a new dataset
# #
precalc: -> precalc: ->
# sort data # shallow copy & sort data
@options.data = @options.data.slice(0)
@options.data.sort (a, b) => (a[@options.xkey] < b[@options.xkey]) - (b[@options.xkey] < a[@options.xkey]) @options.data.sort (a, b) => (a[@options.xkey] < b[@options.xkey]) - (b[@options.xkey] < a[@options.xkey])
# extract labels # extract labels
@columnLabels = $.map @options.data, (d) => d[@options.xkey] @columnLabels = $.map @options.data, (d) => d[@options.xkey]
@ -522,31 +519,6 @@ Morris.labelSeries = (dmin, dmax, pxwidth, specName, xLabelFormat) ->
spec.incr(d) spec.incr(d)
return ret return ret
# Perform a deep copy of the given object.
# Source: http://coffeescriptcookbook.com/chapters/classes_and_objects/cloning
clone = (obj) ->
if not obj? or typeof obj isnt 'object'
return obj
if obj instanceof Date
return new Date(obj.getTime())
if obj instanceof RegExp
flags = ''
flags += 'g' if obj.global?
flags += 'i' if obj.ignoreCase?
flags += 'm' if obj.multiline?
flags += 'y' if obj.sticky?
return new RegExp(obj.source, flags)
newInstance = new obj.constructor()
for key of obj
newInstance[key] = clone obj[key]
return newInstance
minutesSpecHelper = (interval) -> minutesSpecHelper = (interval) ->
span: interval * 60 * 1000 span: interval * 60 * 1000
start: (d) -> new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours()) start: (d) -> new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours())

View file

@ -1,6 +1,6 @@
// Generated by CoffeeScript 1.3.1 // Generated by CoffeeScript 1.3.1
(function() { (function() {
var $, Morris, clone, minutesSpecHelper, secondsSpecHelper, var $, Morris, minutesSpecHelper, secondsSpecHelper,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
$ = jQuery; $ = jQuery;
@ -38,8 +38,6 @@
} }
if (this.options.data === void 0 || this.options.data.length === 0) { if (this.options.data === void 0 || this.options.data.length === 0) {
return; return;
} else {
this.options.data = clone(this.options.data);
} }
this.el.addClass('graph-initialised'); this.el.addClass('graph-initialised');
this.precalc(); this.precalc();
@ -85,6 +83,7 @@
Line.prototype.precalc = function() { Line.prototype.precalc = function() {
var d, series_data, touchHandler, ykey, ymax, ymin, _i, _j, _k, _len, _len1, _ref, _ref1, _ref2, _results, var d, series_data, touchHandler, ykey, ymax, ymin, _i, _j, _k, _len, _len1, _ref, _ref1, _ref2, _results,
_this = this; _this = this;
this.options.data = this.options.data.slice(0);
this.options.data.sort(function(a, b) { this.options.data.sort(function(a, b) {
return (a[_this.options.xkey] < b[_this.options.xkey]) - (b[_this.options.xkey] < a[_this.options.xkey]); return (a[_this.options.xkey] < b[_this.options.xkey]) - (b[_this.options.xkey] < a[_this.options.xkey]);
}); });
@ -591,37 +590,6 @@
return ret; return ret;
}; };
clone = function(obj) {
var flags, key, newInstance;
if (!(obj != null) || typeof obj !== 'object') {
return obj;
}
if (obj instanceof Date) {
return new Date(obj.getTime());
}
if (obj instanceof RegExp) {
flags = '';
if (obj.global != null) {
flags += 'g';
}
if (obj.ignoreCase != null) {
flags += 'i';
}
if (obj.multiline != null) {
flags += 'm';
}
if (obj.sticky != null) {
flags += 'y';
}
return new RegExp(obj.source, flags);
}
newInstance = new obj.constructor();
for (key in obj) {
newInstance[key] = clone(obj[key]);
}
return newInstance;
};
minutesSpecHelper = function(interval) { minutesSpecHelper = function(interval) {
return { return {
span: interval * 60 * 1000, span: interval * 60 * 1000,

2
morris.min.js vendored

File diff suppressed because one or more lines are too long

View file

@ -12,28 +12,28 @@
module("Morris"); module("Morris");
test("Input data remains untouched", function () { test("Input data remains untouched", function () {
var my_data = [{x: 1, y: 1}, {x: 2, y: 2}]; var my_data = [{x: 1, y: 1}, {x: 2, y: 2}];
var expected_data = [{x: 1, y: 1}, {x: 2, y: 2}]; var expected_data = [{x: 1, y: 1}, {x: 2, y: 2}];
Morris.Line({ Morris.Line({
element: "graph-placeholder", element: "graph-placeholder",
data: my_data, data: my_data,
xkey: 'x', xkey: 'x',
ykeys: ['y'], ykeys: ['y'],
labels: ['dontcare'] labels: ['dontcare']
}); });
deepEqual(my_data, expected_data); deepEqual(my_data, expected_data);
}); });
test("Nicer error when the placeholder was not found.", function () { test("Raise an error when the placeholder element is not found.", function () {
var my_data = [{x: 1, y: 1}, {x: 2, y: 2}]; var my_data = [{x: 1, y: 1}, {x: 2, y: 2}];
raises(function() { raises(function() {
Morris.Line({ Morris.Line({
element: "thisplacedoesnotexist", element: "thisplacedoesnotexist",
data: my_data, data: my_data,
xkey: 'x', xkey: 'x',
ykeys: ['y'], ykeys: ['y'],
labels: ['dontcare']}) labels: ['dontcare']})
}, "Graph placeholder not found."); }, "Graph placeholder not found.");
}); });
test("Morris.commas", function () { test("Morris.commas", function () {