xcharts-rails 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b06e86246b2a3823363bd102b3bae6124e5aa456
4
+ data.tar.gz: 355c8a203bbb4c68d472bf94bc227b3a9662afb1
5
+ SHA512:
6
+ metadata.gz: 64dba6c8161ce1079d758183d7732207895b7746cdfeb0979c6192d1bccda85a7dca9e385103c349d928c85408b6895d6d1dac31cd6852c2bee04d9a35923aff
7
+ data.tar.gz: fdd0bfabbcdf9af52a1164201136d64323c0b88432e94e9cefa70f0948aea473f936a6633ef7912f07e720b45c7973de2000547f12d7eac382192fecb7132280
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ xcharts
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in xcharts_rails.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Orlando Del Aguila
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # xcharts-rails gem
2
+
3
+ A gem to add [Xcharts](http://tenxer.github.io/xcharts/) into your Rails application Assets Pipeline.
4
+
5
+ This repository is a fork of [orlando/xcharts_rails](https://github.com/orlando/xcharts_rails).
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'xcharts-rails'
12
+
13
+ And then execute:
14
+
15
+ $ bundle install
16
+
17
+ Now add `xcharts` to `application.js` and `application.css`:
18
+
19
+ Open `application.js` and add the following lines:
20
+
21
+ //= require d3.v3
22
+ //= require xcharts
23
+
24
+ Then open `application.css` and add the following line:
25
+
26
+ *= require xcharts
27
+
28
+ Now you are ready to follow [the Xcharts documentation](http://tenxer.github.io/xcharts/docs/#data).
29
+
30
+ ## Contributing
31
+
32
+ 1. Fork it
33
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
34
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
35
+ 4. Push to the branch (`git push origin my-new-feature`)
36
+ 5. Create new Pull Request
37
+
38
+ ## Copyright
39
+
40
+ Copyright (c) 2014 Guillaume Hain. See LICENSE.txt for
41
+ further details.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,5 @@
1
+ require 'xcharts_rails/version'
2
+
3
+ module XchartsRails
4
+ require 'xcharts_rails/engine' if defined?(Rails)
5
+ end
@@ -0,0 +1,10 @@
1
+ require 'rails'
2
+
3
+ module XchartsRails
4
+ class Engine < ::Rails::Engine
5
+ initializer 'xcharts_rails.setup',
6
+ :group => :all do |app|
7
+ app.paths['config'] << File.join(config.root, 'vendor')
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ module XchartsRails
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,1158 @@
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
+
6
+ (function () {
7
+
8
+ var xChart,
9
+ _vis = {},
10
+ _scales = {},
11
+ _visutils = {};
12
+ (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) {
13
+ return _.chain(_.range(zIndex, 10)).reverse().map(function (z) {
14
+ return 'g[data-index="' + z + '"]';
15
+ }).value().join(', ');
16
+ }
17
+
18
+ function colorClass(el, i) {
19
+ var c = el.getAttribute('class');
20
+ return ((c !== null) ? c.replace(/color\d+/g, '') : '') + ' color' + i;
21
+ }
22
+
23
+ _visutils = {
24
+ getInsertionPoint: getInsertionPoint,
25
+ colorClass: colorClass
26
+ };
27
+ var local = this,
28
+ defaultSpacing = 0.25;
29
+
30
+ function _getDomain(data, axis) {
31
+ return _.chain(data)
32
+ .pluck('data')
33
+ .flatten()
34
+ .pluck(axis)
35
+ .uniq()
36
+ .filter(function (d) {
37
+ return d !== undefined && d !== null;
38
+ })
39
+ .value()
40
+ .sort(d3.ascending);
41
+ }
42
+
43
+ _scales.ordinal = function (data, axis, bounds, extents) {
44
+ var domain = _getDomain(data, axis);
45
+ return d3.scale.ordinal()
46
+ .domain(domain)
47
+ .rangeRoundBands(bounds, defaultSpacing);
48
+ };
49
+
50
+ _scales.linear = function (data, axis, bounds, extents) {
51
+ return d3.scale.linear()
52
+ .domain(extents)
53
+ .nice()
54
+ .rangeRound(bounds);
55
+ };
56
+
57
+ _scales.exponential = function (data, axis, bounds, extents) {
58
+ return d3.scale.pow()
59
+ .exponent(0.65)
60
+ .domain(extents)
61
+ .nice()
62
+ .rangeRound(bounds);
63
+ };
64
+
65
+ _scales.time = function (data, axis, bounds, extents) {
66
+ return d3.time.scale()
67
+ .domain(_.map(extents, function (d) { return new Date(d); }))
68
+ .range(bounds);
69
+ };
70
+
71
+ function _extendDomain(domain, axis) {
72
+ var min = domain[0],
73
+ max = domain[1],
74
+ diff,
75
+ e;
76
+
77
+ if (min === max) {
78
+ e = Math.max(Math.round(min / 10), 4);
79
+ min -= e;
80
+ max += e;
81
+ }
82
+
83
+ diff = max - min;
84
+ min = (min) ? min - (diff / 10) : min;
85
+ min = (domain[0] > 0) ? Math.max(min, 0) : min;
86
+ max = (max) ? max + (diff / 10) : max;
87
+ max = (domain[1] < 0) ? Math.min(max, 0) : max;
88
+
89
+ return [min, max];
90
+ }
91
+
92
+ function _getExtents(options, data, xType, yType) {
93
+ var extents,
94
+ nData = _.chain(data)
95
+ .pluck('data')
96
+ .flatten()
97
+ .value();
98
+
99
+ extents = {
100
+ x: d3.extent(nData, function (d) { return d.x; }),
101
+ y: d3.extent(nData, function (d) { return d.y; })
102
+ };
103
+
104
+ _.each([xType, yType], function (type, i) {
105
+ var axis = (i) ? 'y' : 'x',
106
+ extended;
107
+ extents[axis] = d3.extent(nData, function (d) { return d[axis]; });
108
+ if (type === 'ordinal') {
109
+ return;
110
+ }
111
+
112
+ _.each([axis + 'Min', axis + 'Max'], function (minMax, i) {
113
+ if (type !== 'time') {
114
+ extended = _extendDomain(extents[axis]);
115
+ }
116
+
117
+ if (options.hasOwnProperty(minMax) && options[minMax] !== null) {
118
+ extents[axis][i] = options[minMax];
119
+ } else if (type !== 'time') {
120
+ extents[axis][i] = extended[i];
121
+ }
122
+ });
123
+ });
124
+
125
+ return extents;
126
+ }
127
+
128
+ _scales.xy = function (self, data, xType, yType) {
129
+ var o = self._options,
130
+ extents = _getExtents(o, data, xType, yType),
131
+ scales = {},
132
+ horiz = [o.axisPaddingLeft, self._width],
133
+ vert = [self._height, o.axisPaddingTop],
134
+ xScale,
135
+ yScale;
136
+
137
+ _.each([xType, yType], function (type, i) {
138
+ var axis = (i === 0) ? 'x' : 'y',
139
+ bounds = (i === 0) ? horiz : vert,
140
+ fn = xChart.getScale(type);
141
+ scales[axis] = fn(data, axis, bounds, extents[axis]);
142
+ });
143
+
144
+ return scales;
145
+ };
146
+ (function () {
147
+ var zIndex = 2,
148
+ selector = 'g.bar',
149
+ insertBefore = _visutils.getInsertionPoint(zIndex);
150
+
151
+ function postUpdateScale(self, scaleData, mainData, compData) {
152
+ self.xScale2 = d3.scale.ordinal()
153
+ .domain(d3.range(0, mainData.length))
154
+ .rangeRoundBands([0, self.xScale.rangeBand()], 0.08);
155
+ }
156
+
157
+ function enter(self, storage, className, data, callbacks) {
158
+ var barGroups, bars,
159
+ yZero = self.yZero;
160
+
161
+ barGroups = self._g.selectAll(selector + className)
162
+ .data(data, function (d) {
163
+ return d.className;
164
+ });
165
+
166
+ barGroups.enter().insert('g', insertBefore)
167
+ .attr('data-index', zIndex)
168
+ .style('opacity', 0)
169
+ .attr('class', function (d, i) {
170
+ var cl = _.uniq((className + d.className).split('.')).join(' ');
171
+ return cl + ' bar ' + _visutils.colorClass(this, i);
172
+ })
173
+ .attr('transform', function (d, i) {
174
+ return 'translate(' + self.xScale2(i) + ',0)';
175
+ });
176
+
177
+ bars = barGroups.selectAll('rect')
178
+ .data(function (d) {
179
+ return d.data;
180
+ }, function (d) {
181
+ return d.x;
182
+ });
183
+
184
+ bars.enter().append('rect')
185
+ .attr('width', 0)
186
+ .attr('rx', 3)
187
+ .attr('ry', 3)
188
+ .attr('x', function (d) {
189
+ return self.xScale(d.x) + (self.xScale2.rangeBand() / 2);
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
+ .on('mouseover', callbacks.mouseover)
198
+ .on('mouseout', callbacks.mouseout)
199
+ .on('click', callbacks.click);
200
+
201
+ storage.barGroups = barGroups;
202
+ storage.bars = bars;
203
+ }
204
+
205
+ function update(self, storage, timing) {
206
+ var yZero = self.yZero;
207
+
208
+ storage.barGroups
209
+ .attr('class', function (d, i) {
210
+ return _visutils.colorClass(this, i);
211
+ })
212
+ .transition().duration(timing)
213
+ .style('opacity', 1)
214
+ .attr('transform', function (d, i) {
215
+ return 'translate(' + self.xScale2(i) + ',0)';
216
+ });
217
+
218
+ storage.bars.transition().duration(timing)
219
+ .attr('width', self.xScale2.rangeBand())
220
+ .attr('x', function (d) {
221
+ return self.xScale(d.x);
222
+ })
223
+ .attr('height', function (d) {
224
+ return Math.abs(yZero - self.yScale(d.y));
225
+ })
226
+ .attr('y', function (d) {
227
+ return (d.y < 0) ? yZero : self.yScale(d.y);
228
+ });
229
+ }
230
+
231
+ function exit(self, storage, timing) {
232
+ storage.bars.exit()
233
+ .transition().duration(timing)
234
+ .attr('width', 0)
235
+ .remove();
236
+ storage.barGroups.exit()
237
+ .transition().duration(timing)
238
+ .style('opacity', 0)
239
+ .remove();
240
+ }
241
+
242
+ function destroy(self, storage, timing) {
243
+ var band = (self.xScale2) ? self.xScale2.rangeBand() / 2 : 0;
244
+ delete self.xScale2;
245
+ storage.bars
246
+ .transition().duration(timing)
247
+ .attr('width', 0)
248
+ .attr('x', function (d) {
249
+ return self.xScale(d.x) + band;
250
+ });
251
+ }
252
+
253
+ _vis.bar = {
254
+ postUpdateScale: postUpdateScale,
255
+ enter: enter,
256
+ update: update,
257
+ exit: exit,
258
+ destroy: destroy
259
+ };
260
+ }());
261
+ (function () {
262
+
263
+ var zIndex = 3,
264
+ selector = 'g.line',
265
+ insertBefore = _visutils.getInsertionPoint(zIndex);
266
+
267
+ function enter(self, storage, className, data, callbacks) {
268
+ var inter = self._options.interpolation,
269
+ x = function (d, i) {
270
+ if (!self.xScale2 && !self.xScale.rangeBand) {
271
+ return self.xScale(d.x);
272
+ }
273
+ return self.xScale(d.x) + (self.xScale.rangeBand() / 2);
274
+ },
275
+ y = function (d) { return self.yScale(d.y); },
276
+ line = d3.svg.line()
277
+ .x(x)
278
+ .interpolate(inter),
279
+ area = d3.svg.area()
280
+ .x(x)
281
+ .y1(self.yZero)
282
+ .interpolate(inter),
283
+ container,
284
+ fills,
285
+ paths;
286
+
287
+ function datum(d) {
288
+ return [d.data];
289
+ }
290
+
291
+ container = self._g.selectAll(selector + className)
292
+ .data(data, function (d) {
293
+ return d.className;
294
+ });
295
+
296
+ container.enter().insert('g', insertBefore)
297
+ .attr('data-index', zIndex)
298
+ .attr('class', function (d, i) {
299
+ var cl = _.uniq((className + d.className).split('.')).join(' ');
300
+ return cl + ' line ' + _visutils.colorClass(this, i);
301
+ });
302
+
303
+ fills = container.selectAll('path.fill')
304
+ .data(datum);
305
+
306
+ fills.enter().append('path')
307
+ .attr('class', 'fill')
308
+ .style('opacity', 0)
309
+ .attr('d', area.y0(y));
310
+
311
+ paths = container.selectAll('path.line')
312
+ .data(datum);
313
+
314
+ paths.enter().append('path')
315
+ .attr('class', 'line')
316
+ .style('opacity', 0)
317
+ .attr('d', line.y(y));
318
+
319
+ storage.lineContainers = container;
320
+ storage.lineFills = fills;
321
+ storage.linePaths = paths;
322
+ storage.lineX = x;
323
+ storage.lineY = y;
324
+ storage.lineA = area;
325
+ storage.line = line;
326
+ }
327
+
328
+ function update(self, storage, timing) {
329
+ storage.lineContainers
330
+ .attr('class', function (d, i) {
331
+ return _visutils.colorClass(this, i);
332
+ });
333
+
334
+ storage.lineFills.transition().duration(timing)
335
+ .style('opacity', 1)
336
+ .attr('d', storage.lineA.y0(storage.lineY));
337
+
338
+ storage.linePaths.transition().duration(timing)
339
+ .style('opacity', 1)
340
+ .attr('d', storage.line.y(storage.lineY));
341
+ }
342
+
343
+ function exit(self, storage) {
344
+ storage.linePaths.exit()
345
+ .style('opacity', 0)
346
+ .remove();
347
+ storage.lineFills.exit()
348
+ .style('opacity', 0)
349
+ .remove();
350
+
351
+ storage.lineContainers.exit()
352
+ .remove();
353
+ }
354
+
355
+ function destroy(self, storage, timing) {
356
+ storage.linePaths.transition().duration(timing)
357
+ .style('opacity', 0);
358
+ storage.lineFills.transition().duration(timing)
359
+ .style('opacity', 0);
360
+ }
361
+
362
+ _vis.line = {
363
+ enter: enter,
364
+ update: update,
365
+ exit: exit,
366
+ destroy: destroy
367
+ };
368
+ }());
369
+ (function () {
370
+ var line = _vis.line;
371
+
372
+ function enter(self, storage, className, data, callbacks) {
373
+ var circles;
374
+
375
+ line.enter(self, storage, className, data, callbacks);
376
+
377
+ circles = storage.lineContainers.selectAll('circle')
378
+ .data(function (d) {
379
+ return d.data;
380
+ }, function (d) {
381
+ return d.x;
382
+ });
383
+
384
+ circles.enter().append('circle')
385
+ .style('opacity', 0)
386
+ .attr('cx', storage.lineX)
387
+ .attr('cy', storage.lineY)
388
+ .attr('r', 5)
389
+ .on('mouseover', callbacks.mouseover)
390
+ .on('mouseout', callbacks.mouseout)
391
+ .on('click', callbacks.click);
392
+
393
+ storage.lineCircles = circles;
394
+ }
395
+
396
+ function update(self, storage, timing) {
397
+ line.update.apply(null, _.toArray(arguments));
398
+
399
+ storage.lineCircles.transition().duration(timing)
400
+ .style('opacity', 1)
401
+ .attr('cx', storage.lineX)
402
+ .attr('cy', storage.lineY);
403
+ }
404
+
405
+ function exit(self, storage) {
406
+ storage.lineCircles.exit()
407
+ .remove();
408
+ line.exit.apply(null, _.toArray(arguments));
409
+ }
410
+
411
+ function destroy(self, storage, timing) {
412
+ line.destroy.apply(null, _.toArray(arguments));
413
+ if (!storage.lineCircles) {
414
+ return;
415
+ }
416
+ storage.lineCircles.transition().duration(timing)
417
+ .style('opacity', 0);
418
+ }
419
+
420
+ _vis['line-dotted'] = {
421
+ enter: enter,
422
+ update: update,
423
+ exit: exit,
424
+ destroy: destroy
425
+ };
426
+ }());
427
+ (function () {
428
+ var line = _vis['line-dotted'];
429
+
430
+ function enter(self, storage, className, data, callbacks) {
431
+ line.enter(self, storage, className, data, callbacks);
432
+ }
433
+
434
+ function _accumulate_data(data) {
435
+ function reduce(memo, num) {
436
+ return memo + num.y;
437
+ }
438
+
439
+ var nData = _.map(data, function (set) {
440
+ var i = set.data.length,
441
+ d = _.clone(set.data);
442
+ set = _.clone(set);
443
+ while (i) {
444
+ i -= 1;
445
+ // Need to clone here, otherwise we are actually setting the same
446
+ // data onto the original data set.
447
+ d[i] = _.clone(set.data[i]);
448
+ d[i].y0 = set.data[i].y;
449
+ d[i].y = _.reduce(_.first(set.data, i), reduce, set.data[i].y);
450
+ }
451
+ return _.extend(set, { data: d });
452
+ });
453
+
454
+ return nData;
455
+ }
456
+
457
+ function _resetData(self) {
458
+ if (!self.hasOwnProperty('cumulativeOMainData')) {
459
+ return;
460
+ }
461
+ self._mainData = self.cumulativeOMainData;
462
+ delete self.cumulativeOMainData;
463
+ self._compData = self.cumulativeOCompData;
464
+ delete self.cumulativeOCompData;
465
+ }
466
+
467
+ function preUpdateScale(self, data) {
468
+ _resetData(self);
469
+ self.cumulativeOMainData = self._mainData;
470
+ self._mainData = _accumulate_data(self._mainData);
471
+ self.cumulativeOCompData = self._compData;
472
+ self._compData = _accumulate_data(self._compData);
473
+ }
474
+
475
+ function destroy(self, storage, timing) {
476
+ _resetData(self);
477
+ line.destroy.apply(null, _.toArray(arguments));
478
+ }
479
+
480
+ _vis.cumulative = {
481
+ preUpdateScale: preUpdateScale,
482
+ enter: enter,
483
+ update: line.update,
484
+ exit: line.exit,
485
+ destroy: destroy
486
+ };
487
+ }());
488
+ var emptyData = [[]],
489
+ defaults = {
490
+ // User interaction callbacks
491
+ mouseover: function (data, i) {},
492
+ mouseout: function (data, i) {},
493
+ click: function (data, i) {},
494
+
495
+ // Padding between the axes and the contents of the chart
496
+ axisPaddingTop: 0,
497
+ axisPaddingRight: 0,
498
+ axisPaddingBottom: 5,
499
+ axisPaddingLeft: 20,
500
+
501
+ // Padding around the edge of the chart (space for axis labels, etc)
502
+ paddingTop: 0,
503
+ paddingRight: 0,
504
+ paddingBottom: 20,
505
+ paddingLeft: 60,
506
+
507
+ // Axis tick formatting
508
+ tickHintX: 10,
509
+ tickFormatX: function (x) { return x; },
510
+ tickHintY: 10,
511
+ tickFormatY: function (y) { return y; },
512
+
513
+ // Min/Max Axis Values
514
+ xMin: null,
515
+ xMax: null,
516
+ yMin: null,
517
+ yMax: null,
518
+
519
+ // Pre-format input data
520
+ dataFormatX: function (x) { return x; },
521
+ dataFormatY: function (y) { return y; },
522
+
523
+ unsupported: function (selector) {
524
+ d3.select(selector).text('SVG is not supported on your browser');
525
+ },
526
+
527
+ // Callback functions if no data
528
+ empty: function (self, selector, d) {},
529
+ notempty: function (self, selector) {},
530
+
531
+ timing: 750,
532
+
533
+ // Line interpolation
534
+ interpolation: 'monotone',
535
+
536
+ // Data sorting
537
+ sortX: function (a, b) {
538
+ return (!a.x && !b.x) ? 0 : (a.x < b.x) ? -1 : 1;
539
+ }
540
+ };
541
+
542
+ // What/how should the warning/error be presented?
543
+ function svgEnabled() {
544
+ var d = document;
545
+ return (!!d.createElementNS &&
546
+ !!d.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect);
547
+ }
548
+
549
+ /**
550
+ * Creates a new chart
551
+ *
552
+ * @param string type The drawing type for the main data
553
+ * @param array data Data to render in the chart
554
+ * @param string selector CSS Selector for the parent element for the chart
555
+ * @param object options Optional. See `defaults` for options
556
+ *
557
+ * Examples:
558
+ * var data = {
559
+ * "main": [
560
+ * {
561
+ * "data": [
562
+ * {
563
+ * "x": "2012-08-09T07:00:00.522Z",
564
+ * "y": 68
565
+ * },
566
+ * {
567
+ * "x": "2012-08-10T07:00:00.522Z",
568
+ * "y": 295
569
+ * },
570
+ * {
571
+ * "x": "2012-08-11T07:00:00.522Z",
572
+ * "y": 339
573
+ * },
574
+ * ],
575
+ * "className": ".foo"
576
+ * }
577
+ * ],
578
+ * "xScale": "ordinal",
579
+ * "yScale": "linear",
580
+ * "comp": [
581
+ * {
582
+ * "data": [
583
+ * {
584
+ * "x": "2012-08-09T07:00:00.522Z",
585
+ * "y": 288
586
+ * },
587
+ * {
588
+ * "x": "2012-08-10T07:00:00.522Z",
589
+ * "y": 407
590
+ * },
591
+ * {
592
+ * "x": "2012-08-11T07:00:00.522Z",
593
+ * "y": 459
594
+ * }
595
+ * ],
596
+ * "className": ".comp.comp_foo",
597
+ * "type": "line-arrowed"
598
+ * }
599
+ * ]
600
+ * },
601
+ * myChart = new Chart('bar', data, '#chart');
602
+ *
603
+ */
604
+ function xChart(type, data, selector, options) {
605
+ var self = this,
606
+ resizeLock;
607
+
608
+ self._options = options = _.defaults(options || {}, defaults);
609
+
610
+ if (svgEnabled() === false) {
611
+ return options.unsupported(selector);
612
+ }
613
+
614
+ self._selector = selector;
615
+ self._container = d3.select(selector);
616
+ self._drawSvg();
617
+ self._mainStorage = {};
618
+ self._compStorage = {};
619
+
620
+ data = _.clone(data);
621
+ if (type && !data.type) {
622
+ data.type = type;
623
+ }
624
+
625
+ self.setData(data);
626
+
627
+ d3.select(window).on('resize.for.' + selector, function () {
628
+ if (resizeLock) {
629
+ clearTimeout(resizeLock);
630
+ }
631
+ resizeLock = setTimeout(function () {
632
+ resizeLock = null;
633
+ self._resize();
634
+ }, 500);
635
+ });
636
+ }
637
+
638
+ /**
639
+ * Add a visualization type
640
+ *
641
+ * @param string type Unique key/name used with setType
642
+ * @param object vis object map of vis methods
643
+ */
644
+ xChart.setVis = function (type, vis) {
645
+ if (_vis.hasOwnProperty(type)) {
646
+ throw 'Cannot override vis type "' + type + '".';
647
+ }
648
+ _vis[type] = vis;
649
+ };
650
+
651
+ /**
652
+ * Get a clone of a visualization
653
+ * Useful for extending vis functionality
654
+ *
655
+ * @param string type Unique key/name of the vis
656
+ */
657
+ xChart.getVis = function (type) {
658
+ if (!_vis.hasOwnProperty(type)) {
659
+ throw 'Vis type "' + type + '" does not exist.';
660
+ }
661
+
662
+ return _.clone(_vis[type]);
663
+ };
664
+
665
+ xChart.setScale = function (name, fn) {
666
+ if (_scales.hasOwnProperty(name)) {
667
+ throw 'Scale type "' + name + '" already exists.';
668
+ }
669
+
670
+ _scales[name] = fn;
671
+ };
672
+
673
+ xChart.getScale = function (name) {
674
+ if (!_scales.hasOwnProperty(name)) {
675
+ throw 'Scale type "' + name + '" does not exist.';
676
+ }
677
+ return _scales[name];
678
+ };
679
+
680
+ xChart.visutils = _visutils;
681
+
682
+ _.defaults(xChart.prototype, {
683
+ /**
684
+ * Set or change the drawing type for the main data.
685
+ *
686
+ * @param string type Must be an available drawing type
687
+ *
688
+ */
689
+ setType: function (type, skipDraw) {
690
+ var self = this;
691
+
692
+ if (self._type && type === self._type) {
693
+ return;
694
+ }
695
+
696
+ if (!_vis.hasOwnProperty(type)) {
697
+ throw 'Vis type "' + type + '" is not defined.';
698
+ }
699
+
700
+ if (self._type) {
701
+ self._destroy(self._vis, self._mainStorage);
702
+ }
703
+
704
+ self._type = type;
705
+ self._vis = _vis[type];
706
+ if (!skipDraw) {
707
+ self._draw();
708
+ }
709
+ },
710
+
711
+ /**
712
+ * Set and update the data for the chart. Optionally skip drawing.
713
+ *
714
+ * @param object data New data. See new xChart example for format
715
+ *
716
+ */
717
+ setData: function (data) {
718
+ var self = this,
719
+ o = self._options,
720
+ nData = _.clone(data);
721
+
722
+ if (!data.hasOwnProperty('main')) {
723
+ throw 'No "main" key found in given chart data.';
724
+ }
725
+
726
+ switch (data.type) {
727
+ case 'bar':
728
+ // force the xScale to be ordinal
729
+ data.xScale = 'ordinal';
730
+ break;
731
+ case undefined:
732
+ data.type = self._type;
733
+ break;
734
+ }
735
+
736
+ o.xMin = (isNaN(parseInt(data.xMin, 10))) ? o.xMin : data.xMin;
737
+ o.xMax = (isNaN(parseInt(data.xMax, 10))) ? o.xMax : data.xMax;
738
+ o.yMin = (isNaN(parseInt(data.yMin, 10))) ? o.yMin : data.yMin;
739
+ o.yMax = (isNaN(parseInt(data.yMax, 10))) ? o.yMax : data.yMax;
740
+
741
+ if (self._vis) {
742
+ self._destroy(self._vis, self._mainStorage);
743
+ }
744
+
745
+ self.setType(data.type, true);
746
+
747
+ function _mapData(set) {
748
+ var d = _.map(_.clone(set.data), function (p) {
749
+ var np = _.clone(p);
750
+ if (p.hasOwnProperty('x')) {
751
+ np.x = o.dataFormatX(p.x);
752
+ }
753
+ if (p.hasOwnProperty('y')) {
754
+ np.y = o.dataFormatY(p.y);
755
+ }
756
+ return np;
757
+ }).sort(o.sortX);
758
+ return _.extend(_.clone(set), { data: d });
759
+ }
760
+
761
+ nData.main = _.map(nData.main, _mapData);
762
+ self._mainData = nData.main;
763
+ self._xScaleType = nData.xScale;
764
+ self._yScaleType = nData.yScale;
765
+
766
+ if (nData.hasOwnProperty('comp')) {
767
+ nData.comp = _.map(nData.comp, _mapData);
768
+ self._compData = nData.comp;
769
+ } else {
770
+ self._compData = [];
771
+ }
772
+
773
+ self._draw();
774
+ },
775
+
776
+ /**
777
+ * Change the scale of an axis
778
+ *
779
+ * @param string axis Name of an axis. One of 'x' or 'y'
780
+ * @param string type Name of the scale type
781
+ *
782
+ */
783
+ setScale: function (axis, type) {
784
+ var self = this;
785
+
786
+ switch (axis) {
787
+ case 'x':
788
+ self._xScaleType = type;
789
+ break;
790
+ case 'y':
791
+ self._yScaleType = type;
792
+ break;
793
+ default:
794
+ throw 'Cannot change scale of unknown axis "' + axis + '".';
795
+ }
796
+
797
+ self._draw();
798
+ },
799
+
800
+ /**
801
+ * Create the SVG element and g container. Resize if necessary.
802
+ */
803
+ _drawSvg: function () {
804
+ var self = this,
805
+ c = self._container,
806
+ options = self._options,
807
+ width = parseInt(c.style('width').replace('px', ''), 10),
808
+ height = parseInt(c.style('height').replace('px', ''), 10),
809
+ svg,
810
+ g,
811
+ gScale;
812
+
813
+ svg = c.selectAll('svg')
814
+ .data(emptyData);
815
+
816
+ svg.enter().append('svg')
817
+ // Inherit the height and width from the parent element
818
+ .attr('height', height)
819
+ .attr('width', width)
820
+ .attr('class', 'xchart');
821
+
822
+ svg.transition()
823
+ .attr('width', width)
824
+ .attr('height', height);
825
+
826
+ g = svg.selectAll('g')
827
+ .data(emptyData);
828
+
829
+ g.enter().append('g')
830
+ .attr(
831
+ 'transform',
832
+ 'translate(' + options.paddingLeft + ',' + options.paddingTop + ')'
833
+ );
834
+
835
+ gScale = g.selectAll('g.scale')
836
+ .data(emptyData);
837
+
838
+ gScale.enter().append('g')
839
+ .attr('class', 'scale');
840
+
841
+ self._svg = svg;
842
+ self._g = g;
843
+ self._gScale = gScale;
844
+
845
+ self._height = height - options.paddingTop - options.paddingBottom -
846
+ options.axisPaddingTop - options.axisPaddingBottom;
847
+ self._width = width - options.paddingLeft - options.paddingRight -
848
+ options.axisPaddingLeft - options.axisPaddingRight;
849
+ },
850
+
851
+ /**
852
+ * Resize the visualization
853
+ */
854
+ _resize: function (event) {
855
+ var self = this;
856
+
857
+ self._drawSvg();
858
+ self._draw();
859
+ },
860
+
861
+ /**
862
+ * Draw the x and y axes
863
+ */
864
+ _drawAxes: function () {
865
+ if (this._noData) {
866
+ return;
867
+ }
868
+ var self = this,
869
+ o = self._options,
870
+ t = self._gScale.transition().duration(o.timing),
871
+ xTicks = o.tickHintX,
872
+ yTicks = o.tickHintY,
873
+ bottom = self._height + o.axisPaddingTop + o.axisPaddingBottom,
874
+ zeroLine = d3.svg.line().x(function (d) { return d; }),
875
+ zLine,
876
+ zLinePath,
877
+ xAxis,
878
+ xRules,
879
+ yAxis,
880
+ yRules,
881
+ labels;
882
+
883
+ xRules = d3.svg.axis()
884
+ .scale(self.xScale)
885
+ .ticks(xTicks)
886
+ .tickSize(-self._height)
887
+ .tickFormat(o.tickFormatX)
888
+ .orient('bottom');
889
+
890
+ xAxis = self._gScale.selectAll('g.axisX')
891
+ .data(emptyData);
892
+
893
+ xAxis.enter().append('g')
894
+ .attr('class', 'axis axisX')
895
+ .attr('transform', 'translate(0,' + bottom + ')');
896
+
897
+ xAxis.call(xRules);
898
+
899
+ labels = self._gScale.selectAll('.axisX g')[0];
900
+ if (labels.length > (self._width / 80)) {
901
+ labels.sort(function (a, b) {
902
+ var r = /translate\(([^,)]+)/;
903
+ a = a.getAttribute('transform').match(r);
904
+ b = b.getAttribute('transform').match(r);
905
+ return parseFloat(a[1], 10) - parseFloat(b[1], 10);
906
+ });
907
+
908
+ d3.selectAll(labels)
909
+ .filter(function (d, i) {
910
+ return i % (Math.ceil(labels.length / xTicks) + 1);
911
+ })
912
+ .remove();
913
+ }
914
+
915
+ yRules = d3.svg.axis()
916
+ .scale(self.yScale)
917
+ .ticks(yTicks)
918
+ .tickSize(-self._width - o.axisPaddingRight - o.axisPaddingLeft)
919
+ .tickFormat(o.tickFormatY)
920
+ .orient('left');
921
+
922
+ yAxis = self._gScale.selectAll('g.axisY')
923
+ .data(emptyData);
924
+
925
+ yAxis.enter().append('g')
926
+ .attr('class', 'axis axisY')
927
+ .attr('transform', 'translate(0,0)');
928
+
929
+ t.selectAll('g.axisY')
930
+ .call(yRules);
931
+
932
+ // zero line
933
+ zLine = self._gScale.selectAll('g.axisZero')
934
+ .data([[]]);
935
+
936
+ zLine.enter().append('g')
937
+ .attr('class', 'axisZero');
938
+
939
+ zLinePath = zLine.selectAll('line')
940
+ .data([[]]);
941
+
942
+ zLinePath.enter().append('line')
943
+ .attr('x1', 0)
944
+ .attr('x2', self._width + o.axisPaddingLeft + o.axisPaddingRight)
945
+ .attr('y1', self.yZero)
946
+ .attr('y2', self.yZero);
947
+
948
+ zLinePath.transition().duration(o.timing)
949
+ .attr('y1', self.yZero)
950
+ .attr('y2', self.yZero);
951
+ },
952
+
953
+ /**
954
+ * Update the x and y scales (used when drawing)
955
+ *
956
+ * Optional methods in drawing types:
957
+ * preUpdateScale
958
+ * postUpdateScale
959
+ *
960
+ * Example implementation in vis type:
961
+ *
962
+ * function postUpdateScale(self, scaleData, mainData, compData) {
963
+ * self.xScale2 = d3.scale.ordinal()
964
+ * .domain(d3.range(0, mainData.length))
965
+ * .rangeRoundBands([0, self.xScale.rangeBand()], 0.08);
966
+ * }
967
+ *
968
+ */
969
+ _updateScale: function () {
970
+ var self = this,
971
+ _unionData = function () {
972
+ return _.union(self._mainData, self._compData);
973
+ },
974
+ scaleData = _unionData(),
975
+ vis = self._vis,
976
+ scale,
977
+ min;
978
+
979
+ delete self.xScale;
980
+ delete self.yScale;
981
+ delete self.yZero;
982
+
983
+ if (vis.hasOwnProperty('preUpdateScale')) {
984
+ vis.preUpdateScale(self, scaleData, self._mainData, self._compData);
985
+ }
986
+
987
+ // Just in case preUpdateScale modified
988
+ scaleData = _unionData();
989
+ scale = _scales.xy(self, scaleData, self._xScaleType, self._yScaleType);
990
+
991
+ self.xScale = scale.x;
992
+ self.yScale = scale.y;
993
+
994
+ min = self.yScale.domain()[0];
995
+ self.yZero = (min > 0) ? self.yScale(min) : self.yScale(0);
996
+
997
+ if (vis.hasOwnProperty('postUpdateScale')) {
998
+ vis.postUpdateScale(self, scaleData, self._mainData, self._compData);
999
+ }
1000
+ },
1001
+
1002
+ /**
1003
+ * Create (Enter) the elements for the vis
1004
+ *
1005
+ * Required method
1006
+ *
1007
+ * Example implementation in vis type:
1008
+ *
1009
+ * function enter(self, data, callbacks) {
1010
+ * var foo = self._g.selectAll('g.foobar')
1011
+ * .data(data);
1012
+ * foo.enter().append('g')
1013
+ * .attr('class', 'foobar');
1014
+ * self.foo = foo;
1015
+ * }
1016
+ */
1017
+ _enter: function (vis, storage, data, className) {
1018
+ var self = this,
1019
+ callbacks = {
1020
+ click: self._options.click,
1021
+ mouseover: self._options.mouseover,
1022
+ mouseout: self._options.mouseout
1023
+ };
1024
+ self._checkVisMethod(vis, 'enter');
1025
+ vis.enter(self, storage, className, data, callbacks);
1026
+ },
1027
+
1028
+ /**
1029
+ * Update the elements opened by the select method
1030
+ *
1031
+ * Required method
1032
+ *
1033
+ * Example implementation in vis type:
1034
+ *
1035
+ * function update(self, timing) {
1036
+ * self.bars.transition().duration(timing)
1037
+ * .attr('width', self.xScale2.rangeBand())
1038
+ * .attr('height', function (d) {
1039
+ * return self.yScale(d.y);
1040
+ * });
1041
+ * }
1042
+ */
1043
+ _update: function (vis, storage) {
1044
+ var self = this;
1045
+ self._checkVisMethod(vis, 'update');
1046
+ vis.update(self, storage, self._options.timing);
1047
+ },
1048
+
1049
+ /**
1050
+ * Remove or transition out the elements that no longer have data
1051
+ *
1052
+ * Required method
1053
+ *
1054
+ * Example implementation in vis type:
1055
+ *
1056
+ * function exit(self) {
1057
+ * self.bars.exit().remove();
1058
+ * }
1059
+ */
1060
+ _exit: function (vis, storage) {
1061
+ var self = this;
1062
+ self._checkVisMethod(vis, 'exit');
1063
+ vis.exit(self, storage, self._options.timing);
1064
+ },
1065
+
1066
+ /**
1067
+ * Destroy the current vis type (transition to new type)
1068
+ *
1069
+ * Required method
1070
+ *
1071
+ * Example implementation in vis type:
1072
+ *
1073
+ * function destroy(self, timing) {
1074
+ * self.bars.transition().duration(timing)
1075
+ * attr('height', 0);
1076
+ * delete self.bars;
1077
+ * }
1078
+ */
1079
+ _destroy: function (vis, storage) {
1080
+ var self = this;
1081
+ self._checkVisMethod(vis, 'destroy');
1082
+ try {
1083
+ vis.destroy(self, storage, self._options.timing);
1084
+ } catch (e) {}
1085
+ },
1086
+
1087
+ /**
1088
+ * Draw the visualization
1089
+ */
1090
+ _draw: function () {
1091
+ var self = this,
1092
+ o = self._options,
1093
+ comp,
1094
+ compKeys;
1095
+
1096
+ self._noData = _.flatten(_.pluck(self._mainData, 'data')
1097
+ .concat(_.pluck(self._compData, 'data'))).length === 0;
1098
+
1099
+ self._updateScale();
1100
+ self._drawAxes();
1101
+
1102
+ self._enter(self._vis, self._mainStorage, self._mainData, '.main');
1103
+ self._exit(self._vis, self._mainStorage);
1104
+ self._update(self._vis, self._mainStorage);
1105
+
1106
+ comp = _.chain(self._compData).groupBy(function (d) {
1107
+ return d.type;
1108
+ });
1109
+ compKeys = comp.keys();
1110
+
1111
+ // Find old comp vis items and remove any that no longer exist
1112
+ _.each(self._compStorage, function (d, key) {
1113
+ if (-1 === compKeys.indexOf(key).value()) {
1114
+ var vis = _vis[key];
1115
+ self._enter(vis, d, [], '.comp.' + key.replace(/\W+/g, ''));
1116
+ self._exit(vis, d);
1117
+ }
1118
+ });
1119
+
1120
+ comp.each(function (d, key) {
1121
+ var vis = _vis[key], storage;
1122
+ if (!self._compStorage.hasOwnProperty(key)) {
1123
+ self._compStorage[key] = {};
1124
+ }
1125
+ storage = self._compStorage[key];
1126
+ self._enter(vis, storage, d, '.comp.' + key.replace(/\W+/g, ''));
1127
+ self._exit(vis, storage);
1128
+ self._update(vis, storage);
1129
+ });
1130
+
1131
+ if (self._noData) {
1132
+ o.empty(self, self._selector, self._mainData);
1133
+ } else {
1134
+ o.notempty(self, self._selector);
1135
+ }
1136
+ },
1137
+
1138
+ /**
1139
+ * Ensure drawing method exists
1140
+ */
1141
+ _checkVisMethod: function (vis, method) {
1142
+ var self = this;
1143
+ if (!vis[method]) {
1144
+ throw 'Required method "' + method + '" not found on vis type "' +
1145
+ self._type + '".';
1146
+ }
1147
+ }
1148
+ });
1149
+ if (typeof define === 'function' && define.amd && typeof define.amd === 'object') {
1150
+ define(function () {
1151
+ return xChart;
1152
+ });
1153
+ return;
1154
+ }
1155
+
1156
+ window.xChart = xChart;
1157
+
1158
+ }());