1 
  2 /**
  3  * @fileoverview Simple pie chart implementation.
  4  * @version 1.0.1
  5  * @link http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml
  6  * @link https://developers.google.com/closure/compiler/docs/js-for-compiler
  7  */
  8 
  9 
 10 
 11 /**
 12  * TagChart constructor.
 13  * @param {string|Element} container The HTML container.
 14  * @constructor
 15  * @extends {charts.BaseChart} charts.BaseChart
 16  * @requires formatters.NumberFormatter
 17  * @example
 18  * <b>var</b> chart = <b>new</b> charts.TagChart('container_id');
 19  * chart.draw([['Country', 'Population'],
 20  *             ['Germany', 200],
 21  *             ['USA',     300],
 22  *             ['Brazil',  400],
 23  *             ['Canada',  500],
 24  *             ['France',  600],
 25  *             ['Russia',  700]
 26  * ]);
 27  *
 28  * <div style="border: solid 1px #ccc; margin: 5px; padding: 5px; width: 560px">
 29  *   <div id="chart-container"
 30  *        style="width: 560px; height: 300px;"></div>
 31  * </div>
 32  * <script src="http://datamart.github.io/Greylock/greylock.js"></script>
 33  * <script>
 34  *   var chart = new charts.TagChart('chart-container');
 35  *   chart.draw([['Country', 'Population'],
 36  *               ['Germany', 200],
 37  *               ['USA',     300],
 38  *               ['Brazil',  400],
 39  *               ['Canada',  500],
 40  *               ['France',  600],
 41  *               ['Russia',  700]
 42  *   ]);
 43  * </script>
 44  */
 45 charts.TagChart = function(container) {
 46   charts.BaseChart.apply(this, arguments);
 47 
 48   /**
 49    * Draws the chart based on <code>data</code> and <code>opt_options</code>.
 50    * @param {!Array.<Array>} data A chart data.
 51    * @param {Object=} opt_options A optional chart's configuration options.
 52    * @see charts.BaseChart#getOptions
 53    * @override
 54    * @example
 55    * options: {
 56    *   'font': {'family': 'Arial'}
 57    * }
 58    */
 59   this.draw = function(data, opt_options) {
 60     data_ = data;
 61     options_ = getOptions_(opt_options);
 62     self_.tooltip.setOptions(options_);
 63     shuffle_(data_);
 64 
 65     draw_();
 66   };
 67 
 68   /**
 69    * Draws content into <code>this.container</code> as <code>innerHTML</code>.
 70    * @param {string} content SVG or VML markup content.
 71    * @param {number=} opt_width Optional chart width.
 72    * @param {number=} opt_height Optional chart height.
 73    */
 74   this.drawContent = function(content, opt_width, opt_height) {
 75     opt_width = opt_width || self_.container.offsetWidth || 200;
 76     opt_height = opt_height || self_.container.offsetHeight || opt_width;
 77 
 78     self_.container.style.position = 'relative';
 79     self_.container.style.overflow = 'hidden';
 80 
 81     self_.container.innerHTML = content;
 82   };
 83 
 84   // Export for closure compiler.
 85   this['draw'] = this.draw;
 86 
 87   // Export for closure compiler.
 88   this['drawContent'] = this.drawContent;
 89 
 90   /**
 91    * Gets chart's options merged with defaults chart's options.
 92    * @param {Object=} opt_options A optional chart's configuration options.
 93    * @return {!Object.<string, *>} A map of name/value pairs.
 94    * @see charts.BaseChart#getOptions
 95    * @private
 96    * @example
 97    * options: {
 98    *   'font': {'family': 'Arial'}
 99    * }
100    */
101   function getOptions_(opt_options) {
102     opt_options = opt_options || {};
103     opt_options['font'] = opt_options['font'] || {};
104     opt_options['font']['family'] = opt_options['font']['family'] || '';
105     return self_.getOptions(opt_options);
106   }
107 
108   /**
109    * @private
110    */
111   function draw_() {
112     /** @type {!Array.<number>} */ var range = self_.getDataRange(data_, 1);
113     /** @type {number} */ var width = self_.container.offsetWidth || 200;
114     /** @type {number} */ var height = self_.container.offsetHeight || width;
115     /** @type {string} */ var content = '<div style="text-align: center;' +
116                                         ' line-height: 1.6em; padding: 10px;">';
117     /** @type {number} */ var minValue = range[0];
118     /** @type {number} */ var maxValue = range[1];
119 
120     for (/** @type {number} */ var i = 1; i < data_.length; i++) {
121       /** @type {string} */ var title = data_[i][0];
122       /** @type {number} */ var value = data_[i][1];
123       /** @type {string} */ var color = options_['colors'][i - 1];
124       /** @type {string} */ var tooltip = '<b>' + title + '</b><br>' +
125                                           data_[0][1] + ': ' + value;
126       /** @type {number} */ var fontSize = Math.round(
127           (value - minValue) * 150 / maxValue) + 100;
128 
129       content += '<span ' +
130                  'tooltip="' + tooltip + '"' +
131                  'style="' +
132                  'font-size:' + fontSize + '%;' +
133                  'padding: 0 3px;' +
134                  'font-family:' + options_['font']['family'] + ';' +
135                  'color: ' + color +
136                  '">' + title + ' </span>';
137     }
138 
139     content += '</div>';
140 
141     self_.drawContent(content, width, height);
142     setTimeout(initEvents_, 100);
143   }
144 
145 
146   function shuffle_(array) {
147     /** @type {!Array} */ var head = array.shift();
148     /** @type {number} */ var i = array.length;
149     while (i) {
150       /** @type {number} */ var randIndex = Math.floor(Math.random() * i--);
151       /** @type {number} */ var tmp = array[i];
152       array[i] = array[randIndex];
153       array[randIndex] = tmp;
154     }
155     array.unshift(head);
156     return array;
157   }
158 
159   /**
160    * Initializes events handlers.
161    * @private
162    */
163   function initEvents_() {
164     /** @type {NodeList} */
165     var nodes = dom.getElementsByTagName(self_.container, 'SPAN');
166     for (/** @type {number} */ var i = 0; i < nodes.length; i++) {
167       setEvents_(nodes[i]);
168     }
169   }
170 
171   /**
172    * Sets events handlers.
173    * @param {!Element} node The element.
174    * @private
175    */
176   function setEvents_(node) {
177     /** @type {string} */ var attr = 'opacity';
178     /** @type {!Object.<string, function(Event,...)>} */ var events = {};
179 
180     events[dom.events.TYPE.MOUSEMOVE] = function(e) {
181       self_.tooltip.show(e);
182     };
183 
184     events[dom.events.TYPE.MOUSEOVER] = function(e) {
185       node.style[attr] = 1;
186       self_.tooltip.show(e);
187     };
188 
189     events[dom.events.TYPE.MOUSEOUT] = function(e) {
190       node.style[attr] = options_['opacity'];
191       self_.tooltip.hide(e);
192     };
193 
194     for (/** @type {string} */ var key in events) {
195       dom.events.addEventListener(node, key, events[key]);
196     }
197 
198     events[dom.events.TYPE.MOUSEOUT](null);
199   }
200 
201   /**
202    * The reference to current class instance. Used in private methods.
203    * @type {!charts.TagChart}
204    * @private
205    */
206   var self_ = this;
207 
208   /**
209    * @type {Array.<Array>}
210    * @private
211    */
212   var data_ = null;
213 
214   /**
215    * @dict
216    * @private
217    */
218   var options_ = null;
219 
220 };
221 
222 // Export for closure compiler.
223 charts['PieChart'] = charts.PieChart;
224