1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | 2× 2× 2× 2× 1× 1× 1× 2× 2× 2× 2× 2× 2× 2× 2× 2× 2× 2× 2× 4× 2× 2× 2× 2× 1× | import Chartist from 'chartist' // rewite chartist's base class. // pass Chartist Object to plugin as a second argument // by hcl1687 2016-07-19 19:21 // TODO: Currently we need to re-draw the chart on window resize. This is usually very bad and will affect performance. // This is done because we can't work with relative coordinates when drawing the chart because SVG Path does not // work with relative positions yet. We need to check if we can do a viewBox hack to switch to percentage. // See http://mozilla.6506.n7.nabble.com/Specyfing-paths-with-percentages-unit-td247474.html // Update: can be done using the above method tested here: http://codepen.io/gionkunz/pen/KDvLj // The problem is with the label offsets that can't be converted into percentage and affecting the chart container /** * Updates the chart which currently does a full reconstruction of the SVG DOM * * @param {Object} [data] Optional data you'd like to set for the chart before it will update. If not specified the update method will use the data that is already configured with the chart. * @param {Object} [options] Optional options you'd like to add to the previous options for the chart before it will update. If not specified the update method will use the options that have been already configured with the chart. * @param {Boolean} [override] If set to true, the passed options will be used to extend the options that have been configured already. Otherwise the chart default options will be used as the base * @memberof Chartist.Base */ function update (data, options, override) { if (data) { this.data = data // Event for data transformation that allows to manipulate the data before it gets rendered in the charts this.eventEmitter.emit('data', { type: 'update', data: this.data }) } if (options) { this.options = Chartist.extend({}, override ? this.options : this.defaultOptions, options) // If chartist was not initialized yet, we just set the options and leave the rest to the initialization // Otherwise we re-create the optionsProvider at this point if (!this.initializeTimeoutId) { this.optionsProvider.removeMediaQueryListeners() this.optionsProvider = Chartist.optionsProvider(this.options, this.responsiveOptions, this.eventEmitter) } } // Only re-created the chart if it has been initialized yet if (!this.initializeTimeoutId) { this.createChart(this.optionsProvider.getCurrentOptions()) } // Return a reference to the chart object to chain up calls return this } /** * This method can be called on the API object of each chart and will un-register all event listeners that were added to other components. This currently includes a window.resize listener as well as media query listeners if any responsive options have been provided. Use this function if you need to destroy and recreate Chartist charts dynamically. * * @memberof Chartist.Base */ function detach () { // Only detach if initialization already occurred on this chart. If this chart still hasn't initialized (therefore // the initializationTimeoutId is still a valid timeout reference, we will clear the timeout if (!this.initializeTimeoutId) { window.removeEventListener('resize', this.resizeListener) this.optionsProvider.removeMediaQueryListeners() } else { window.clearTimeout(this.initializeTimeoutId) } return this } /** * Use this function to register event handlers. The handler callbacks are synchronous and will run in the main thread rather than the event loop. * * @memberof Chartist.Base * @param {String} event Name of the event. Check the examples for supported events. * @param {Function} handler The handler function that will be called when an event with the given name was emitted. This function will receive a data argument which contains event data. See the example for more details. */ function on (event, handler) { this.eventEmitter.addEventHandler(event, handler) return this } /** * Use this function to un-register event handlers. If the handler function parameter is omitted all handlers for the given event will be un-registered. * * @memberof Chartist.Base * @param {String} event Name of the event for which a handler should be removed * @param {Function} [handler] The handler function that that was previously used to register a new event handler. This handler will be removed from the event handler list. If this parameter is omitted then all event handlers for the given event are removed from the list. */ function off (event, handler) { this.eventEmitter.removeEventHandler(event, handler) return this } function initialize () { // Add window resize listener that re-creates the chart window.addEventListener('resize', this.resizeListener) // Obtain current options based on matching media queries (if responsive options are given) // This will also register a listener that is re-creating the chart based on media changes this.optionsProvider = Chartist.optionsProvider(this.options, this.responsiveOptions, this.eventEmitter) // Register options change listener that will trigger a chart update this.eventEmitter.addEventHandler('optionsChanged', function () { this.update() }.bind(this)) // Before the first chart creation we need to register us with all plugins that are configured // Initialize all relevant plugins with our chart object and the plugin options specified in the config if (this.options.plugins) { this.options.plugins.forEach(function (plugin) { if (plugin instanceof Array) I{ plugin[0](this, plugin[1], Chartist) } else { plugin(this, Chartist) } }.bind(this)) } // Event for data transformation that allows to manipulate the data before it gets rendered in the charts this.eventEmitter.emit('data', { type: 'initial', data: this.data }) // Create the first chart this.createChart(this.optionsProvider.getCurrentOptions()) // As chart is initialized from the event loop now we can reset our timeout reference // This is important if the chart gets initialized on the same element twice this.initializeTimeoutId = undefined } /** * Constructor of chart base class. * * @param query * @param data * @param defaultOptions * @param options * @param responsiveOptions * @constructor */ function Base (query, data, defaultOptions, options, responsiveOptions) { this.container = Chartist.querySelector(query) this.data = data this.defaultOptions = defaultOptions this.options = options this.responsiveOptions = responsiveOptions this.eventEmitter = Chartist.EventEmitter() this.supportsForeignObject = Chartist.Svg.isSupported('Extensibility') this.supportsAnimations = Chartist.Svg.isSupported('AnimationEventsAttribute') this.resizeListener = function resizeListener () { this.update() }.bind(this) if (this.container) E{ // If chartist was already initialized in this container we are detaching all event listeners first if (this.container.__chartist__) I{ this.container.__chartist__.detach() } this.container.__chartist__ = this } // Using event loop for first draw to make it possible to register event listeners in the same call stack where // the chart was created. this.initializeTimeoutId = setTimeout(initialize.bind(this), 0) } // Creating the chart base class export default Chartist.Class.extend({ constructor: Base, optionsProvider: undefined, container: undefined, svg: undefined, eventEmitter: undefined, createChart: function () { throw new Error('Base chart type can\'t be instantiated!') }, update: update, detach: detach, on: on, off: off, version: Chartist.version, supportsForeignObject: false }) |