/* FusionCharts JavaScript Library Copyright FusionCharts Technologies LLP License Information at */ (function (env, factory) { if (typeof module === 'object' && module.exports) { module.exports = env.document ? factory(env) : function(win) { if (!win.document) { throw new Error("Window with document not present"); } return factory(win, true); }; } else { env.FusionCharts = factory(env, true); } }(typeof window !== 'undefined' ? window : this, function (_window, windowExists) { /** * FusionCharts Core Framework * This module contains the basic routines required by subsequent modules to * extend/scale or add functionality to the FusionCharts object. * @private * * @module fusioncharts.constructor */ /* global FusionCharts: true, _window: true, window: false */ if (typeof _window === 'undefined' && typeof window === 'object') { _window = window; } /* exported FusionCharts */ var FusionCharts = (function (_window) { // In case FusionCharts object already exists, we skip this function. if (_window.FusionCharts && _window.FusionCharts.version) { return _window.FusionCharts; } var win = _window, doc = win.document, nav = win.navigator, // The global variable would store all private methods and properties // available to each module. global = { window: win }, /** * ~var {object} modules For maintaining module information. */ modules = global.modules = {}, interpreters = global.interpreters = {}, objectToStringFn = Object.prototype.toString, isIE = /msie/i.test(nav.userAgent) && !win.opera, hasLoaded = /loaded|complete/, mapLegacyDiscovered = false, // flag to keep track of legacy map script notifyLibraryInit = function () {// function to notify init of library var wasReady = global.ready; global.ready = true; if (global.raiseEvent) { global.readyNotified = true; /** * This event is fired when the FusionCharts library is ready to be used. By the time this event is * raised the browser's `DOM` is ready to be interacted with, which corresponds to the * `DOMContentLoaded` event of browsers. In older browsers, where `DOMContentLoaded` is not fired, the * `ready` event corresponds to the `load` event of the page. In case FusionCharts library is included * in the page when the `DOMContentLoaded` event is already fired (i.e. script is loaded asyncronously * using AJAX or by using script deferring methods,) the `ready` event is still fired to ensure * integrity of all the listeners. * * In many ways the nature of this event is similar to `jQuery(document).ready` of jQuery library and * `Ext.onReady` function of ExtJS library. One should interact with the FusionCharts framework (i.e. * create new charts, set options, etc) only after this event has been fired. This event also helps you * to neatly write your codes in separate script files and in page `` thus keeping scripts from * being part of your page ``. * * An alternate (and shorthand) to subscribing the `ready` event is to use the * {@link FusionCharts.ready} function. One advantage that {@link FusionCharts.ready} function has over * this `ready` event is that the `ready` event is fired only once during the life-cycle of a page while * functions passed to the {@link FusionCharts.ready} function is executed even when attached after the * `ready` event has been fired. * * > This is a framework level event and as such can be only listened via * > {@link FusionCharts.addEventLsitener} on the `FusionCharts` class alone. It will not be fired if * > subscribed from individual chart instances. * * @event FusionCharts.ready * @group framework * @since 3.4.0 * * @param {array} version - The FusionCharts framework version is returned in form of an array. This is * equivalent to the array {@link FusionCharts.version} * @param {boolean} now - This indicates whether this event was fired at the instant of * `window.ondomcontentloaded` event (or `window.onload` of older browsers) or whether the window was * already loaded and this event is fired just to maintain integrity. * * @example * * * * * *
Chart loads here...
* * */ global.raiseEvent('ready', { version: global.core.version, now: !wasReady }, global.core); } global.readyNow = !wasReady; }, FUSIONCHARTS, // recursive function that copies one object into another. merge = function (obj1, obj2) { var item, str; //check whether obj2 is an array //if array then iterate through it's index //**** MOOTOOLS precution if (obj2 instanceof Array) { for (item = 0; item < obj2.length; item += 1) { if (typeof obj2[item] !== 'object') { obj1[item] = obj2[item]; } else { if (typeof obj1[item] !== 'object') { obj1[item] = obj2[item] instanceof Array ? [] : {}; } merge(obj1[item], obj2[item]); } } } else { for (item in obj2) { if (typeof obj2[item] === 'object') { str = objectToStringFn.call(obj2[item]); if (str === '[object Object]') { if (typeof obj1[item] !== 'object') { obj1[item] = {}; } merge(obj1[item], obj2[item]); } else if (str === '[object Array]') { if (!(obj1[item] instanceof Array)) { obj1[item] = []; } merge(obj1[item], obj2[item]); } else { obj1[item] = obj2[item]; } } else { obj1[item] = obj2[item]; } } } return obj1; }; /** * This method, when added to the prototype of an object, * allows shallow or deep extension of the object with another * object. */ global.extend = function (sink, source, proto, deep) { var item; // When 'proto' is marked as true, the methods and properties // of source is not added to the prototype of the sink. if (proto && sink.prototype) { sink = sink.prototype; } // If deep extend is specified, then we use the deep copy function // 'merge' if (deep === true) { merge(sink, source); } // Copy all methods and properties of the object passed in parameter // to the object to which this function is attached. else { for (item in source) { sink[item] = source[item]; } } return sink; }; // Function that auto-generates a unique id. global.uniqueId = function () { return 'chartobject-' + (global.uniqueId.lastId += 1); }; global.uniqueId.lastId = 0; // Define the policy to create default parameters for the swfObject. // Values are in format [sourceOption, defaultValue] // This helps in building the initial FusionCharts object when new instances // are created from user parameters. global.policies = { /** * Contains all the customizable options that are used by the library internally and has nothing to do with * renderer attributes, vars or parameters. * @memberOf FusionCharts * @type {object} * @private * @enum */ options: { chartTypeSourcePath: ['typeSourcePath', ''], /** @ignore **/ product: ['product', 'v3'], /** * Default insert mode of adding a FusionCharts to a container */ insertMode: ['insertMode', 'replace'], safeMode: ['safeMode', true], /** * Default parameters for overlay button */ overlayButton: ['overlayButton', undefined], containerBackgroundColor: ['containerBackgroundColor', '#ffffff'], containerBackgroundOpacity: ['containerBackgroundOpacity', 1], containerClassName: ['containerClassName', 'fusioncharts-container'], /** * In case you want to set a default chartType for all new instances of FusionCharts */ chartType: ['type', undefined], /** * Default styling for chart messages */ baseChartMessageFont: ['baseChartMessageFont', 'Verdana,sans'], baseChartMessageFontSize: ['baseChartMessageFontSize', '10'], baseChartMessageColor: ['baseChartMessageColor', '#666666'], /** * Default position for chart messages image */ baseChartMessageImageHAlign: ['baseChartMessageImageHAlign', 'middle'], baseChartMessageImageVAlign: ['baseChartMessageImageVAlign', 'middle'], baseChartMessageImageAlpha: ['baseChartMessageImageAlpha', 100], baseChartMessageImageScale: ['baseChartMessageImageScale', 100], /** * Default message for chart (data related) */ dataLoadStartMessage: ['dataLoadStartMessage', 'Retrieving data. Please wait.'], dataLoadErrorMessage: ['dataLoadErrorMessage', 'Error in loading data.'], dataInvalidMessage: ['dataInvalidMessage', 'Invalid data.'], dataEmptyMessage: ['dataEmptyMessage', 'No data to display.'], /** * Default message for chart (chart related) */ typeNotSupportedMessage: ['typeNotSupportedMessage', 'Chart type not supported.'], browserNotSupportedMessage: ['browserNotSupportedMessage', 'This browser is not supported.'], loadMessage: ['loadMessage', 'Loading chart. Please wait.'], renderErrorMessage: ['renderErrorMessage', 'Unable to render chart.'] }, /** * ~var {object} attributes Contains configurations pertaining to the * host (browser) environment. */ attributes: { lang: ['lang', 'EN'], id: ['id', undefined] }, /** * ~var {array} width configuration for width of the chart. * ~var {array} height configuration for height of the chart. * ~var {array} src specifies chart swf url */ width: ['width', '400'], height: ['height', '300'], src: ['swfUrl', ''] }; // Specifies the order in which the parameters of the new // FusionCharts objects are interpreted and converted to options object. interpreters.stat = ['swfUrl', 'id', 'width', 'height', 'debugMode', 'registerWithJS', 'backgroundColor', 'scaleMode', 'lang', 'detectFlashVersion', 'autoInstallRedirect']; /** * Allows the core to process an arguments object based on a set of policies * and construct an object out of it that is mapped exactly as respective * parameter policy defines. In other words, it uses an object and * creates another object or updates another object with values from the * original arguments object in a particular hierarchy and name that a set * of rules (policies) define. */ global.parsePolicies = function (obj, policies, options) { var prop, policy, value; // Iterate through the data policy and correspondingly create the // three stacks of parameters, attributes and flashVars for (policy in policies) { // Set just the policy object in case of single-level policy. if (global.policies[policy] instanceof Array) { value = options[policies[policy][0]]; obj[policy] = value === undefined ? policies[policy][1] : value; } else { // Define objects that would hold parameters for swfobject. Also // populate with variables from the parameters if (typeof obj[policy] !== 'object') { obj[policy] = {}; } // Set every sub-object for two-level policy for (prop in policies[policy]) { value = options[policies[policy][prop][0]]; obj[policy][prop] = value === undefined ? policies[policy][prop][1] : value; } } } }; /** * Parse commands (command interpretor) based on a specified interpreter * structure */ global.parseCommands = function (obj, interpreter, args) { var i, l; if (typeof interpreter === 'string') { interpreter = interpreters[interpreter] || []; } // Iterate through the arguments template and add the keys to the // options object while fetching corresponding values from arguments // array. for (i = 0, l = interpreter.length; i < l; i++) { obj[interpreter[i]] = args[i]; } return obj; }; /** * Different types of extension registrations */ global.registrars = { 'module': function () { return global.core.apply(global.core, arguments); } }; /** * Create new instances of charts, gauges and maps using this function. * * The preferred way to draw charts, gauges and maps using FusionCharts is to pass the chart configurations and data * to this constructor and call `render()` on the returned instance. You can provide all the properties, data and * event bindings of charts through the parameters passed to this function. * * You can call methods of the {@link FusionCharts} class on the returned instance. For all practical purposes, this * is the first step to creating a chart, gauges and maps using FusionCharts. * * __Accessing existing charts using `FusionCharts()` constructor:__ * * The `FusionCharts` function has a dual behavior - other than creating new charts, it can also be used to access * already created charts. This is done by dropping the `new` operator and passing only the chart `id` as a * parameter. For example, `var salesChart = FusionCharts('sales-chart');` will return the instance of the chart * with the `id` "sales-chart". The previous code snippet is equivalent to * `var salesChart = FusionCharts.items['sales-chart'];`. Refer to {@link FusionCharts.items} to know more. * * @constructor * @global * * @param {object} options - While creating a new instance of FusionCharts, you can pass * an `options` object with all configuration parameters for that instance. All configurations passed through this * object are referred to as "construction parameters". Through these parameters, you can customize the look and * feel of a chart, pass data to the chart, configure its dimensions and bind to events. * * Each of this object's properties correspond to a configuration option. * * @param {!string=} [options.type] - Provide the name of the chart type to be rendered. Full list of charts is * available at {@tutorial setup-list-of-charts}. * * This parameter controls what chart will be rendered. The data passed to the chart has to be compatible with the * chart type specified here. * * Alternatively, you can also call {@link FusionCharts#chartType} on the chart instance to provide the chart type. * * @param {!string=} [options.id] - This name is used to refer to the current instance after the * chart has been created. The chart instance is available under this name in {@link FusionCharts.items}. If no `id` * is provided, FusionCharts automatically generates an `id` for each chart. * * @param {numeric=|percent=} [options.width="400"] - Set the width in pixels or percent such as `640` or * `'50%'`. If width is in pixels, there is no need to provide the `px` unit suffix. You can call * {@link FusionCharts#resizeTo} function on the chart instance to set the width later on. * * @param {numeric=|percent=} [options.height="300"] - Set the height in pixels or percent such as `640` or * `'50%'`. If height is in pixels, there is no need to provide the `px` unit suffix. You can call * {@link FusionCharts#resizeTo} function on the chart instance to set the height later on. * * @param {string=|DOMElement=} [options.renderAt] - A chart needs reference to a DOM element on the page where * it will be rendered. You can provide the HTML ID of the element as a string to this option, or you can pass a * reference of the DOMElement itself where the chart needs to be rendered. * * For example, if you have a DOMElement like `
`, you can provide * the value of the `div`'s `id` attribute to this option as a string. Alternatively, you can pass direct reference * to the DOMElement like: `renderAt: document.getElementByClassName("chart-1")`. * * Instead of providing the DOMElement here, it can also be passed as the first parameter to the * {@link FusionCharts.render} function. Setting the DOMElement in {@link FusionCharts.render} function overrides * the value set here. * * @param {FusionCharts~dataFormats=} [options.dataFormat] - This is the name of the format of data passed to the * `dataSource` option below. Currently, FusionCharts accepts only JSON and XML data. The value for this option is * one of the formats specified in {@link FusionCharts~dataFormats}. * * @param {string=|object=} [options.dataSource] - Provide the source of data and configuration of the chart. * FusionCharts accepts data in the formats specified in {@link FusionCharts~dataFormats}. * * This is the preferred way to set data and configuration of the chart. The data and configuration can also be * updated or set using the functions {@link FusionCharts#setChartData} and {@link FusionCharts#setChartDataUrl}. * * @param {object=} [options.events] - You can bind multiple events to this particular chart instance through * this option. You need to pass an object to this option, where each key is an event name fired by FusionCharts and * value for that key is a callback in the format of {@link FusionCharts~eventListener}. * * To bind multiple charts to the same event, you need to use {@link FusionCharts#addEventListener} function * instead. * * @param {object=} [options.link] - Provide LinkedCharts configuration. See {@link FusionCharts#configureLink} * for details. * * @param {boolean=} [options.showDataLoadingMessage=false] - FusionCharts shows a message while it is retrieving * data from a `url` provided as `dataSource`. While displaying the message the chart is grayed out and interaction * on it is blocked. This can be prevented by setting this option to `false`. * * @param {boolean=} [options.showChartLoadingMessage=true] - Shows `Loading chart...` message in `renderAt` * container if the chart needs to load additional resource/JS files. This can be turned off by setting this option * to `false`. * * @param {string=} [options.baseChartMessageFont='Verdana'] - Allows to set the common custom font face for all * chart messages. * * @param {string=} [options.baseChartMessageFontSize='10'] - Allows to set the common custom font size for all * chart messages. * * @param {hexcolor=} [options.baseChartMessageColor='#666666'] - Allows to set the common custom font color for * all chart messages. * * @param {string=} ['baseChartMessageImageHAlign'='middle'] - Allows to set the common custom horizontal * alignment for all image as chart messages. * * @param {string=} ['baseChartMessageImageVAlign'='middle'] - Allows to set the common custom vertical * alignment for all image as chart messages. * * @param {string=} ['baseChartMessageImageAlpha'='100'] - Allows to set the common custom alpha * for all image as chart messages. * * @param {string=} ['baseChartMessageImageScale'='100'] - Allows to set the common custom scaling * for all image as chart messages. * * @param {string=} [options.dataLoadStartMessage='Retrieving data. Please wait.'] - Allows to set the message to * be displayed before the chart data begins loading. Additional properties like the font face, size, and color can * be set by suffixing the property name with the corresponding message key, e.g. dataLoadStartMessageFont, * dataLoadStartMessageFontSize, dataLoadStartMessageColor. If message keys are not specified, base cosmetics are * used. * * @param {string=} [options.dataLoadErrorMessage='Error in loading data.'] - Allows to set the message to be * displayed when there is an error loading the chart data. Additional properties like the font face, size, and * color can be set by suffixing the property name with the corresponding message key, e.g. * dataLoadStartMessageFont, dataLoadStartMessageFontSize, dataLoadStartMessageColor. If message keys are not * specified, base cosmetics are used. * * @param {string=} [options.dataInvalidMessage='Invalid data.'] - Allows to set the message to be displayed when * the data loaded for the chart is invalid. Additional properties like the font face, size, and color can be * set by suffixing the property name with the corresponding message key, e.g. dataLoadStartMessageFont, * dataLoadStartMessageFontSize, dataLoadStartMessageColor. If message keys are not specified, base cosmetics are * used. * * @param {string=} [options.dataEmptyMessage='No data to display.'] - Allows to set the message to be displayed if * data loaded for the chart is empty. Additional properties like the font face, size, and color can be * set by suffixing the property name with the corresponding message key, e.g. dataLoadStartMessageFont, * dataLoadStartMessageFontSize, dataLoadStartMessageColor. If message keys are not specified, base cosmetics are * used. * * @param {string=} [options.typeNotSupportedMessage='Chart type not supported.'] - Allows to set the message to be * displayed if specified chart type is not supported. Additional properties like the font face, size, and color * can be set by suffixing the property name with the corresponding message key, e.g. dataLoadStartMessageFont, * dataLoadStartMessageFontSize, dataLoadStartMessageColor. If message keys are not specified, base cosmetics are * used. * * @param {string=} [options.loadMessage='Loading chart. Please wait.'] - Allows to set the message to be displayed * when the chart begins to load. Additional properties like the font face, size, and color can be set by * suffixing the property name with the corresponding message key, e.g. dataLoadStartMessageFont, * dataLoadStartMessageFontSize, dataLoadStartMessageColor. If message keys are not specified, base cosmetics are * used. * * @param {string=} [options.renderErrorMessage='Unable to render chart.'] - Allows to set the message to be * displayed if there was an error while rendering the chart. Additional properties like the font face, size, and * color can be set by suffixing the property name with the corresponding message key, e.g. * dataLoadStartMessageFont, dataLoadStartMessageFontSize, dataLoadStartMessageColor. If message keys are not * specified, base cosmetics are used. * * @param {hexcolor=} [options.containerBackgroundColor="#ffffff"] - Sets the background color of the chart's * container HTML DOM element. It is not same as `bgColor` chart attribute. To see this background color, a chart's * own background alpha must be set to `0` by setting `bgAlpha` attribute to `"0"` in chart attributes. * * @param {opacity=} [options.containerBackgroundOpacity=1] - Sets the opacity of the container element. Useful * if the chart has underlying HTML elements or background image that needs to be made visible. If opacity is * reduced, you need to configure the chart itself to be transparent by setting the `bgAlpha` chart attribute. * * @param {string=} [options.containerClassName] - Sets the CSS class that will be set on the container DOM element * of the rendered chart. Default is `fusioncharts-container`. * * @example * * * * * *
FusionCharts will load here...
* * */ FUSIONCHARTS = function (options) { // This point onwards, we must check whether this is being used as a // constructor or not if (!(this instanceof global.core)) { // Allow private communication with modules. In case FusionCharts is // not called as constructor and it is passed an array that is marked // to do private communication, then share the global variable. if (arguments.length === 1 && options instanceof Array && options[0] === 'private') { // Prevent overwriting and duplicate execution of modules. if (modules[options[1]]) { return undefined; } modules[options[1]] = {}; // Check for module-specific information if (options[3] instanceof Array) { global.core.version[options[1]] = options[3]; } // Execute module function if (typeof options[2] === 'function') { return options[2].call(global, modules[options[1]]); } else { return global; } } // Allow using FusionCharts object to directly access its new items if (arguments.length === 1 && typeof options === 'string') { return global.core.items[options]; } /** * This error occurs when the `FusionCharts` constructor is called without the `new` keyword and also * without passing a reference chart id as the parameter. In case you are rendering a new chart, ensure you * do a `new FusionCharts({});` or in case you are using the constructor as a getter, ensure you pass the * chart Id (string) as first parameter. * * @typedef {RuntimeException} Error-25081840 * @memberOf FusionCharts.debugger * @group debugger-error */ global.raiseError && global.raiseError(this, '25081840', 'run', '', new SyntaxError('Use the "new" keyword while creating a new FusionCharts object')); } // Define a variable for iterative key in various loops and the // object variable that stores the options. var opts = {}; /** * @var {object} __state maintains internal state related information. * @private */ this.__state = {}; // Check whether linear arguments are sent and convert it to object. if (arguments.length === 1 && typeof arguments[0] === 'object') { // If the above condition matches, then we can safely assume that // the first parameter is the options object. opts = arguments[0]; } else { // Parse command interpreter policies global.parseCommands(opts, interpreters.stat, arguments); } // Incorporate the trailing object parameter as object-style // parameter input overrides. if (arguments.length > 1 && typeof arguments[arguments.length - 1] === 'object') { delete opts[interpreters.stat[arguments.length - 1]]; global.extend(opts, arguments[arguments.length - 1]); } // Set autogenerated chart-id in case one is not specified this.id = (typeof opts.id === 'undefined') ? this.id = global.uniqueId() : opts.id; // Set dimension passed by user and subsequently validate the options. // - Remove trailing 'px' this.args = opts; // If an item is created with same id, the previous item is disposed. if (global.core.items[this.id] instanceof global.core) { /** * This error occurs when a new chart is created (instantiated) with the an `id` that has been already * assigned to an existing chart. Change the chart Id or dispose the other chart with the duplicate id. * * @typedef {ParameterException} Error-06091847 * @memberOf FusionCharts.debugger * @group debugger-error */ global.raiseWarning(this, '06091847', 'param', '', new Error('A FusionCharts object with the specified id \"' + this.id + '\" already exists. Renaming it to ' + (this.id = global.uniqueId()))); } // Parse global policies. global.parsePolicies(this, global.policies, opts); // Copy chart id to attributes this.attributes.id = this.id; // Set initial dimension of charts this.resizeTo && this.resizeTo(opts.width, opts.height, true); // Set initial chart type of charts this.chartType && this.chartType(opts.type || opts.swfUrl, true); /** * Whenever a new instance of {@link FusionCharts} is created (as in `new FusionCharts(...)`, this pre * initialization event is raised. This event triggers a number of modules that needs to be setup on every * instance of FusionCharts. One can listen to this event perform actions that, on similar grounds, requires * to be setup upn initialization of each chart. * * Since this event is fired upon instantiating a new FusionCharts object, it is virtually impossible to listen * to this event by adding event listener to that individual chart. That is because, by the time one's event * listener is attached using {@link FusionCharts#addEventListener} on the subsequent lines post doing `new * FusionCharts(...)`, this event would have been already fired. Thus, the alternate ways to listen to this * event are: * * 1. Listen to FusionCharts global events using {@link FusionCharts.addEventListener} before even creating a * new instance. (The required instance can be identified by the `id` of the chart using * `eventObject.sender.id`.) * * 2. Pass the event listener as the FusionCharts constructor parameter itself. * * @event FusionCharts.beforeInitialize * @group chart * * @example * // Listening using global events * FusionCharts.addEventListener('beforeInitialize', function (opts) { * // Prints id of the chart being rendered * console.log("Chart with id " + opts.sender.id + " is about to be initialized."); * }); * * // Pass event listener in the FusionCharts constructor * var mychart = new FusionCharts({ * "type": "column2d", * "dataFormat": "json", * "dataSource": { * ... * }, * // Attach event handlers * "events": { * // Attach to beforeInitialize * "beforeInitialize": function () { * console.log("Initializing mychart..."); * } * } * }); * * @param {numeric|percent} height - Height of the chart in pixels or percentage. * @param {numeric|percent} width - Width of the chart in pixels or percentage. */ global.raiseEvent('beforeInitialize', opts, this); // Add this object to the repository of objects within core object. global.core.items[this.id] = this; // Create alias for defaultOptions global.core.defaultOptions = global.core.options; /** * Once a new instance of {@link FusionCharts} is created and is ready to be operated upon, this `initialized` * event is fired. Note that initialization does not indicate that the chart has been rendered. It denotes that * the JavaScript object instance of FusionCharts is created (as in `new FusionCharts(...)` done) and is now * ready to be operated upon (like data being passed onto it, it being rendered, etc.) * * @event FusionCharts.initialized * @group chart * * @param {numeric|percent} height - height of the chart in pixels or percentage . * @param {numeric|percent} width - width of the chart in pixels or percentage . * * @example * // Listening using global events * FusionCharts.addEventListener('initialized', function (opts) { * // Prints id of the chart that has initialized * console.log("Chart with id " + opts.sender.id + " has been initialized."); * }); * * // Pass event listener in the FusionCharts constructor * var mychart = new FusionCharts({ * "type": "column2d", * "dataFormat": "json", * "dataSource": { * ... * }, * // Attach event handlers * "events": { * // Attach to beforeInitialize * "initialized": function () { * console.log("Initialized mychart..."); * } * } * }); */ global.raiseEvent('initialized', opts, this); return this; }; // Set FusionCharts as the primary core. global.core = FUSIONCHARTS; // Make the core extensible and reset the constructor of the object // for maintaining correct prototype chain. global.core.prototype = {}; // Reset constructor. global.core.prototype.constructor = global.core; global.extend(global.core, /** @lends FusionCharts */ { id: 'FusionCharts', /** * Specifies the framework version of {@link FusionCharts}. In the format * `[major, minor, revision, nature, build]` * @type {array} */ version: ['3', '12', '0'], /** * The reference to every new instance of FusionCharts is maintained in this object with the chart `ID` as the * key. Upon {@link FusionCharts#dispose} of the instance, the key is removed from this. One can iterate through * all instances of {@link FusionCharts} using this object. * * A short-hand approach to accessing a single chart by its `id` is to use {@link FusionCharts} function itself * but without the `new` operator and by passing the chart id as the first parameter. * @type {object} * * @group framework * * @example * // Assuming a page has many instances of {@link FusionCharts}, but * // none of them are rendered, we are going to iterate through all and * // render them. * for (var item in FusionCharts.items) { * FusionCharts.items[item].render(); * } * * @example * // Alternate method to access the charts using FusionCharts function to retrieve the chart from its id. * for (var item in FusionCharts.items) { * FusionCharts(item).render(); * } */ items: {}, // Add an object to store options options: {}, /** * The function returns the `DOMElement` that is created inside chart container by FusionCharts. The returned * element is the same as accessing the {@link FusionCharts#ref} property. Note that this is the `` * element created by FusionCharts to render the chart. It is not the container element that was specified * during rendering the chart as the `renderAt` parameter. * * @param {string} id - The ID of the chart, whose `DOMElement` is to be referenced. * * @group framework * @since 3.1.1 * @deprecated 3.2.0 - This method has been deprecated as direct access to `DOMElement` of the chart has become * redundant. {@link FusionCharts#ref} property can be used in the rare case where such access to the * `DOMElement` of a chart is required. * * @returns {DOMElement} * * @example * // Iterate on all charts rendered on a page and move them to a common location * var sidebar = document.getElementById('sidebar-html-div'), // assuming that your common container is this * chart; * * for (chart in FusionCharts.items) { * sidebar.appendChild(FusionCharts.getObjectReference(chart).parentNode); * } * * // The above can be done without using this deprecated getObjectReference method. * for (chart in FusionCharts.items) { * chart = FusionCharts.items[chart]; * chart.ref && sidebar.appendChild(chart.ref.parentNode); * } */ getObjectReference: function (id) { return global.core.items[id].ref; }, /** * Extend FusionCharts functionalities by adding modules and other items. * @private * * @param {string} what - The type of extension that can be done. The possible values can be `"module"`, * `"theme"`, etc. * @param {...*} args - Depending upon what you want to register, supply the relevant registration items. */ register: function (what) { return global.registrars[(what = (what && what.toString && what.toString().toLowerCase()))] && global.registrars[what].apply(global.core, Array.prototype.slice.call(arguments, 1)); }, /** * Get the definations of previously registered components * @private * * @param {string} what - The type of extension that can be done. The possible values can be `"component"`, * `"theme"`, etc. * @param {...*} args - Depending upon what you want to retirvr, supply the relevant names of sub-items. * @todo change the defination of get so that user can't do registration using this */ get: function (what) { return global.registrars[(what = (what && what.toString && what.toString().toLowerCase()))] && global.registrars[what].apply(global.core, Array.prototype.slice.call(arguments, 1)); } }); // Expose the core to the global scope. win.FusionCharts = global.core; // Check whether legacy FusionMaps already exists at this point in execution // time. If yes, then we need to perform routines to assimilate it. if (win.FusionMaps && win.FusionMaps.legacy) { global.core(['private', 'modules.core.geo', win.FusionMaps.legacy, win.FusionMaps.version]); mapLegacyDiscovered = true; } // If FusionMaps legacy was not discovered, we give it another shot after // the page has loaded. if (!(hasLoaded.test(doc.readyState) || doc.loaded)) { (function () { var _timer, script, done; function init() { /* jshint noarg: false */ // quit if this function has already been called if (done){return;} // flag this function so we don't do the same thing twice done = true; // kill the timer if (_timer){clearTimeout(_timer);} if (!mapLegacyDiscovered) { if (win.FusionMaps && win.FusionMaps.legacy) { global.core(['private', 'modules.core.geo', win.FusionMaps.legacy, win.FusionMaps.version]); } win.FusionMaps = global.core; } // Notify that library is ready for consumption. setTimeout(notifyLibraryInit, 1); } function checkInit () { if (hasLoaded.test(doc.readyState)) { init(); // call the onload handler } else { _timer = setTimeout(checkInit, 10); } } if (doc.addEventListener) { doc.addEventListener('DOMContentLoaded', init, false); } else if (doc.attachEvent) { win.attachEvent('onLoad', init); } if (isIE) { try { if (win.location.protocol === 'https:') { doc.write(' * * * * * * * *
*
* * */ outputTo: function (debuggerCallback) { // Check whether the logger is a function or not. If it is a // function, we set a reference to it to be used later as the // logger function. if (typeof debuggerCallback === 'function') { /** * The parameters passed on to the debugger callback function is in line with the value of * {@link FusionCharts.debugger~outputFormats} specified via {@link FusionCharts.debugger.outputTo}. * @callback FusionCharts.debugger~debuggerCallback * * @param {...*} outputFormatParameters - The parameters passed to the callback depend upon the * debugger output format set. The details regarding the different variants of parameter is at * {@link FusionCharts.debugger~outFormats}. */ logger.outputTo = debuggerCallback; } // In case user sends 'null' as the value of the logger function, // we can assume that user wants not to log any output. else if (debuggerCallback === null) { global.core[DEBUGGER].enable(false); delete logger.outputTo; } }, /** * The FusionCharts debugger is not enabled by default. This method allows us to enable the debugger and * also optionally provide basic debugger configuration. * * The debugger works in conjunction with the browser's JavaScript console or any other special console-like * implementation that you may have. To enable the debugger, call this function and pass a callback that * outputs the message to the JavaScript console. The code would log the activities of every chart and the * entire framework. * * ``` * FusionCharts['debugger'].enable(true, function (message) { * console.log(message); * }); * ``` * * If you have added this code right after including the `fusioncharts.js` script in a page that renders a * single chart with id "myChart", your output would look somewhat like: * * ``` * #1 [FusionCharts] fired "ready" event. * #2 [myChart] fired "beforeinitialize" event. * #3 [myChart] fired "beforedataupdate" event. * #4 [myChart] fired "dataupdated" event. * #5 [myChart] fired "initialized" event. * #6 [myChart] fired "beforerender" event. * #7 [myChart] fired "internal.loaded" event. * #8 [myChart] fired "internal.drawstart" event. * #9 [myChart] fired "dataloaded" event. * #10 [myChart] fired "internal.domelementcreated" event. * #11 [myChart] fired "loaded" event. * #12 [myChart] fired "drawcomplete" event. * #13 [myChart] fired "rendercomplete" event. * ``` * * The output clearly shows that FusionCharts declared itself as `ready` and then the chart followed the * routine of initialising itself, loading data, loading dependencies and then completing the rendering * process. Had there been any error, it would have reflected in the output. * * > The debugger is not intended to be kept enabled on a production server since it has performance and * > memory requirement overhead. It is meant for pre-production debugging only. * * @param {boolean} state - Specifies whether to enable logging of debug information. * * @param {FusionCharts.debugger~debuggerCallback=} [outputTo] - The function to which the debugger output * will be passed on. * * @param {FusionCharts.debugger~outputFormats=} [outputFormat="text"] - Can be one of the accepted format * names such as "text", "verbose", "event". * * @returns {boolean} The current 'enable' state of the debugger. */ enable: function (state, outputTo, outputFormat) { // Allow object to be sent as configuration parameter. var config; // In case the first parameter is object and the only parameter, // we copy its contents to various linear parameters and save // a copy of the object for later use. if (typeof state === 'object' && arguments.length === 1) { config = state; state = config.state; outputTo = config.outputTo; outputFormat = config.outputFormat; } // In case user send in only one parameter and that too a // function, we can assume that he wants to use it as a logger // function and also enable logging. if (typeof state === 'function') { if (typeof outputTo === 'string' && (arguments.length === 2 || config)) { outputFormat = outputTo; } outputTo = state; state = true; } // In case user sends in a valid parameter to change the current // state of the debugger, we update the debugger state. if (typeof state === 'boolean' && state !== logger.enabled) { global.core[(logger.enabled = state) ? 'addEventListener' : 'removeEventListener']('*', logger.outputHandler); } // If user sends in a parameter for the logger parameter, we // set it to the logger function reference. if (typeof outputTo === 'function') { logger.outputTo = outputTo; } // Set output format if needed. global.core[DEBUGGER].outputFormat(outputFormat); // Finally send the current debugger state to the user. return logger.enabled; }, /** * *(experimental)* This method fetches FirebugLite component's code and adds it to current page. * Subsequently, on load of the script it enables advanced console logging to it. This is very useful for * debugging on older Internet Explorer browsers and on mobile device browsers that do not have a JavaScript * debugging console. * * Visit http://getfirebug.com/firebuglite for more information on this very popular component. This * function fetches the script from their latest stable release channel as mentioned in * http://getfirebug.com/firebuglite#Stable */ enableFirebugLite: function () { var htmlTags; // Check whether firebug already exists. /*jslint devel:true */ if (win.console && win.console.firebug) { // If firebug already exists, we do not need to include any // script for firebu-lite and we simply enable logging to //console. global.core[DEBUGGER].enable(win.console.log, 'verbose'); return; } /*jslint devel:false */ // Install firebug-lite within page by creating new 'script' element and appending to page head. htmlTags = win.document.getElementsByTagName('html'); htmlTags && htmlTags[0].setAttribute('debug', 'true'); global.loadScript('https://getfirebug.com/firebug-lite.js#overrideConsole=false,startOpened=true', function () { global.core[DEBUGGER].enable(win.console.log, 'verbose'); }, '{ startOpened: true }', true, true); } }, /** * @deprecated 3.4.0 - Please use FusionCharts.debugger.enable instead * @private */ debugMode: { enabled: function () { win.setTimeout(function () { throw new Error('Deprecated! Please use FusionCharts.debugger.enable instead.'); }, 0); return global.core[DEBUGGER].enable.apply(global.core[DEBUGGER], arguments); } } }, false); }]); /** * This module allows FusionCharts to work with W3C Level 2 style events for * allowing multiple handlers per event and also to do event driven development * on a global or per-chart basis. * @private * * @module fusioncharts.events * @requires fusioncharts.constructor * @requires fusioncharts.debugger */ FusionCharts.register('module', ['private', 'modules.mantle.eventmanager', function () { var global = this, win = global.window, core = global.core, objectProtoToString = Object.prototype.toString, arrayToStringIdentifier = objectProtoToString.call([]), isArray = function (obj) { return objectProtoToString.call(obj) === arrayToStringIdentifier; }, // A function to create an abstraction layer so that the try-catch / // error suppression of flash can be avoided while raising events. managedFnCall = function (item, scope, event, args) { // We change the scope of the function with respect to the // object that raised the event. try { item[0].call(scope, event, args || {}); } catch (e) { // Call error in a separate thread to avoid stopping // of chart load. setTimeout(function () { throw e; }, 0); } }, // Function that executes all functions that are to be invoked upon trigger // of an event. slotLoader = function (slot, event, args) { // If slot does not have a queue, we assume that the listener // was never added and halt method. if (!(slot instanceof Array)) { // Statutory W3C NOT preventDefault flag return; } // Initialize variables. var i = 0, scope; // Iterate through the slot and look for match with respect to // type and binding. for (; i < slot.length; i += 1) { // If there is a match found w.r.t. type and bind, we fire it. if (slot[i][1] === event.sender || slot[i][1] === undefined) { // Determine the sender of the event for global events. // The choice of scope differes depending on whether a // global or a local event is being raised. scope = slot[i][1] === event.sender ? event.sender : global.core; managedFnCall(slot[i], scope, event, args); // Check if the user wanted to detach the event if (event.detached === true) { slot.splice(i, 1); i -= 1; event.detached = false; } } // Check whether propagation flag is set to false and discontnue // iteration if needed. if (event.cancelled === true) { break; } } }, EventTarget = { unpropagator: function () { return (this.cancelled = true) === false; }, detacher: function () { return (this.detached = true) === false; }, undefaulter: function () { return (this.prevented = true) === false; }, // Entire collection of listeners. listeners: {}, // The last raised event id. Allows to calculate the next event id. lastEventId: 0, addListener: function (type, listener, bind) { var recurseReturn, i; // In case type is sent as array, we recurse this function. if (isArray(type)) { recurseReturn = []; // We look into each item of the 'type' parameter and send it, // along with other parameters to a recursed addListener // method. for (i = 0; i < type.length; i += 1) { recurseReturn.push(EventTarget.addListener(type[i], listener, bind)); } return recurseReturn; } // Validate the type parameter. Listener cannot be added without // valid type. if (typeof type !== 'string') { /** * The event name has not been provided while adding an event listener. Ensure that you pass a * `string` to the first parameter of {@link FusionCharts.addEventListener}. * * @typedef {ParameterException} Error-03091549 * @memberOf FusionCharts.debugger * @group debugger-error */ global.raiseError(bind || global.core, '03091549', 'param', '::EventTarget.addListener', new Error('Unspecified Event Type')); return; } // Listener must be a function. It will not eval a string. if (typeof listener !== 'function') { /** * The event listener passed to {@link FusionCharts.addEventListener} needs to be a function. * * @typedef {ParameterException} Error-03091550 * @memberOf FusionCharts.debugger * @group debugger-error */ global.raiseError(bind || global.core, '03091550', 'param', '::EventTarget.addListener', new Error('Invalid Event Listener')); return; } // Desensitize the type case for user accessability. type = type.toLowerCase(); // If the insertion position does not have a queue, then create one. if (!(EventTarget.listeners[type] instanceof Array)) { EventTarget.listeners[type] = []; } // Add the listener to the queue. EventTarget.listeners[type].push([listener, bind]); return listener; }, removeListener: function (type, listener, bind) { var slot, i; // Listener must be a function. Else we have nothing to remove! if (typeof listener !== 'function') { /** * The event listener passed to {@link FusionCharts.removeEventListener} needs to be a function. * Otherwise, the event listener function has no way to know which function is to be removed. * * @typedef {ParameterException} Error-03091560 * @memberOf FusionCharts.debugger * @group debugger-error */ global.raiseError(bind || global.core, '03091560', 'param', '::EventTarget.removeListener', new Error('Invalid Event Listener')); return; } // In case type is sent as array, we recurse this function. if (type instanceof Array) { // We look into each item of the 'type' parameter and send it, // along with other parameters to a recursed addListener // method. for (i = 0; i < type.length; i += 1) { EventTarget.removeListener(type[i], listener, bind); } return; } // Validate the type parameter. Listener cannot be removed without // valid type. if (typeof type !== 'string') { /** * The event name passed to {@link FusionCharts.removeEventListener} needs to be a string. * * @typedef {ParameterException} Error-03091559 * @memberOf FusionCharts.debugger * @group debugger-error */ global.raiseError(bind || global.core, '03091559', 'param', '::EventTarget.removeListener', new Error('Unspecified Event Type')); return; } // Desensitize the type case for user accessability. type = type.toLowerCase(); // Create a reference to the slot for easy lookup in this method. slot = EventTarget.listeners[type]; // If slot does not have a queue, we assume that the listener // was never added and halt method. if (!(slot instanceof Array)) { return; } // Iterate through the slot and remove every instance of the // event handler. for (i = 0; i < slot.length; i += 1) { // Remove all instances of the listener found in the queue. if (slot[i][0] === listener && slot[i][1] === bind) { slot.splice(i, 1); i -= 1; } } }, // opts can have { async:true, omni:true } triggerEvent: function (type, sender, args, eventScope, defaultFn, cancelFn) { // In case, event type is missing, dispatch cannot proceed. if (typeof type !== 'string') { /** * The event name passed to {@link FusionCharts.removeEventListener} needs to be a string. * @private * * @typedef {ParameterException} Error-03091602 * @memberOf FusionCharts.debugger * @group debugger-error */ global.raiseError(sender, '03091602', 'param', '::EventTarget.dispatchEvent', new Error('Invalid Event Type')); return undefined; } // Desensitize the type case for user accessability. type = type.toLowerCase(); // Model the event as per W3C standards. Add the function to cancel // event propagation by user handlers. Also append an incremental // event id. var eventObject = { eventType: type, eventId: (EventTarget.lastEventId += 1), sender: sender || new Error('Orphan Event'), cancelled: false, stopPropagation: this.unpropagator, prevented: false, preventDefault: this.undefaulter, detached: false, detachHandler: this.detacher }; /** * Event listeners are used to tap into different stages of creating, updating, rendering or removing * charts. A FusionCharts instance fires specific events based on what stage it is in. For example, the * `renderComplete` event is fired each time a chart has finished rendering. You can listen to any such * event using {@link FusionCharts.addEventListener} or {@link FusionCharts#addEventListener} and bind * your own functions to that event. * * These functions are known as "listeners" and are passed on to the second argument (`listener`) of the * {@link FusionCharts.addEventListener} and {@link FusionCharts#addEventListener} functions. * * @callback FusionCharts~eventListener * @see FusionCharts.addEventListener * @see FusionCharts.removeEventListener * * @param {object} eventObject - The first parameter passed to the listener function is an event object * that contains all information pertaining to a particular event. * * @param {string} eventObject.type - The name of the event. * * @param {number} eventObject.eventId - A unique ID associated with the event. Internally it is an * incrementing counter and as such can be indirectly used to verify the order in which the event was * fired. * * @param {FusionCharts} eventObject.sender - The instance of FusionCharts object that fired this event. * Occassionally, for events that are not fired by individual charts, but are fired by the framework, * will have the framework as this property. * * @param {boolean} eventObject.cancelled - Shows whether an event's propagation was cancelled or not. * It is set to `true` when `.stopPropagation()` is called. * * @param {function} eventObject.stopPropagation - Call this function from within a listener to prevent * subsequent listeners from being executed. * * @param {boolean} eventObject.prevented - Shows whether the default action of this event has been * prevented. It is set to `true` when `.preventDefault()` is called. * * @param {function} eventObject.preventDefault - Call this function to prevent the default action of an * event. For example, for the event {@link FusionCharts#event:beforeResize}, if you do * `.preventDefault()`, the resize will never take place and instead * {@link FusionCharts#event:resizeCancelled} will be fired. * * @param {boolean} eventObject.detached - Denotes whether a listener has been detached and no longer * gets executed for any subsequent event of this particular `type`. * * @param {function} eventObject.detachHandler - Allows the listener to remove itself rather than being * called externally by {@link FusionCharts.removeEventListener}. This is very useful for one-time event * listening or for special situations when the event is no longer required to be listened when the * event has been fired with a specific condition. * * @param {object} eventArgs - Every event has an argument object as second parameter that contains * information relevant to that particular event. */ slotLoader(EventTarget.listeners[type], eventObject, args); // Facilitate the call of a global event listener. slotLoader(EventTarget.listeners['*'], eventObject, args); // Execute default action switch (eventObject.prevented) { case true: if (typeof cancelFn === 'function') { try { cancelFn.call(eventScope || sender || win, eventObject, args || {}); } catch (err) { // Call error in a separate thread to avoid stopping // of chart load. setTimeout(function () { throw err; }, 0); } } break; default: if (typeof defaultFn === 'function') { try { defaultFn.call(eventScope || sender || win, eventObject, args || {}); } catch (err) { // Call error in a separate thread to avoid stopping // of chart load. setTimeout(function () { throw err; }, 0); } } } // Statutory W3C NOT preventDefault flag return true; } }, // Facilitate for raising events internally. raiseEvent = global.raiseEvent = function (type, args, obj, eventScope, defaultFn, cancelledFn) { return EventTarget.triggerEvent(type, obj, args, eventScope, defaultFn, cancelledFn); }, /** * List of events that has an equivalent legacy event. Used by the * raiseEvent method to check whether a particular event raised * has any corresponding legacy event. * * @type object */ legacyEventList = global.legacyEventList = {}, /** * Maintains a list of recently raised conditional events * @type object */ conditionChecks = {}; global.disposeEvents = function (target) { var type, i; // Iterate through all events in the collection of listeners for (type in EventTarget.listeners) { for (i = 0; i < EventTarget.listeners[type].length; i += 1) { // When a match is found, delete the listener from the // collection. if (EventTarget.listeners[type][i][1] === target) { EventTarget.listeners[type].splice(i, 1); } } } }; /** * This method allows to uniformly raise events of FusionCharts * Framework. * * @param {string} name specifies the name of the event to be raised. * @param {object} args allows to provide an arguments object to be * passed on to the event listeners. * @param {core} obj is the FusionCharts instance object on * behalf of which the event would be raised. * @param {array} legacyArgs is an array of arguments to be passed on * to the equivalent legacy event. * @param {Event} source * @param {function} defaultFn * @param {function} cancelFn * * @type undefined */ global.raiseEventWithLegacy = function (name, args, obj, legacyArgs, eventScope, defaultFn, cancelledFn) { var legacy = legacyEventList[name]; raiseEvent(name, args, obj, eventScope, defaultFn, cancelledFn); if (legacy && typeof win[legacy] === 'function') { setTimeout(function () { win[legacy].apply(eventScope || win, legacyArgs); }, 0); } }; /** * This allows one to raise related events that are grouped together and * raised by multiple sources. Usually this is used where a congregation * of successive events need to cancel out each other and behave like a * unified entity. * * @param {string} check is used to identify event groups. Provide same value * for all events that you want to group together from multiple sources. * @param {string} name specifies the name of the event to be raised. * @param {object} args allows to provide an arguments object to be * passed on to the event listeners. * @param {core} obj is the FusionCharts instance object on * behalf of which the event would be raised. * @param {object} eventScope * @param {function} defaultFn * @param {function} cancelledFn * * @returns {undefined} */ global.raiseEventGroup = function (check, name, args, obj, eventScope, defaultFn, cancelledFn) { var id = obj.id, hash = check + id; if (conditionChecks[hash]) { clearTimeout(conditionChecks[hash]); delete conditionChecks[hash]; } else { if (id && hash) { conditionChecks[hash] = setTimeout(function () { raiseEvent(name, args, obj, eventScope, defaultFn, cancelledFn); delete conditionChecks[hash]; }, 0); } else { raiseEvent(name, args, obj, eventScope, defaultFn, cancelledFn); } } }; // Extend the eventlisteners to internal global. global.addEventListener = function (type, listener) { return EventTarget.addListener(type, listener); }; global.removeEventListener = function (type, listener) { return EventTarget.removeListener(type, listener); }; global.extend(core, /** @lends FusionCharts */ { /** * Bind callbacks to events fired throughout FusionCharts. This method can be used to listen to events across * all FusionCharts instances on a page. * * An event listener is used to execute custom functions when an event is fired. FusionCharts fires events at * all stages of creating, updating, rendering or removing a chart. This function lets you tap into any of these * events and provide your own functions which will be called when those events are triggered. * * An alternative to this function is to use {@link FusionCharts#addEventListener} method on a chart instance to * bind to an event fired by a specific chart. * * @param {string|array} type - The event name to listen to. The event name is not case sensitive. In case you * want to register an event to multiple events in the same registration call, provide them as an array of event * names. * @param {FusionCharts~eventListener} listener - Pass the function that is to be executed when the event is * fired. Upon an event, the listeners for that event are executed sequentially with arguments that are specific * to that event. See {@link FusionCharts~eventListener} for more details on the arguments. * * @group event-handling:add * @example * // Show a message when a number of charts have been rendered on a page. * FusionCharts.ready(function { * var counter = 0, * threshold = 3; * * FusionCharts.addEventListener("rendered", function (eventObject) { * counter++; * if (counter > threshold) { * alert("More than " + threshold + "charts rendered!"); * } * }); * }); */ addEventListener: function (type, listener) { return EventTarget.addListener(type, listener); }, /** * Removes an event that was originally added using {@link FusionCharts.addEventListener}. * @param {string} type - The event name whose listener needs to be removed/detached. * @param {function} listener - The listener function that needs to be removed. * * @group event-handling:remove */ removeEventListener: function (type, listener) { return EventTarget.removeListener(type, listener); }, /** * This function allows to register callback functions to be executed when FusionCharts library is ready to be * used. In general, the framework is ready after `DOMContentLoaded` browser event has been fired and all the * initial dependent files/modules are available. One can attach multiple callbacks by calling this function any * number of time. * * The callback function is executed even when attached after FusionCharts is already ready! Thus, it is * recommended that all entry-point and initialization codes are written within this block. This also helps in * neatly organizing all codes within a script file or the page `` and as such contextually separating * code from HTML blocks. * * @param {FusionCharts~readyCallback} readyCallback - Pass a function that would be executed as callback when * FusionCharts framework is ready. * @param {*} [args={@link FusionCharts}] - Argument to be passed on to the callback function. * @param {function=} [context={@link FusionCharts}] - In the situation where the function passed via `fn` * parameter needs to be executed in a different scope than the default {@link FusionCharts} scope, pass the * appropriate class object here. * * @example * // Render a chart within a chart container `div` element. * FusionCharts.ready(function (FusionCharts) { * var chart = new FusionCharts({ * type: "column2d", * renderAt: "chart-container-div", * dataSource: "my-chart-data.json", * dataFormat: "jsonurl" * }); * // Since we are in the `ready` block, the `chart-container-div` * // element should be available by now. * chart.render(); * }); */ ready: function (readyCallback, args, context) { // Check if core is already ready or not. If not ready then we need to attach the function to the ready // event listener. if (global.ready) { // If it is already ready, we do not need to check for readiness subsequently as this state cannot ever // rollback. Thus we redefine the function for performant consumption post readiness. core.ready = function (readyCallback, context) { (typeof readyCallback === 'function') && setTimeout(function () { /** * The function passed as ready callback is executed when FusionCharts library is ready. Use * {@link FusionCharts.ready} to request executing of your callback function. * @callback FusionCharts~readyCallback * @param {FusionCharts|*} args - By default, the parameter passed to the callback function is * the FusionCharts library class unless specified otherwise in the `args` parameter of * {@link FusionCharts.ready} */ readyCallback.call(context || core, args || core); }, 0); }; core.ready(readyCallback, context); } else if (typeof readyCallback === 'function') { core.addEventListener('ready', function () { core.ready(readyCallback, args, context); }); } return this; } }); core.on = core.addEventListener; // alias // Add eventListener extensibility to FusionCharts prototype so that individual FusionCharts objects can use // per-chart events. global.extend(core.prototype, /** @lends FusionCharts# */ { /** * Listen to events fired by an individual chart. For more information on the available events, refer to the * events section. * * @param {string|string[]} type - The event name that needs to be listened to. The event name is not case * sensitive. In case you want to register an event to multiple events in the same registration call, provide * them as an array of event names. * @param {FusionCharts~eventListener} listener - Pass the function that is to be executed when the event is * fired. Upon an event, the listeners for that event are executed sequentially with arguments that are * specific to that event. See {@link FusionCharts~eventListener} for more details on the arguments. * * @group event-handling:add */ addEventListener: function (type, listener) { return EventTarget.addListener(type, listener, this); }, /** * Removes an event that was originally added using {@link FusionCharts#addEventListener}. * @param {string} type - The event name whose listener needs to be removed/detached. * @param {function} listener - The listener function that needs to be removed. * * @group event-handling:remove */ removeEventListener: function (type, listener) { return EventTarget.removeListener(type, listener, this); } }); core.prototype.on = core.prototype.addEventListener; // alias // Add ability to parse events sent via core constructor. global.policies.options.events = ['events', {}]; global.addEventListener('beforeInitialize', function (e) { var chart = e.sender, events = chart.options.events, key; if (events) { for (key in events) { if (typeof events[key] === 'function') { chart.addEventListener(key, events[key]); } } } }); // Raise library initialization event, if not already done. if (global.ready && !global.readyNotified) { global.readyNotified = true; /** * @fires FusionCharts#ready */ global.raiseEvent('ready', { version: global.core.version, now: global.readyNow }, global.core); } }]); /** * This module handles all XMLHttpRequests by exposing a managed AJAX module. * @private * * @module fusioncharts.ajax * @requires fusioncharts.constructor */ FusionCharts.register('module', ['private', 'modules.mantle.ajax', function () { var global = this, // define constants for future use. FUNCTION = 'function', MSXMLHTTP = 'Microsoft.XMLHTTP', MSXMLHTTP2 = 'Msxml2.XMLHTTP', GET = 'GET', POST = 'POST', XHREQERROR = 'XmlHttprequest Error', RUN = 'run', ERRNO = '1110111515A', win = global.window, // keep a local reference of window scope // Probe IE version version = parseFloat(win.navigator.appVersion.split('MSIE')[1]), ielt8 = (version >= 5.5 && version <= 7) ? true : false, firefox = /mozilla/i.test(win.navigator.userAgent), // // Calculate flags. // Check whether the page is on file protocol. fileProtocol = win.location.protocol === 'file:', AXObject = win.ActiveXObject, // Check if native xhr is present XHRNative = (!AXObject || !fileProtocol) && win.XMLHttpRequest, // stats /** * These counters keep a track of various http requests sent by FusionCharts. These can be retrieved using * {@link FusionCharts.ajax.stats} * @memberOf FusionCharts.ajax~ * * @property {number} objects - The number of internal `Ajax` objects created by FusionCharts. This is not same * as `XMLHttpRequest` object since one `Ajax` object can have a number of `XMLHttpRequest` objects. * @property {number} xhr - A count of all `XMLHttpRequest` objects created. * @property {number} requests - Sum of all requests sent to server (both GET and POST.) * @property {number} success - The total number of requests that had a successful return from server. * @property {number} failure - The total number of requests that failed to communicate with server due to * various factors. * @property {number} idle - `XMLHttpRequest` objects that are idle for later use or has not been disposed yet. */ counters = { objects: 0, xhr: 0, requests: 0, success: 0, failure: 0, idle: 0 }, // Prepare function to retrieve compatible xmlhttprequest. newXmlHttpRequest = function () { var xmlhttp; // if xmlhttprequest is present as native, use it. if (XHRNative) { newXmlHttpRequest = function () { counters.xhr++; return new XHRNative(); }; return newXmlHttpRequest(); } // Use activeX for IE try { xmlhttp = new AXObject(MSXMLHTTP2); newXmlHttpRequest = function () { counters.xhr++; return new AXObject(MSXMLHTTP2); }; } catch (e) { try { xmlhttp = new AXObject(MSXMLHTTP); newXmlHttpRequest = function () { counters.xhr++; return new AXObject(MSXMLHTTP); }; } catch (e) { xmlhttp = false; } } return xmlhttp; }, // Ajax class. Ajax; /** * @namespace FusionCharts.ajax */ global.core.ajax = /** @lends FusionCharts.ajax */ { /** * Returns the statistics of AJAX requests sent by FusionCharts. * * @param {string} [type] - Optionally, one can request a specific type of statistic by providing the type as * one of the property-name of {@link FusionCharts.ajax~counters}. * * @returns {FusionCharts.ajax~counters} If no specific nature of statistics is passed to the `type` * parameter, this function would return an object containing all counters, otherwise it returns a specific * counter as in {@link FusionCharts.ajax~counters}. */ stats: function (type) { return type ? counters[type] : global.extend({}, counters); }, /** * The default `http-headers` that are sent with every AJAX request to the server. More can be added * and existing items can be modified. * @enum * @group framework */ headers: { /** * Prevents cacheing of AJAX requests. * @type {string} */ 'If-Modified-Since': 'Sat, 29 Oct 1994 19:43:31 GMT', /** * Lets the server know that this is an AJAX request. * @type {string} */ 'X-Requested-With': 'XMLHttpRequest', /** * Lets server know which web application is sending requests. * @type {string} */ 'X-Requested-By': 'FusionCharts', /** * Mentions content-types that are acceptable for the response. Some servers require this for Ajax * communication. * @type {string} */ 'Accept': 'text/plain, */*', /** * The MIME type of the body of the request along with its charset. * @type {string} */ 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' } }; Ajax = global.ajax = function (success, error) { this.onSuccess = success; this.onError = error; this.open = false; counters.objects++; counters.idle++; }; global.extend(Ajax.prototype, { headers: global.core.ajax.headers, transact: function (method, url, data, callbackArgs) { var wrapper = this, xmlhttp = wrapper.xmlhttp, headers = wrapper.headers, errorCallback = wrapper.onError, successCallback = wrapper.onSuccess, isPost = (method === POST), postData, xRequestedBy = 'X-Requested-By', hasOwn = Object.prototype.hasOwnProperty, i; // X-Requested-By is removed from header during cross domain ajax call if (url.search(/^(http:\/\/|https:\/\/)/) !== -1 && win.location.hostname !== /(http:\/\/|https:\/\/)([^\/\:]*)/.exec(url)[2]) { // If the url does not contain http or https, then its a same domain call. No need to use regex to get // domain. If it contains then checks domain. delete headers[xRequestedBy]; } else { !hasOwn.call(headers, xRequestedBy) && (headers[xRequestedBy] = 'FusionCharts'); } if (!xmlhttp || ielt8 || firefox) { xmlhttp = newXmlHttpRequest(); wrapper.xmlhttp = xmlhttp; } xmlhttp.onreadystatechange = function () { try { if (xmlhttp.readyState === 4) { if ((!xmlhttp.status && fileProtocol) || (xmlhttp.status >= 200 && xmlhttp.status < 300) || xmlhttp.status === 304 || xmlhttp.status === 1223 || xmlhttp.status === 0) { successCallback && successCallback(xmlhttp.responseText, wrapper, callbackArgs, url); counters.success++; } else if (errorCallback) { errorCallback(new Error(XHREQERROR), wrapper, callbackArgs, url); counters.failure++; } counters.idle--; wrapper.open = false; } } catch (error) { if (errorCallback) { errorCallback(error, wrapper, callbackArgs, url); } if (win.FC_DEV_ENVIRONMENT) { setTimeout(function () { throw error; }, 0); } counters.failure++; } }; try { xmlhttp.open((isPost ? POST : GET), url, true); if (xmlhttp.overrideMimeType) { xmlhttp.overrideMimeType('text/plain'); } if (isPost) { if (typeof data === 'string') { postData = data; } else { postData = []; for (i in data) { postData.push(i + '=' + (data[i] + '') .replace(/\=/g, '%3D').replace(/\&/g, '%26')); } postData = postData.join('&'); } } else { postData = null; } for (i in headers) { xmlhttp.setRequestHeader(i, headers[i]); } xmlhttp.send(postData); counters.requests++; counters.idle++; wrapper.open = true; } catch (e) { /** * AJAX runtime error. Raised when AJAX transactions raise error (security or others). The error * message describes the nature of the error. * * @typedef {RuntimeException} Error-1110111515A * @memberOf FusionCharts.debugger * @group debugger-error */ global.raiseError(global.core, ERRNO, RUN, XHREQERROR, e.message); } return xmlhttp; }, get: function (url, callbackArgs) { return this.transact(GET, url, undefined, callbackArgs); }, post: function (url, data, callbackArgs) { return this.transact(POST, url, data, callbackArgs); }, abort: function () { var instance = this, xmlhttp = instance.xmlhttp; instance.open = false; return xmlhttp && typeof xmlhttp.abort === FUNCTION && xmlhttp.readyState && xmlhttp.readyState !== 0 && xmlhttp.abort(); }, dispose: function () { var instance = this; instance.open && instance.abort(); delete instance.onError; delete instance.onSuccess; delete instance.xmlhttp; delete instance.open; counters.objects--; return (instance = null); } }); }]); /** * Generic Runtime Module * @private * * @module fusioncharts.runtime * @requires fusioncharts.constructor * @requires fusioncharts.events * @requires fusioncharts.debugger */ FusionCharts.register('module', ['private', 'modules.mantle.runtime;1.1', function () { var global = this, win = global.window, // Set the FusionCharts filename possibilities as regular expression. SCRIPT_NAME_REGEX = /(^|[\/\\])(fusioncharts\.js)([\?#].*)?$/ig, // jshint ignore:line BLOCK_EXTERNAL_SCRIPT_LOADING = false, DISALLOW_CROSSDOMAIN_RESOURCE = false, SCRIPT_LOAD_TIMEOUTMS = 15000, checkBadChars = /[\\\"<>;&]/, hasProtocolDef = /^[^\S]*?(sf|f|ht)(tp|tps):\/\//i, FUNCTION = 'function', /** * Regular Expressions that helps to check XSS security loops. */ LOAD_EVENTNAME = 'externalresourceload', /** * To keep a track of scripts requested. * @type Object */ scriptsRequested = {}, /** * To keep a track of loaded script tags */ scriptTags = {}, /** * To keep a track of scripts loaded. * @type Object */ scriptsLoaded = {}, /** * Keep a track of load failure check * @type object */ scriptLoadFailureTimeout = {}, /** * Function that safely deletes all items in a DOM element. */ purgeDOM = global.purgeDOM = function (d) { var a = d.attributes, i, l, n; if (a) { for (i = a.length - 1; i >= 0; i -= 1) { n = a[i].name; if (typeof d[n] === 'function') { d[n] = null; } } } a = d.childNodes; if (a) { l = a.length; for (i = 0; i < l; i += 1) { purgeDOM(d.childNodes[i]); } } }, // Deconstruct policies. // Update the arguments with latest copy of all variables by // reverse engineering the policies. deconstructPolicySet = function (policies, options, obj) { var policy, prop; for (policy in policies) { // Set just the policy object in case of single-level policy. if (policies[policy] instanceof Array) { options[policies[policy][0]] = obj[policy]; } else { // Copy the source of multi-level policies for (prop in policies[policy]) { options[policies[policy][prop][0]] = obj[policy][prop]; } } } }, signatureMatchRegex = /^(FusionCharts|FusionWidgets|FusionMaps)/; /** * Function to determine the script base uri for a script name */ global.getScriptBaseUri = function (scriptNameRegex) { // Get a collection of all script nodes. var scripts = win.document.getElementsByTagName('script'), l = scripts.length, src, i; // Iterate through the script node collection and match whether its // 'src' attribute contains fusioncharts file name. for (i = 0; i < l; i += 1) { src = scripts[i].getAttribute('src'); if (!(src === undefined || src === null || src.match(scriptNameRegex) === null)) { return src.replace(scriptNameRegex, '$1'); } } return undefined; }; // Get the script base uri. (Regexp has been updated) global.core.options.scriptBaseUri = (function () { var baseUri = global.getScriptBaseUri(SCRIPT_NAME_REGEX); if (baseUri === undefined) { /** * FusionCharts JavaScript Library automatically determines the location where it was loaded from within * the server. This it does by probing the `