dc
Version:
A multi-dimensional charting library built to work natively with crossfilter and rendered using d3.js
300 lines (274 loc) • 7.45 kB
JavaScript
/**
* The default date format for dc.js
* @name dateFormat
* @memberof dc
* @type {Function}
* @default d3.time.format('%m/%d/%Y')
*/
dc.dateFormat = d3.time.format('%m/%d/%Y');
/**
* @namespace printers
* @memberof dc
* @type {{}}
*/
dc.printers = {};
/**
* Converts a list of filters into a readable string.
* @method filters
* @memberof dc.printers
* @param {Array<dc.filters>} filters
* @returns {String}
*/
dc.printers.filters = function (filters) {
var s = '';
for (var i = 0; i < filters.length; ++i) {
if (i > 0) {
s += ', ';
}
s += dc.printers.filter(filters[i]);
}
return s;
};
/**
* Converts a filter into a readable string.
* @method filter
* @memberof dc.printers
* @param {dc.filters|any|Array<any>} filter
* @returns {String}
*/
dc.printers.filter = function (filter) {
var s = '';
if (typeof filter !== 'undefined' && filter !== null) {
if (filter instanceof Array) {
if (filter.length >= 2) {
s = '[' + dc.utils.printSingleValue(filter[0]) + ' -> ' + dc.utils.printSingleValue(filter[1]) + ']';
} else if (filter.length >= 1) {
s = dc.utils.printSingleValue(filter[0]);
}
} else {
s = dc.utils.printSingleValue(filter);
}
}
return s;
};
/**
* Returns a function that given a string property name, can be used to pluck the property off an object. A function
* can be passed as the second argument to also alter the data being returned.
*
* This can be a useful shorthand method to create accessor functions.
* @method pluck
* @memberof dc
* @example
* var xPluck = dc.pluck('x');
* var objA = {x: 1};
* xPluck(objA) // 1
* @example
* var xPosition = dc.pluck('x', function (x, i) {
* // `this` is the original datum,
* // `x` is the x property of the datum,
* // `i` is the position in the array
* return this.radius + x;
* });
* dc.selectAll('.circle').data(...).x(xPosition);
* @param {String} n
* @param {Function} [f]
* @returns {Function}
*/
dc.pluck = function (n, f) {
if (!f) {
return function (d) { return d[n]; };
}
return function (d, i) { return f.call(d, d[n], i); };
};
/**
* @namespace utils
* @memberof dc
* @type {{}}
*/
dc.utils = {};
/**
* Print a single value filter.
* @method printSingleValue
* @memberof dc.utils
* @param {any} filter
* @returns {String}
*/
dc.utils.printSingleValue = function (filter) {
var s = '' + filter;
if (filter instanceof Date) {
s = dc.dateFormat(filter);
} else if (typeof(filter) === 'string') {
s = filter;
} else if (dc.utils.isFloat(filter)) {
s = dc.utils.printSingleValue.fformat(filter);
} else if (dc.utils.isInteger(filter)) {
s = Math.round(filter);
}
return s;
};
dc.utils.printSingleValue.fformat = d3.format('.2f');
/**
* Arbitrary add one value to another.
* @method add
* @memberof dc.utils
* @todo
* These assume than any string r is a percentage (whether or not it includes %).
* They also generate strange results if l is a string.
* @param {String|Date|Number} l the value to modify
* @param {Number} r the amount by which to modify the value
* @param {String} [t] if `l` is a `Date`, the
* [interval](https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Intervals.md#interval) in
* the `d3.time` namespace
* @returns {String|Date|Number}
*/
dc.utils.add = function (l, r, t) {
if (typeof r === 'string') {
r = r.replace('%', '');
}
if (l instanceof Date) {
if (typeof r === 'string') {
r = +r;
}
if (t === 'millis') {
return new Date(l.getTime() + r);
}
t = t || 'day';
return d3.time[t].offset(l, r);
} else if (typeof r === 'string') {
var percentage = (+r / 100);
return l > 0 ? l * (1 + percentage) : l * (1 - percentage);
} else {
return l + r;
}
};
/**
* Arbitrary subtract one value from another.
* @method subtract
* @memberof dc.utils
* @todo
* These assume than any string r is a percentage (whether or not it includes %).
* They also generate strange results if l is a string.
* @param {String|Date|Number} l the value to modify
* @param {Number} r the amount by which to modify the value
* @param {String} [t] if `l` is a `Date`, the
* [interval](https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Intervals.md#interval) in
* the `d3.time` namespace
* @returns {String|Date|Number}
*/
dc.utils.subtract = function (l, r, t) {
if (typeof r === 'string') {
r = r.replace('%', '');
}
if (l instanceof Date) {
if (typeof r === 'string') {
r = +r;
}
if (t === 'millis') {
return new Date(l.getTime() - r);
}
t = t || 'day';
return d3.time[t].offset(l, -r);
} else if (typeof r === 'string') {
var percentage = (+r / 100);
return l < 0 ? l * (1 + percentage) : l * (1 - percentage);
} else {
return l - r;
}
};
/**
* Is the value a number?
* @method isNumber
* @memberof dc.utils
* @param {any} n
* @returns {Boolean}
*/
dc.utils.isNumber = function (n) {
return n === +n;
};
/**
* Is the value a float?
* @method isFloat
* @memberof dc.utils
* @param {any} n
* @returns {Boolean}
*/
dc.utils.isFloat = function (n) {
return n === +n && n !== (n | 0);
};
/**
* Is the value an integer?
* @method isInteger
* @memberof dc.utils
* @param {any} n
* @returns {Boolean}
*/
dc.utils.isInteger = function (n) {
return n === +n && n === (n | 0);
};
/**
* Is the value very close to zero?
* @method isNegligible
* @memberof dc.utils
* @param {any} n
* @returns {Boolean}
*/
dc.utils.isNegligible = function (n) {
return !dc.utils.isNumber(n) || (n < dc.constants.NEGLIGIBLE_NUMBER && n > -dc.constants.NEGLIGIBLE_NUMBER);
};
/**
* Ensure the value is no greater or less than the min/max values. If it is return the boundary value.
* @method clamp
* @memberof dc.utils
* @param {any} val
* @param {any} min
* @param {any} max
* @returns {any}
*/
dc.utils.clamp = function (val, min, max) {
return val < min ? min : (val > max ? max : val);
};
/**
* Using a simple static counter, provide a unique integer id.
* @method uniqueId
* @memberof dc.utils
* @returns {Number}
*/
var _idCounter = 0;
dc.utils.uniqueId = function () {
return ++_idCounter;
};
/**
* Convert a name to an ID.
* @method nameToId
* @memberof dc.utils
* @param {String} name
* @returns {String}
*/
dc.utils.nameToId = function (name) {
return name.toLowerCase().replace(/[\s]/g, '_').replace(/[\.']/g, '');
};
/**
* Append or select an item on a parent element.
* @method appendOrSelect
* @memberof dc.utils
* @param {d3.selection} parent
* @param {String} selector
* @param {String} tag
* @returns {d3.selection}
*/
dc.utils.appendOrSelect = function (parent, selector, tag) {
tag = tag || selector;
var element = parent.select(selector);
if (element.empty()) {
element = parent.append(tag);
}
return element;
};
/**
* Return the number if the value is a number; else 0.
* @method safeNumber
* @memberof dc.utils
* @param {Number|any} n
* @returns {Number}
*/
dc.utils.safeNumber = function (n) { return dc.utils.isNumber(+n) ? +n : 0;};