| /** | 
|  * @license Highcharts JS v3.0.6 (2013-10-04) | 
|  * Prototype adapter | 
|  * | 
|  * @author Michael Nelson, Torstein Hønsi. | 
|  * | 
|  * Feel free to use and modify this script. | 
|  * Highcharts license: www.highcharts.com/license. | 
|  */ | 
|   | 
| // JSLint options: | 
| /*global Effect, Class, Event, Element, $, $$, $A */ | 
|   | 
| // Adapter interface between prototype and the Highcharts charting library | 
| var HighchartsAdapter = (function () { | 
|   | 
| var hasEffect = typeof Effect !== 'undefined'; | 
|   | 
| return { | 
|   | 
|     /** | 
|      * Initialize the adapter. This is run once as Highcharts is first run. | 
|      * @param {Object} pathAnim The helper object to do animations across adapters. | 
|      */ | 
|     init: function (pathAnim) { | 
|         if (hasEffect) { | 
|             /** | 
|              * Animation for Highcharts SVG element wrappers only | 
|              * @param {Object} element | 
|              * @param {Object} attribute | 
|              * @param {Object} to | 
|              * @param {Object} options | 
|              */ | 
|             Effect.HighchartsTransition = Class.create(Effect.Base, { | 
|                 initialize: function (element, attr, to, options) { | 
|                     var from, | 
|                         opts; | 
|   | 
|                     this.element = element; | 
|                     this.key = attr; | 
|                     from = element.attr ? element.attr(attr) : $(element).getStyle(attr); | 
|   | 
|                     // special treatment for paths | 
|                     if (attr === 'd') { | 
|                         this.paths = pathAnim.init( | 
|                             element, | 
|                             element.d, | 
|                             to | 
|                         ); | 
|                         this.toD = to; | 
|   | 
|   | 
|                         // fake values in order to read relative position as a float in update | 
|                         from = 0; | 
|                         to = 1; | 
|                     } | 
|   | 
|                     opts = Object.extend((options || {}), { | 
|                         from: from, | 
|                         to: to, | 
|                         attribute: attr | 
|                     }); | 
|                     this.start(opts); | 
|                 }, | 
|                 setup: function () { | 
|                     HighchartsAdapter._extend(this.element); | 
|                     // If this is the first animation on this object, create the _highcharts_animation helper that | 
|                     // contain pointers to the animation objects. | 
|                     if (!this.element._highchart_animation) { | 
|                         this.element._highchart_animation = {}; | 
|                     } | 
|   | 
|                     // Store a reference to this animation instance. | 
|                     this.element._highchart_animation[this.key] = this; | 
|                 }, | 
|                 update: function (position) { | 
|                     var paths = this.paths, | 
|                         element = this.element, | 
|                         obj; | 
|   | 
|                     if (paths) { | 
|                         position = pathAnim.step(paths[0], paths[1], position, this.toD); | 
|                     } | 
|   | 
|                     if (element.attr) { // SVGElement | 
|                          | 
|                         if (element.element) { // If not, it has been destroyed (#1405) | 
|                             element.attr(this.options.attribute, position); | 
|                         } | 
|                      | 
|                     } else { // HTML, #409 | 
|                         obj = {}; | 
|                         obj[this.options.attribute] = position; | 
|                         $(element).setStyle(obj); | 
|                     } | 
|                      | 
|                 }, | 
|                 finish: function () { | 
|                     // Delete the property that holds this animation now that it is finished. | 
|                     // Both canceled animations and complete ones gets a 'finish' call. | 
|                     if (this.element && this.element._highchart_animation) { // #1405 | 
|                         delete this.element._highchart_animation[this.key]; | 
|                     } | 
|                 } | 
|             }); | 
|         } | 
|     }, | 
|      | 
|     /** | 
|      * Run a general method on the framework, following jQuery syntax | 
|      * @param {Object} el The HTML element | 
|      * @param {String} method Which method to run on the wrapped element | 
|      */ | 
|     adapterRun: function (el, method) { | 
|          | 
|         // This currently works for getting inner width and height. If adding | 
|         // more methods later, we need a conditional implementation for each. | 
|         return parseInt($(el).getStyle(method), 10); | 
|          | 
|     }, | 
|   | 
|     /** | 
|      * Downloads a script and executes a callback when done. | 
|      * @param {String} scriptLocation | 
|      * @param {Function} callback | 
|      */ | 
|     getScript: function (scriptLocation, callback) { | 
|         var head = $$('head')[0]; // Returns an array, so pick the first element. | 
|         if (head) { | 
|             // Append a new 'script' element, set its type and src attributes, add a 'load' handler that calls the callback | 
|             head.appendChild(new Element('script', { type: 'text/javascript', src: scriptLocation}).observe('load', callback)); | 
|         } | 
|     }, | 
|   | 
|     /** | 
|      * Custom events in prototype needs to be namespaced. This method adds a namespace 'h:' in front of | 
|      * events that are not recognized as native. | 
|      */ | 
|     addNS: function (eventName) { | 
|         var HTMLEvents = /^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/, | 
|             MouseEvents = /^(?:click|mouse(?:down|up|over|move|out))$/; | 
|         return (HTMLEvents.test(eventName) || MouseEvents.test(eventName)) ? | 
|             eventName : | 
|             'h:' + eventName; | 
|     }, | 
|   | 
|     // el needs an event to be attached. el is not necessarily a dom element | 
|     addEvent: function (el, event, fn) { | 
|         if (el.addEventListener || el.attachEvent) { | 
|             Event.observe($(el), HighchartsAdapter.addNS(event), fn); | 
|   | 
|         } else { | 
|             HighchartsAdapter._extend(el); | 
|             el._highcharts_observe(event, fn); | 
|         } | 
|     }, | 
|   | 
|     // motion makes things pretty. use it if effects is loaded, if not... still get to the end result. | 
|     animate: function (el, params, options) { | 
|         var key, | 
|             fx; | 
|   | 
|         // default options | 
|         options = options || {}; | 
|         options.delay = 0; | 
|         options.duration = (options.duration || 500) / 1000; | 
|         options.afterFinish = options.complete; | 
|   | 
|         // animate wrappers and DOM elements | 
|         if (hasEffect) { | 
|             for (key in params) { | 
|                 // The fx variable is seemingly thrown away here, but the Effect.setup will add itself to the _highcharts_animation object | 
|                 // on the element itself so its not really lost. | 
|                 fx = new Effect.HighchartsTransition($(el), key, params[key], options); | 
|             } | 
|         } else { | 
|             if (el.attr) { // #409 without effects | 
|                 for (key in params) { | 
|                     el.attr(key, params[key]); | 
|                 } | 
|             } | 
|             if (options.complete) { | 
|                 options.complete(); | 
|             } | 
|         } | 
|   | 
|         if (!el.attr) { // HTML element, #409 | 
|             $(el).setStyle(params); | 
|         } | 
|     }, | 
|   | 
|     // this only occurs in higcharts 2.0+ | 
|     stop: function (el) { | 
|         var key; | 
|         if (el._highcharts_extended && el._highchart_animation) { | 
|             for (key in el._highchart_animation) { | 
|                 // Cancel the animation | 
|                 // The 'finish' function in the Effect object will remove the reference | 
|                 el._highchart_animation[key].cancel(); | 
|             } | 
|         } | 
|     }, | 
|   | 
|     // um.. each | 
|     each: function (arr, fn) { | 
|         $A(arr).each(fn); | 
|     }, | 
|      | 
|     inArray: function (item, arr, from) { | 
|         return arr ? arr.indexOf(item, from) : -1; | 
|     }, | 
|   | 
|     /** | 
|      * Get the cumulative offset relative to the top left of the page. This method, unlike its | 
|      * jQuery and MooTools counterpart, still suffers from issue #208 regarding the position | 
|      * of a chart within a fixed container. | 
|      */ | 
|     offset: function (el) { | 
|         return $(el).cumulativeOffset(); | 
|     }, | 
|   | 
|     // fire an event based on an event name (event) and an object (el). | 
|     // again, el may not be a dom element | 
|     fireEvent: function (el, event, eventArguments, defaultFunction) { | 
|         if (el.fire) { | 
|             el.fire(HighchartsAdapter.addNS(event), eventArguments); | 
|         } else if (el._highcharts_extended) { | 
|             eventArguments = eventArguments || {}; | 
|             el._highcharts_fire(event, eventArguments); | 
|         } | 
|   | 
|         if (eventArguments && eventArguments.defaultPrevented) { | 
|             defaultFunction = null; | 
|         } | 
|   | 
|         if (defaultFunction) { | 
|             defaultFunction(eventArguments); | 
|         } | 
|     }, | 
|   | 
|     removeEvent: function (el, event, handler) { | 
|         if ($(el).stopObserving) { | 
|             if (event) { | 
|                 event = HighchartsAdapter.addNS(event); | 
|             } | 
|             $(el).stopObserving(event, handler); | 
|         } if (window === el) { | 
|             Event.stopObserving(el, event, handler); | 
|         } else { | 
|             HighchartsAdapter._extend(el); | 
|             el._highcharts_stop_observing(event, handler); | 
|         } | 
|     }, | 
|      | 
|     washMouseEvent: function (e) { | 
|         return e; | 
|     }, | 
|   | 
|     // um, grep | 
|     grep: function (arr, fn) { | 
|         return arr.findAll(fn); | 
|     }, | 
|   | 
|     // um, map | 
|     map: function (arr, fn) { | 
|         return arr.map(fn); | 
|     }, | 
|   | 
|     // extend an object to handle highchart events (highchart objects, not svg elements). | 
|     // this is a very simple way of handling events but whatever, it works (i think) | 
|     _extend: function (object) { | 
|         if (!object._highcharts_extended) { | 
|             Object.extend(object, { | 
|                 _highchart_events: {}, | 
|                 _highchart_animation: null, | 
|                 _highcharts_extended: true, | 
|                 _highcharts_observe: function (name, fn) { | 
|                     this._highchart_events[name] = [this._highchart_events[name], fn].compact().flatten(); | 
|                 }, | 
|                 _highcharts_stop_observing: function (name, fn) { | 
|                     if (name) { | 
|                         if (fn) { | 
|                             this._highchart_events[name] = [this._highchart_events[name]].compact().flatten().without(fn); | 
|                         } else { | 
|                             delete this._highchart_events[name]; | 
|                         } | 
|                     } else { | 
|                         this._highchart_events = {}; | 
|                     } | 
|                 }, | 
|                 _highcharts_fire: function (name, args) { | 
|                     var target = this; | 
|                     (this._highchart_events[name] || []).each(function (fn) { | 
|                         // args is never null here | 
|                         if (args.stopped) { | 
|                             return; // "throw $break" wasn't working. i think because of the scope of 'this'. | 
|                         } | 
|   | 
|                         // Attach a simple preventDefault function to skip default handler if called | 
|                         args.preventDefault = function () { | 
|                             args.defaultPrevented = true; | 
|                         }; | 
|                         args.target = target; | 
|   | 
|                         // If the event handler return false, prevent the default handler from executing | 
|                         if (fn.bind(this)(args) === false) { | 
|                             args.preventDefault(); | 
|                         } | 
|                     } | 
| .bind(this)); | 
|                 } | 
|             }); | 
|         } | 
|     } | 
| }; | 
| }()); |