xcharts.js 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158
  1. /*!
  2. xCharts v0.3.0 Copyright (c) 2012, tenXer, Inc. All Rights Reserved.
  3. @license MIT license. http://github.com/tenXer/xcharts for details
  4. */
  5. (function () {
  6. var xChart,
  7. _vis = {},
  8. _scales = {},
  9. _visutils = {};
  10. (function(){var n=this,t=n._,r={},e=Array.prototype,u=Object.prototype,i=Function.prototype,a=e.push,o=e.slice,c=e.concat,l=u.toString,f=u.hasOwnProperty,s=e.forEach,p=e.map,v=e.reduce,h=e.reduceRight,g=e.filter,d=e.every,m=e.some,y=e.indexOf,b=e.lastIndexOf,x=Array.isArray,_=Object.keys,j=i.bind,w=function(n){return n instanceof w?n:this instanceof w?(this._wrapped=n,void 0):new w(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=w),exports._=w):n._=w,w.VERSION="1.4.3";var A=w.each=w.forEach=function(n,t,e){if(null!=n)if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a in n)if(w.has(n,a)&&t.call(e,n[a],a,n)===r)return};w.map=w.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e[e.length]=t.call(r,n,u,i)}),e)};var O="Reduce of empty array with no initial value";w.reduce=w.foldl=w.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduce===v)return e&&(t=w.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(O);return r},w.reduceRight=w.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduceRight===h)return e&&(t=w.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=w.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[--i]:--i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(O);return r},w.find=w.detect=function(n,t,r){var e;return E(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},w.filter=w.select=function(n,t,r){var e=[];return null==n?e:g&&n.filter===g?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&(e[e.length]=n)}),e)},w.reject=function(n,t,r){return w.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},w.every=w.all=function(n,t,e){t||(t=w.identity);var u=!0;return null==n?u:d&&n.every===d?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var E=w.some=w.any=function(n,t,e){t||(t=w.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};w.contains=w.include=function(n,t){return null==n?!1:y&&n.indexOf===y?-1!=n.indexOf(t):E(n,function(n){return n===t})},w.invoke=function(n,t){var r=o.call(arguments,2);return w.map(n,function(n){return(w.isFunction(t)?t:n[t]).apply(n,r)})},w.pluck=function(n,t){return w.map(n,function(n){return n[t]})},w.where=function(n,t){return w.isEmpty(t)?[]:w.filter(n,function(n){for(var r in t)if(t[r]!==n[r])return!1;return!0})},w.max=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.max.apply(Math,n);if(!t&&w.isEmpty(n))return-1/0;var e={computed:-1/0,value:-1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;a>=e.computed&&(e={value:n,computed:a})}),e.value},w.min=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.min.apply(Math,n);if(!t&&w.isEmpty(n))return 1/0;var e={computed:1/0,value:1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;e.computed>a&&(e={value:n,computed:a})}),e.value},w.shuffle=function(n){var t,r=0,e=[];return A(n,function(n){t=w.random(r++),e[r-1]=e[t],e[t]=n}),e};var F=function(n){return w.isFunction(n)?n:function(t){return t[n]}};w.sortBy=function(n,t,r){var e=F(t);return w.pluck(w.map(n,function(n,t,u){return{value:n,index:t,criteria:e.call(r,n,t,u)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||void 0===r)return 1;if(e>r||void 0===e)return-1}return n.index<t.index?-1:1}),"value")};var k=function(n,t,r,e){var u={},i=F(t||w.identity);return A(n,function(t,a){var o=i.call(r,t,a,n);e(u,o,t)}),u};w.groupBy=function(n,t,r){return k(n,t,r,function(n,t,r){(w.has(n,t)?n[t]:n[t]=[]).push(r)})},w.countBy=function(n,t,r){return k(n,t,r,function(n,t){w.has(n,t)||(n[t]=0),n[t]++})},w.sortedIndex=function(n,t,r,e){r=null==r?w.identity:F(r);for(var u=r.call(e,t),i=0,a=n.length;a>i;){var o=i+a>>>1;u>r.call(e,n[o])?i=o+1:a=o}return i},w.toArray=function(n){return n?w.isArray(n)?o.call(n):n.length===+n.length?w.map(n,w.identity):w.values(n):[]},w.size=function(n){return null==n?0:n.length===+n.length?n.length:w.keys(n).length},w.first=w.head=w.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:o.call(n,0,t)},w.initial=function(n,t,r){return o.call(n,0,n.length-(null==t||r?1:t))},w.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:o.call(n,Math.max(n.length-t,0))},w.rest=w.tail=w.drop=function(n,t,r){return o.call(n,null==t||r?1:t)},w.compact=function(n){return w.filter(n,w.identity)};var R=function(n,t,r){return A(n,function(n){w.isArray(n)?t?a.apply(r,n):R(n,t,r):r.push(n)}),r};w.flatten=function(n,t){return R(n,t,[])},w.without=function(n){return w.difference(n,o.call(arguments,1))},w.uniq=w.unique=function(n,t,r,e){w.isFunction(t)&&(e=r,r=t,t=!1);var u=r?w.map(n,r,e):n,i=[],a=[];return A(u,function(r,e){(t?e&&a[a.length-1]===r:w.contains(a,r))||(a.push(r),i.push(n[e]))}),i},w.union=function(){return w.uniq(c.apply(e,arguments))},w.intersection=function(n){var t=o.call(arguments,1);return w.filter(w.uniq(n),function(n){return w.every(t,function(t){return w.indexOf(t,n)>=0})})},w.difference=function(n){var t=c.apply(e,o.call(arguments,1));return w.filter(n,function(n){return!w.contains(t,n)})},w.zip=function(){for(var n=o.call(arguments),t=w.max(w.pluck(n,"length")),r=Array(t),e=0;t>e;e++)r[e]=w.pluck(n,""+e);return r},w.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},w.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=w.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},w.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u--;)if(n[u]===t)return u;return-1},w.range=function(n,t,r){1>=arguments.length&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=Array(e);e>u;)i[u++]=n,n+=r;return i};var I=function(){};w.bind=function(n,t){var r,e;if(n.bind===j&&j)return j.apply(n,o.call(arguments,1));if(!w.isFunction(n))throw new TypeError;return r=o.call(arguments,2),e=function(){if(!(this instanceof e))return n.apply(t,r.concat(o.call(arguments)));I.prototype=n.prototype;var u=new I;I.prototype=null;var i=n.apply(u,r.concat(o.call(arguments)));return Object(i)===i?i:u}},w.bindAll=function(n){var t=o.call(arguments,1);return 0==t.length&&(t=w.functions(n)),A(t,function(t){n[t]=w.bind(n[t],n)}),n},w.memoize=function(n,t){var r={};return t||(t=w.identity),function(){var e=t.apply(this,arguments);return w.has(r,e)?r[e]:r[e]=n.apply(this,arguments)}},w.delay=function(n,t){var r=o.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},w.defer=function(n){return w.delay.apply(w,[n,1].concat(o.call(arguments,1)))},w.throttle=function(n,t){var r,e,u,i,a=0,o=function(){a=new Date,u=null,i=n.apply(r,e)};return function(){var c=new Date,l=t-(c-a);return r=this,e=arguments,0>=l?(clearTimeout(u),u=null,a=c,i=n.apply(r,e)):u||(u=setTimeout(o,l)),i}},w.debounce=function(n,t,r){var e,u;return function(){var i=this,a=arguments,o=function(){e=null,r||(u=n.apply(i,a))},c=r&&!e;return clearTimeout(e),e=setTimeout(o,t),c&&(u=n.apply(i,a)),u}},w.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},w.wrap=function(n,t){return function(){var r=[n];return a.apply(r,arguments),t.apply(this,r)}},w.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r--)t=[n[r].apply(this,t)];return t[0]}},w.after=function(n,t){return 0>=n?t():function(){return 1>--n?t.apply(this,arguments):void 0}},w.keys=_||function(n){if(n!==Object(n))throw new TypeError("Invalid object");var t=[];for(var r in n)w.has(n,r)&&(t[t.length]=r);return t},w.values=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push(n[r]);return t},w.pairs=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push([r,n[r]]);return t},w.invert=function(n){var t={};for(var r in n)w.has(n,r)&&(t[n[r]]=r);return t},w.functions=w.methods=function(n){var t=[];for(var r in n)w.isFunction(n[r])&&t.push(r);return t.sort()},w.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},w.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},w.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)w.contains(r,u)||(t[u]=n[u]);return t},w.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)null==n[r]&&(n[r]=t[r])}),n},w.clone=function(n){return w.isObject(n)?w.isArray(n)?n.slice():w.extend({},n):n},w.tap=function(n,t){return t(n),n};var S=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof w&&(n=n._wrapped),t instanceof w&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case"[object String]":return n==t+"";case"[object Number]":return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case"[object Date]":case"[object Boolean]":return+n==+t;case"[object RegExp]":return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]==n)return e[i]==t;r.push(n),e.push(t);var a=0,o=!0;if("[object Array]"==u){if(a=n.length,o=a==t.length)for(;a--&&(o=S(n[a],t[a],r,e)););}else{var c=n.constructor,f=t.constructor;if(c!==f&&!(w.isFunction(c)&&c instanceof c&&w.isFunction(f)&&f instanceof f))return!1;for(var s in n)if(w.has(n,s)&&(a++,!(o=w.has(t,s)&&S(n[s],t[s],r,e))))break;if(o){for(s in t)if(w.has(t,s)&&!a--)break;o=!a}}return r.pop(),e.pop(),o};w.isEqual=function(n,t){return S(n,t,[],[])},w.isEmpty=function(n){if(null==n)return!0;if(w.isArray(n)||w.isString(n))return 0===n.length;for(var t in n)if(w.has(n,t))return!1;return!0},w.isElement=function(n){return!(!n||1!==n.nodeType)},w.isArray=x||function(n){return"[object Array]"==l.call(n)},w.isObject=function(n){return n===Object(n)},A(["Arguments","Function","String","Number","Date","RegExp"],function(n){w["is"+n]=function(t){return l.call(t)=="[object "+n+"]"}}),w.isArguments(arguments)||(w.isArguments=function(n){return!(!n||!w.has(n,"callee"))}),w.isFunction=function(n){return"function"==typeof n},w.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},w.isNaN=function(n){return w.isNumber(n)&&n!=+n},w.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"==l.call(n)},w.isNull=function(n){return null===n},w.isUndefined=function(n){return void 0===n},w.has=function(n,t){return f.call(n,t)},w.noConflict=function(){return n._=t,this},w.identity=function(n){return n},w.times=function(n,t,r){for(var e=Array(n),u=0;n>u;u++)e[u]=t.call(r,u);return e},w.random=function(n,t){return null==t&&(t=n,n=0),n+(0|Math.random()*(t-n+1))};var T={escape:{"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#x27;","/":"&#x2F;"}};T.unescape=w.invert(T.escape);var M={escape:RegExp("["+w.keys(T.escape).join("")+"]","g"),unescape:RegExp("("+w.keys(T.unescape).join("|")+")","g")};w.each(["escape","unescape"],function(n){w[n]=function(t){return null==t?"":(""+t).replace(M[n],function(t){return T[n][t]})}}),w.result=function(n,t){if(null==n)return null;var r=n[t];return w.isFunction(r)?r.call(n):r},w.mixin=function(n){A(w.functions(n),function(t){var r=w[t]=n[t];w.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),z.call(this,r.apply(w,n))}})};var N=0;w.uniqueId=function(n){var t=""+ ++N;return n?n+t:t},w.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var q=/(.)^/,B={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},D=/\\|'|\r|\n|\t|\u2028|\u2029/g;w.template=function(n,t,r){r=w.defaults({},r,w.templateSettings);var e=RegExp([(r.escape||q).source,(r.interpolate||q).source,(r.evaluate||q).source].join("|")+"|$","g"),u=0,i="__p+='";n.replace(e,function(t,r,e,a,o){return i+=n.slice(u,o).replace(D,function(n){return"\\"+B[n]}),r&&(i+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'"),e&&(i+="'+\n((__t=("+e+"))==null?'':__t)+\n'"),a&&(i+="';\n"+a+"\n__p+='"),u=o+t.length,t}),i+="';\n",r.variable||(i="with(obj||{}){\n"+i+"}\n"),i="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+i+"return __p;\n";try{var a=Function(r.variable||"obj","_",i)}catch(o){throw o.source=i,o}if(t)return a(t,w);var c=function(n){return a.call(this,n,w)};return c.source="function("+(r.variable||"obj")+"){\n"+i+"}",c},w.chain=function(n){return w(n).chain()};var z=function(n){return this._chain?w(n).chain():n};w.mixin(w),A(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=e[n];w.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!=n&&"splice"!=n||0!==r.length||delete r[0],z.call(this,r)}}),A(["concat","join","slice"],function(n){var t=e[n];w.prototype[n]=function(){return z.call(this,t.apply(this._wrapped,arguments))}}),w.extend(w.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this);function getInsertionPoint(zIndex) {
  11. return _.chain(_.range(zIndex, 10)).reverse().map(function (z) {
  12. return 'g[data-index="' + z + '"]';
  13. }).value().join(', ');
  14. }
  15. function colorClass(el, i) {
  16. var c = el.getAttribute('class');
  17. return ((c !== null) ? c.replace(/color\d+/g, '') : '') + ' color' + i;
  18. }
  19. _visutils = {
  20. getInsertionPoint: getInsertionPoint,
  21. colorClass: colorClass
  22. };
  23. var local = this,
  24. defaultSpacing = 0.25;
  25. function _getDomain(data, axis) {
  26. return _.chain(data)
  27. .pluck('data')
  28. .flatten()
  29. .pluck(axis)
  30. .uniq()
  31. .filter(function (d) {
  32. return d !== undefined && d !== null;
  33. })
  34. .value()
  35. .sort(d3.ascending);
  36. }
  37. _scales.ordinal = function (data, axis, bounds, extents) {
  38. var domain = _getDomain(data, axis);
  39. return d3.scale.ordinal()
  40. .domain(domain)
  41. .rangeRoundBands(bounds, defaultSpacing);
  42. };
  43. _scales.linear = function (data, axis, bounds, extents) {
  44. return d3.scale.linear()
  45. .domain(extents)
  46. .nice()
  47. .rangeRound(bounds);
  48. };
  49. _scales.exponential = function (data, axis, bounds, extents) {
  50. return d3.scale.pow()
  51. .exponent(0.65)
  52. .domain(extents)
  53. .nice()
  54. .rangeRound(bounds);
  55. };
  56. _scales.time = function (data, axis, bounds, extents) {
  57. return d3.time.scale()
  58. .domain(_.map(extents, function (d) { return new Date(d); }))
  59. .range(bounds);
  60. };
  61. function _extendDomain(domain, axis) {
  62. var min = domain[0],
  63. max = domain[1],
  64. diff,
  65. e;
  66. if (min === max) {
  67. e = Math.max(Math.round(min / 10), 4);
  68. min -= e;
  69. max += e;
  70. }
  71. diff = max - min;
  72. min = (min) ? min - (diff / 10) : min;
  73. min = (domain[0] > 0) ? Math.max(min, 0) : min;
  74. max = (max) ? max + (diff / 10) : max;
  75. max = (domain[1] < 0) ? Math.min(max, 0) : max;
  76. return [min, max];
  77. }
  78. function _getExtents(options, data, xType, yType) {
  79. var extents,
  80. nData = _.chain(data)
  81. .pluck('data')
  82. .flatten()
  83. .value();
  84. extents = {
  85. x: d3.extent(nData, function (d) { return d.x; }),
  86. y: d3.extent(nData, function (d) { return d.y; })
  87. };
  88. _.each([xType, yType], function (type, i) {
  89. var axis = (i) ? 'y' : 'x',
  90. extended;
  91. extents[axis] = d3.extent(nData, function (d) { return d[axis]; });
  92. if (type === 'ordinal') {
  93. return;
  94. }
  95. _.each([axis + 'Min', axis + 'Max'], function (minMax, i) {
  96. if (type !== 'time') {
  97. extended = _extendDomain(extents[axis]);
  98. }
  99. if (options.hasOwnProperty(minMax) && options[minMax] !== null) {
  100. extents[axis][i] = options[minMax];
  101. } else if (type !== 'time') {
  102. extents[axis][i] = extended[i];
  103. }
  104. });
  105. });
  106. return extents;
  107. }
  108. _scales.xy = function (self, data, xType, yType) {
  109. var o = self._options,
  110. extents = _getExtents(o, data, xType, yType),
  111. scales = {},
  112. horiz = [o.axisPaddingLeft, self._width],
  113. vert = [self._height, o.axisPaddingTop],
  114. xScale,
  115. yScale;
  116. _.each([xType, yType], function (type, i) {
  117. var axis = (i === 0) ? 'x' : 'y',
  118. bounds = (i === 0) ? horiz : vert,
  119. fn = xChart.getScale(type);
  120. scales[axis] = fn(data, axis, bounds, extents[axis]);
  121. });
  122. return scales;
  123. };
  124. (function () {
  125. var zIndex = 2,
  126. selector = 'g.bar',
  127. insertBefore = _visutils.getInsertionPoint(zIndex);
  128. function postUpdateScale(self, scaleData, mainData, compData) {
  129. self.xScale2 = d3.scale.ordinal()
  130. .domain(d3.range(0, mainData.length))
  131. .rangeRoundBands([0, self.xScale.rangeBand()], 0.08);
  132. }
  133. function enter(self, storage, className, data, callbacks) {
  134. var barGroups, bars,
  135. yZero = self.yZero;
  136. barGroups = self._g.selectAll(selector + className)
  137. .data(data, function (d) {
  138. return d.className;
  139. });
  140. barGroups.enter().insert('g', insertBefore)
  141. .attr('data-index', zIndex)
  142. .style('opacity', 0)
  143. .attr('class', function (d, i) {
  144. var cl = _.uniq((className + d.className).split('.')).join(' ');
  145. return cl + ' bar ' + _visutils.colorClass(this, i);
  146. })
  147. .attr('transform', function (d, i) {
  148. return 'translate(' + self.xScale2(i) + ',0)';
  149. });
  150. bars = barGroups.selectAll('rect')
  151. .data(function (d) {
  152. return d.data;
  153. }, function (d) {
  154. return d.x;
  155. });
  156. bars.enter().append('rect')
  157. .attr('width', 0)
  158. .attr('rx', 3)
  159. .attr('ry', 3)
  160. .attr('x', function (d) {
  161. return self.xScale(d.x) + (self.xScale2.rangeBand() / 2);
  162. })
  163. .attr('height', function (d) {
  164. return Math.abs(yZero - self.yScale(d.y));
  165. })
  166. .attr('y', function (d) {
  167. return (d.y < 0) ? yZero : self.yScale(d.y);
  168. })
  169. .on('mouseover', callbacks.mouseover)
  170. .on('mouseout', callbacks.mouseout)
  171. .on('click', callbacks.click);
  172. storage.barGroups = barGroups;
  173. storage.bars = bars;
  174. }
  175. function update(self, storage, timing) {
  176. var yZero = self.yZero;
  177. storage.barGroups
  178. .attr('class', function (d, i) {
  179. return _visutils.colorClass(this, i);
  180. })
  181. .transition().duration(timing)
  182. .style('opacity', 1)
  183. .attr('transform', function (d, i) {
  184. return 'translate(' + self.xScale2(i) + ',0)';
  185. });
  186. storage.bars.transition().duration(timing)
  187. .attr('width', self.xScale2.rangeBand())
  188. .attr('x', function (d) {
  189. return self.xScale(d.x);
  190. })
  191. .attr('height', function (d) {
  192. return Math.abs(yZero - self.yScale(d.y));
  193. })
  194. .attr('y', function (d) {
  195. return (d.y < 0) ? yZero : self.yScale(d.y);
  196. });
  197. }
  198. function exit(self, storage, timing) {
  199. storage.bars.exit()
  200. .transition().duration(timing)
  201. .attr('width', 0)
  202. .remove();
  203. storage.barGroups.exit()
  204. .transition().duration(timing)
  205. .style('opacity', 0)
  206. .remove();
  207. }
  208. function destroy(self, storage, timing) {
  209. var band = (self.xScale2) ? self.xScale2.rangeBand() / 2 : 0;
  210. delete self.xScale2;
  211. storage.bars
  212. .transition().duration(timing)
  213. .attr('width', 0)
  214. .attr('x', function (d) {
  215. return self.xScale(d.x) + band;
  216. });
  217. }
  218. _vis.bar = {
  219. postUpdateScale: postUpdateScale,
  220. enter: enter,
  221. update: update,
  222. exit: exit,
  223. destroy: destroy
  224. };
  225. }());
  226. (function () {
  227. var zIndex = 3,
  228. selector = 'g.line',
  229. insertBefore = _visutils.getInsertionPoint(zIndex);
  230. function enter(self, storage, className, data, callbacks) {
  231. var inter = self._options.interpolation,
  232. x = function (d, i) {
  233. if (!self.xScale2 && !self.xScale.rangeBand) {
  234. return self.xScale(d.x);
  235. }
  236. return self.xScale(d.x) + (self.xScale.rangeBand() / 2);
  237. },
  238. y = function (d) { return self.yScale(d.y); },
  239. line = d3.svg.line()
  240. .x(x)
  241. .interpolate(inter),
  242. area = d3.svg.area()
  243. .x(x)
  244. .y1(self.yZero)
  245. .interpolate(inter),
  246. container,
  247. fills,
  248. paths;
  249. function datum(d) {
  250. return [d.data];
  251. }
  252. container = self._g.selectAll(selector + className)
  253. .data(data, function (d) {
  254. return d.className;
  255. });
  256. container.enter().insert('g', insertBefore)
  257. .attr('data-index', zIndex)
  258. .attr('class', function (d, i) {
  259. var cl = _.uniq((className + d.className).split('.')).join(' ');
  260. return cl + ' line ' + _visutils.colorClass(this, i);
  261. });
  262. fills = container.selectAll('path.fill')
  263. .data(datum);
  264. fills.enter().append('path')
  265. .attr('class', 'fill')
  266. .style('opacity', 0)
  267. .attr('d', area.y0(y));
  268. paths = container.selectAll('path.line')
  269. .data(datum);
  270. paths.enter().append('path')
  271. .attr('class', 'line')
  272. .style('opacity', 0)
  273. .attr('d', line.y(y));
  274. storage.lineContainers = container;
  275. storage.lineFills = fills;
  276. storage.linePaths = paths;
  277. storage.lineX = x;
  278. storage.lineY = y;
  279. storage.lineA = area;
  280. storage.line = line;
  281. }
  282. function update(self, storage, timing) {
  283. storage.lineContainers
  284. .attr('class', function (d, i) {
  285. return _visutils.colorClass(this, i);
  286. });
  287. storage.lineFills.transition().duration(timing)
  288. .style('opacity', 1)
  289. .attr('d', storage.lineA.y0(storage.lineY));
  290. storage.linePaths.transition().duration(timing)
  291. .style('opacity', 1)
  292. .attr('d', storage.line.y(storage.lineY));
  293. }
  294. function exit(self, storage) {
  295. storage.linePaths.exit()
  296. .style('opacity', 0)
  297. .remove();
  298. storage.lineFills.exit()
  299. .style('opacity', 0)
  300. .remove();
  301. storage.lineContainers.exit()
  302. .remove();
  303. }
  304. function destroy(self, storage, timing) {
  305. storage.linePaths.transition().duration(timing)
  306. .style('opacity', 0);
  307. storage.lineFills.transition().duration(timing)
  308. .style('opacity', 0);
  309. }
  310. _vis.line = {
  311. enter: enter,
  312. update: update,
  313. exit: exit,
  314. destroy: destroy
  315. };
  316. }());
  317. (function () {
  318. var line = _vis.line;
  319. function enter(self, storage, className, data, callbacks) {
  320. var circles;
  321. line.enter(self, storage, className, data, callbacks);
  322. circles = storage.lineContainers.selectAll('circle')
  323. .data(function (d) {
  324. return d.data;
  325. }, function (d) {
  326. return d.x;
  327. });
  328. circles.enter().append('circle')
  329. .style('opacity', 0)
  330. .attr('cx', storage.lineX)
  331. .attr('cy', storage.lineY)
  332. .attr('r', 5)
  333. .on('mouseover', callbacks.mouseover)
  334. .on('mouseout', callbacks.mouseout)
  335. .on('click', callbacks.click);
  336. storage.lineCircles = circles;
  337. }
  338. function update(self, storage, timing) {
  339. line.update.apply(null, _.toArray(arguments));
  340. storage.lineCircles.transition().duration(timing)
  341. .style('opacity', 1)
  342. .attr('cx', storage.lineX)
  343. .attr('cy', storage.lineY);
  344. }
  345. function exit(self, storage) {
  346. storage.lineCircles.exit()
  347. .remove();
  348. line.exit.apply(null, _.toArray(arguments));
  349. }
  350. function destroy(self, storage, timing) {
  351. line.destroy.apply(null, _.toArray(arguments));
  352. if (!storage.lineCircles) {
  353. return;
  354. }
  355. storage.lineCircles.transition().duration(timing)
  356. .style('opacity', 0);
  357. }
  358. _vis['line-dotted'] = {
  359. enter: enter,
  360. update: update,
  361. exit: exit,
  362. destroy: destroy
  363. };
  364. }());
  365. (function () {
  366. var line = _vis['line-dotted'];
  367. function enter(self, storage, className, data, callbacks) {
  368. line.enter(self, storage, className, data, callbacks);
  369. }
  370. function _accumulate_data(data) {
  371. function reduce(memo, num) {
  372. return memo + num.y;
  373. }
  374. var nData = _.map(data, function (set) {
  375. var i = set.data.length,
  376. d = _.clone(set.data);
  377. set = _.clone(set);
  378. while (i) {
  379. i -= 1;
  380. // Need to clone here, otherwise we are actually setting the same
  381. // data onto the original data set.
  382. d[i] = _.clone(set.data[i]);
  383. d[i].y0 = set.data[i].y;
  384. d[i].y = _.reduce(_.first(set.data, i), reduce, set.data[i].y);
  385. }
  386. return _.extend(set, { data: d });
  387. });
  388. return nData;
  389. }
  390. function _resetData(self) {
  391. if (!self.hasOwnProperty('cumulativeOMainData')) {
  392. return;
  393. }
  394. self._mainData = self.cumulativeOMainData;
  395. delete self.cumulativeOMainData;
  396. self._compData = self.cumulativeOCompData;
  397. delete self.cumulativeOCompData;
  398. }
  399. function preUpdateScale(self, data) {
  400. _resetData(self);
  401. self.cumulativeOMainData = self._mainData;
  402. self._mainData = _accumulate_data(self._mainData);
  403. self.cumulativeOCompData = self._compData;
  404. self._compData = _accumulate_data(self._compData);
  405. }
  406. function destroy(self, storage, timing) {
  407. _resetData(self);
  408. line.destroy.apply(null, _.toArray(arguments));
  409. }
  410. _vis.cumulative = {
  411. preUpdateScale: preUpdateScale,
  412. enter: enter,
  413. update: line.update,
  414. exit: line.exit,
  415. destroy: destroy
  416. };
  417. }());
  418. var emptyData = [[]],
  419. defaults = {
  420. // User interaction callbacks
  421. mouseover: function (data, i) {},
  422. mouseout: function (data, i) {},
  423. click: function (data, i) {},
  424. // Padding between the axes and the contents of the chart
  425. axisPaddingTop: 0,
  426. axisPaddingRight: 0,
  427. axisPaddingBottom: 5,
  428. axisPaddingLeft: 20,
  429. // Padding around the edge of the chart (space for axis labels, etc)
  430. paddingTop: 0,
  431. paddingRight: 0,
  432. paddingBottom: 20,
  433. paddingLeft: 60,
  434. // Axis tick formatting
  435. tickHintX: 10,
  436. tickFormatX: function (x) { return x; },
  437. tickHintY: 10,
  438. tickFormatY: function (y) { return y; },
  439. // Min/Max Axis Values
  440. xMin: null,
  441. xMax: null,
  442. yMin: null,
  443. yMax: null,
  444. // Pre-format input data
  445. dataFormatX: function (x) { return x; },
  446. dataFormatY: function (y) { return y; },
  447. unsupported: function (selector) {
  448. d3.select(selector).text('SVG is not supported on your browser');
  449. },
  450. // Callback functions if no data
  451. empty: function (self, selector, d) {},
  452. notempty: function (self, selector) {},
  453. timing: 750,
  454. // Line interpolation
  455. interpolation: 'monotone',
  456. // Data sorting
  457. sortX: function (a, b) {
  458. return (!a.x && !b.x) ? 0 : (a.x < b.x) ? -1 : 1;
  459. }
  460. };
  461. // What/how should the warning/error be presented?
  462. function svgEnabled() {
  463. var d = document;
  464. return (!!d.createElementNS &&
  465. !!d.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect);
  466. }
  467. /**
  468. * Creates a new chart
  469. *
  470. * @param string type The drawing type for the main data
  471. * @param array data Data to render in the chart
  472. * @param string selector CSS Selector for the parent element for the chart
  473. * @param object options Optional. See `defaults` for options
  474. *
  475. * Examples:
  476. * var data = {
  477. * "main": [
  478. * {
  479. * "data": [
  480. * {
  481. * "x": "2012-08-09T07:00:00.522Z",
  482. * "y": 68
  483. * },
  484. * {
  485. * "x": "2012-08-10T07:00:00.522Z",
  486. * "y": 295
  487. * },
  488. * {
  489. * "x": "2012-08-11T07:00:00.522Z",
  490. * "y": 339
  491. * },
  492. * ],
  493. * "className": ".foo"
  494. * }
  495. * ],
  496. * "xScale": "ordinal",
  497. * "yScale": "linear",
  498. * "comp": [
  499. * {
  500. * "data": [
  501. * {
  502. * "x": "2012-08-09T07:00:00.522Z",
  503. * "y": 288
  504. * },
  505. * {
  506. * "x": "2012-08-10T07:00:00.522Z",
  507. * "y": 407
  508. * },
  509. * {
  510. * "x": "2012-08-11T07:00:00.522Z",
  511. * "y": 459
  512. * }
  513. * ],
  514. * "className": ".comp.comp_foo",
  515. * "type": "line-arrowed"
  516. * }
  517. * ]
  518. * },
  519. * myChart = new Chart('bar', data, '#chart');
  520. *
  521. */
  522. function xChart(type, data, selector, options) {
  523. var self = this,
  524. resizeLock;
  525. self._options = options = _.defaults(options || {}, defaults);
  526. if (svgEnabled() === false) {
  527. return options.unsupported(selector);
  528. }
  529. self._selector = selector;
  530. self._container = d3.select(selector);
  531. self._drawSvg();
  532. self._mainStorage = {};
  533. self._compStorage = {};
  534. data = _.clone(data);
  535. if (type && !data.type) {
  536. data.type = type;
  537. }
  538. self.setData(data);
  539. d3.select(window).on('resize.for.' + selector, function () {
  540. if (resizeLock) {
  541. clearTimeout(resizeLock);
  542. }
  543. resizeLock = setTimeout(function () {
  544. resizeLock = null;
  545. self._resize();
  546. }, 500);
  547. });
  548. }
  549. /**
  550. * Add a visualization type
  551. *
  552. * @param string type Unique key/name used with setType
  553. * @param object vis object map of vis methods
  554. */
  555. xChart.setVis = function (type, vis) {
  556. if (_vis.hasOwnProperty(type)) {
  557. throw 'Cannot override vis type "' + type + '".';
  558. }
  559. _vis[type] = vis;
  560. };
  561. /**
  562. * Get a clone of a visualization
  563. * Useful for extending vis functionality
  564. *
  565. * @param string type Unique key/name of the vis
  566. */
  567. xChart.getVis = function (type) {
  568. if (!_vis.hasOwnProperty(type)) {
  569. throw 'Vis type "' + type + '" does not exist.';
  570. }
  571. return _.clone(_vis[type]);
  572. };
  573. xChart.setScale = function (name, fn) {
  574. if (_scales.hasOwnProperty(name)) {
  575. throw 'Scale type "' + name + '" already exists.';
  576. }
  577. _scales[name] = fn;
  578. };
  579. xChart.getScale = function (name) {
  580. if (!_scales.hasOwnProperty(name)) {
  581. throw 'Scale type "' + name + '" does not exist.';
  582. }
  583. return _scales[name];
  584. };
  585. xChart.visutils = _visutils;
  586. _.defaults(xChart.prototype, {
  587. /**
  588. * Set or change the drawing type for the main data.
  589. *
  590. * @param string type Must be an available drawing type
  591. *
  592. */
  593. setType: function (type, skipDraw) {
  594. var self = this;
  595. if (self._type && type === self._type) {
  596. return;
  597. }
  598. if (!_vis.hasOwnProperty(type)) {
  599. throw 'Vis type "' + type + '" is not defined.';
  600. }
  601. if (self._type) {
  602. self._destroy(self._vis, self._mainStorage);
  603. }
  604. self._type = type;
  605. self._vis = _vis[type];
  606. if (!skipDraw) {
  607. self._draw();
  608. }
  609. },
  610. /**
  611. * Set and update the data for the chart. Optionally skip drawing.
  612. *
  613. * @param object data New data. See new xChart example for format
  614. *
  615. */
  616. setData: function (data) {
  617. var self = this,
  618. o = self._options,
  619. nData = _.clone(data);
  620. if (!data.hasOwnProperty('main')) {
  621. throw 'No "main" key found in given chart data.';
  622. }
  623. switch (data.type) {
  624. case 'bar':
  625. // force the xScale to be ordinal
  626. data.xScale = 'ordinal';
  627. break;
  628. case undefined:
  629. data.type = self._type;
  630. break;
  631. }
  632. o.xMin = (isNaN(parseInt(data.xMin, 10))) ? o.xMin : data.xMin;
  633. o.xMax = (isNaN(parseInt(data.xMax, 10))) ? o.xMax : data.xMax;
  634. o.yMin = (isNaN(parseInt(data.yMin, 10))) ? o.yMin : data.yMin;
  635. o.yMax = (isNaN(parseInt(data.yMax, 10))) ? o.yMax : data.yMax;
  636. if (self._vis) {
  637. self._destroy(self._vis, self._mainStorage);
  638. }
  639. self.setType(data.type, true);
  640. function _mapData(set) {
  641. var d = _.map(_.clone(set.data), function (p) {
  642. var np = _.clone(p);
  643. if (p.hasOwnProperty('x')) {
  644. np.x = o.dataFormatX(p.x);
  645. }
  646. if (p.hasOwnProperty('y')) {
  647. np.y = o.dataFormatY(p.y);
  648. }
  649. return np;
  650. }).sort(o.sortX);
  651. return _.extend(_.clone(set), { data: d });
  652. }
  653. nData.main = _.map(nData.main, _mapData);
  654. self._mainData = nData.main;
  655. self._xScaleType = nData.xScale;
  656. self._yScaleType = nData.yScale;
  657. if (nData.hasOwnProperty('comp')) {
  658. nData.comp = _.map(nData.comp, _mapData);
  659. self._compData = nData.comp;
  660. } else {
  661. self._compData = [];
  662. }
  663. self._draw();
  664. },
  665. /**
  666. * Change the scale of an axis
  667. *
  668. * @param string axis Name of an axis. One of 'x' or 'y'
  669. * @param string type Name of the scale type
  670. *
  671. */
  672. setScale: function (axis, type) {
  673. var self = this;
  674. switch (axis) {
  675. case 'x':
  676. self._xScaleType = type;
  677. break;
  678. case 'y':
  679. self._yScaleType = type;
  680. break;
  681. default:
  682. throw 'Cannot change scale of unknown axis "' + axis + '".';
  683. }
  684. self._draw();
  685. },
  686. /**
  687. * Create the SVG element and g container. Resize if necessary.
  688. */
  689. _drawSvg: function () {
  690. var self = this,
  691. c = self._container,
  692. options = self._options,
  693. width = parseInt(c.style('width').replace('px', ''), 10),
  694. height = parseInt(c.style('height').replace('px', ''), 10),
  695. svg,
  696. g,
  697. gScale;
  698. svg = c.selectAll('svg')
  699. .data(emptyData);
  700. svg.enter().append('svg')
  701. // Inherit the height and width from the parent element
  702. .attr('height', height)
  703. .attr('width', width)
  704. .attr('class', 'xchart');
  705. svg.transition()
  706. .attr('width', width)
  707. .attr('height', height);
  708. g = svg.selectAll('g')
  709. .data(emptyData);
  710. g.enter().append('g')
  711. .attr(
  712. 'transform',
  713. 'translate(' + options.paddingLeft + ',' + options.paddingTop + ')'
  714. );
  715. gScale = g.selectAll('g.scale')
  716. .data(emptyData);
  717. gScale.enter().append('g')
  718. .attr('class', 'scale');
  719. self._svg = svg;
  720. self._g = g;
  721. self._gScale = gScale;
  722. self._height = height - options.paddingTop - options.paddingBottom -
  723. options.axisPaddingTop - options.axisPaddingBottom;
  724. self._width = width - options.paddingLeft - options.paddingRight -
  725. options.axisPaddingLeft - options.axisPaddingRight;
  726. },
  727. /**
  728. * Resize the visualization
  729. */
  730. _resize: function (event) {
  731. var self = this;
  732. self._drawSvg();
  733. self._draw();
  734. },
  735. /**
  736. * Draw the x and y axes
  737. */
  738. _drawAxes: function () {
  739. if (this._noData) {
  740. return;
  741. }
  742. var self = this,
  743. o = self._options,
  744. t = self._gScale.transition().duration(o.timing),
  745. xTicks = o.tickHintX,
  746. yTicks = o.tickHintY,
  747. bottom = self._height + o.axisPaddingTop + o.axisPaddingBottom,
  748. zeroLine = d3.svg.line().x(function (d) { return d; }),
  749. zLine,
  750. zLinePath,
  751. xAxis,
  752. xRules,
  753. yAxis,
  754. yRules,
  755. labels;
  756. xRules = d3.svg.axis()
  757. .scale(self.xScale)
  758. .ticks(xTicks)
  759. .tickSize(-self._height)
  760. .tickFormat(o.tickFormatX)
  761. .orient('bottom');
  762. xAxis = self._gScale.selectAll('g.axisX')
  763. .data(emptyData);
  764. xAxis.enter().append('g')
  765. .attr('class', 'axis axisX')
  766. .attr('transform', 'translate(0,' + bottom + ')');
  767. xAxis.call(xRules);
  768. labels = self._gScale.selectAll('.axisX g')[0];
  769. if (labels.length > (self._width / 80)) {
  770. labels.sort(function (a, b) {
  771. var r = /translate\(([^,)]+)/;
  772. a = a.getAttribute('transform').match(r);
  773. b = b.getAttribute('transform').match(r);
  774. return parseFloat(a[1], 10) - parseFloat(b[1], 10);
  775. });
  776. d3.selectAll(labels)
  777. .filter(function (d, i) {
  778. return i % (Math.ceil(labels.length / xTicks) + 1);
  779. })
  780. .remove();
  781. }
  782. yRules = d3.svg.axis()
  783. .scale(self.yScale)
  784. .ticks(yTicks)
  785. .tickSize(-self._width - o.axisPaddingRight - o.axisPaddingLeft)
  786. .tickFormat(o.tickFormatY)
  787. .orient('left');
  788. yAxis = self._gScale.selectAll('g.axisY')
  789. .data(emptyData);
  790. yAxis.enter().append('g')
  791. .attr('class', 'axis axisY')
  792. .attr('transform', 'translate(0,0)');
  793. t.selectAll('g.axisY')
  794. .call(yRules);
  795. // zero line
  796. zLine = self._gScale.selectAll('g.axisZero')
  797. .data([[]]);
  798. zLine.enter().append('g')
  799. .attr('class', 'axisZero');
  800. zLinePath = zLine.selectAll('line')
  801. .data([[]]);
  802. zLinePath.enter().append('line')
  803. .attr('x1', 0)
  804. .attr('x2', self._width + o.axisPaddingLeft + o.axisPaddingRight)
  805. .attr('y1', self.yZero)
  806. .attr('y2', self.yZero);
  807. zLinePath.transition().duration(o.timing)
  808. .attr('y1', self.yZero)
  809. .attr('y2', self.yZero);
  810. },
  811. /**
  812. * Update the x and y scales (used when drawing)
  813. *
  814. * Optional methods in drawing types:
  815. * preUpdateScale
  816. * postUpdateScale
  817. *
  818. * Example implementation in vis type:
  819. *
  820. * function postUpdateScale(self, scaleData, mainData, compData) {
  821. * self.xScale2 = d3.scale.ordinal()
  822. * .domain(d3.range(0, mainData.length))
  823. * .rangeRoundBands([0, self.xScale.rangeBand()], 0.08);
  824. * }
  825. *
  826. */
  827. _updateScale: function () {
  828. var self = this,
  829. _unionData = function () {
  830. return _.union(self._mainData, self._compData);
  831. },
  832. scaleData = _unionData(),
  833. vis = self._vis,
  834. scale,
  835. min;
  836. delete self.xScale;
  837. delete self.yScale;
  838. delete self.yZero;
  839. if (vis.hasOwnProperty('preUpdateScale')) {
  840. vis.preUpdateScale(self, scaleData, self._mainData, self._compData);
  841. }
  842. // Just in case preUpdateScale modified
  843. scaleData = _unionData();
  844. scale = _scales.xy(self, scaleData, self._xScaleType, self._yScaleType);
  845. self.xScale = scale.x;
  846. self.yScale = scale.y;
  847. min = self.yScale.domain()[0];
  848. self.yZero = (min > 0) ? self.yScale(min) : self.yScale(0);
  849. if (vis.hasOwnProperty('postUpdateScale')) {
  850. vis.postUpdateScale(self, scaleData, self._mainData, self._compData);
  851. }
  852. },
  853. /**
  854. * Create (Enter) the elements for the vis
  855. *
  856. * Required method
  857. *
  858. * Example implementation in vis type:
  859. *
  860. * function enter(self, data, callbacks) {
  861. * var foo = self._g.selectAll('g.foobar')
  862. * .data(data);
  863. * foo.enter().append('g')
  864. * .attr('class', 'foobar');
  865. * self.foo = foo;
  866. * }
  867. */
  868. _enter: function (vis, storage, data, className) {
  869. var self = this,
  870. callbacks = {
  871. click: self._options.click,
  872. mouseover: self._options.mouseover,
  873. mouseout: self._options.mouseout
  874. };
  875. self._checkVisMethod(vis, 'enter');
  876. vis.enter(self, storage, className, data, callbacks);
  877. },
  878. /**
  879. * Update the elements opened by the select method
  880. *
  881. * Required method
  882. *
  883. * Example implementation in vis type:
  884. *
  885. * function update(self, timing) {
  886. * self.bars.transition().duration(timing)
  887. * .attr('width', self.xScale2.rangeBand())
  888. * .attr('height', function (d) {
  889. * return self.yScale(d.y);
  890. * });
  891. * }
  892. */
  893. _update: function (vis, storage) {
  894. var self = this;
  895. self._checkVisMethod(vis, 'update');
  896. vis.update(self, storage, self._options.timing);
  897. },
  898. /**
  899. * Remove or transition out the elements that no longer have data
  900. *
  901. * Required method
  902. *
  903. * Example implementation in vis type:
  904. *
  905. * function exit(self) {
  906. * self.bars.exit().remove();
  907. * }
  908. */
  909. _exit: function (vis, storage) {
  910. var self = this;
  911. self._checkVisMethod(vis, 'exit');
  912. vis.exit(self, storage, self._options.timing);
  913. },
  914. /**
  915. * Destroy the current vis type (transition to new type)
  916. *
  917. * Required method
  918. *
  919. * Example implementation in vis type:
  920. *
  921. * function destroy(self, timing) {
  922. * self.bars.transition().duration(timing)
  923. * attr('height', 0);
  924. * delete self.bars;
  925. * }
  926. */
  927. _destroy: function (vis, storage) {
  928. var self = this;
  929. self._checkVisMethod(vis, 'destroy');
  930. try {
  931. vis.destroy(self, storage, self._options.timing);
  932. } catch (e) {}
  933. },
  934. /**
  935. * Draw the visualization
  936. */
  937. _draw: function () {
  938. var self = this,
  939. o = self._options,
  940. comp,
  941. compKeys;
  942. self._noData = _.flatten(_.pluck(self._mainData, 'data')
  943. .concat(_.pluck(self._compData, 'data'))).length === 0;
  944. self._updateScale();
  945. self._drawAxes();
  946. self._enter(self._vis, self._mainStorage, self._mainData, '.main');
  947. self._exit(self._vis, self._mainStorage);
  948. self._update(self._vis, self._mainStorage);
  949. comp = _.chain(self._compData).groupBy(function (d) {
  950. return d.type;
  951. });
  952. compKeys = comp.keys();
  953. // Find old comp vis items and remove any that no longer exist
  954. _.each(self._compStorage, function (d, key) {
  955. if (-1 === compKeys.indexOf(key).value()) {
  956. var vis = _vis[key];
  957. self._enter(vis, d, [], '.comp.' + key.replace(/\W+/g, ''));
  958. self._exit(vis, d);
  959. }
  960. });
  961. comp.each(function (d, key) {
  962. var vis = _vis[key], storage;
  963. if (!self._compStorage.hasOwnProperty(key)) {
  964. self._compStorage[key] = {};
  965. }
  966. storage = self._compStorage[key];
  967. self._enter(vis, storage, d, '.comp.' + key.replace(/\W+/g, ''));
  968. self._exit(vis, storage);
  969. self._update(vis, storage);
  970. });
  971. if (self._noData) {
  972. o.empty(self, self._selector, self._mainData);
  973. } else {
  974. o.notempty(self, self._selector);
  975. }
  976. },
  977. /**
  978. * Ensure drawing method exists
  979. */
  980. _checkVisMethod: function (vis, method) {
  981. var self = this;
  982. if (!vis[method]) {
  983. throw 'Required method "' + method + '" not found on vis type "' +
  984. self._type + '".';
  985. }
  986. }
  987. });
  988. if (typeof define === 'function' && define.amd && typeof define.amd === 'object') {
  989. define(function () {
  990. return xChart;
  991. });
  992. return;
  993. }
  994. window.xChart = xChart;
  995. }());