懒羊羊
2023-08-30 1ac2bc1590406d9babec036e154d8d08f34a6aa1
提交 | 用户 | 时间
1ac2bc 1 /**
2  * @license Highcharts JS v3.0.6 (2013-10-04)
3  * Prototype adapter
4  *
5  * @author Michael Nelson, Torstein Hønsi.
6  *
7  * Feel free to use and modify this script.
8  * Highcharts license: www.highcharts.com/license.
9  */
10
11 // JSLint options:
12 /*global Effect, Class, Event, Element, $, $$, $A */
13
14 // Adapter interface between prototype and the Highcharts charting library
15 var HighchartsAdapter = (function () {
16
17 var hasEffect = typeof Effect !== 'undefined';
18
19 return {
20
21     /**
22      * Initialize the adapter. This is run once as Highcharts is first run.
23      * @param {Object} pathAnim The helper object to do animations across adapters.
24      */
25     init: function (pathAnim) {
26         if (hasEffect) {
27             /**
28              * Animation for Highcharts SVG element wrappers only
29              * @param {Object} element
30              * @param {Object} attribute
31              * @param {Object} to
32              * @param {Object} options
33              */
34             Effect.HighchartsTransition = Class.create(Effect.Base, {
35                 initialize: function (element, attr, to, options) {
36                     var from,
37                         opts;
38
39                     this.element = element;
40                     this.key = attr;
41                     from = element.attr ? element.attr(attr) : $(element).getStyle(attr);
42
43                     // special treatment for paths
44                     if (attr === 'd') {
45                         this.paths = pathAnim.init(
46                             element,
47                             element.d,
48                             to
49                         );
50                         this.toD = to;
51
52
53                         // fake values in order to read relative position as a float in update
54                         from = 0;
55                         to = 1;
56                     }
57
58                     opts = Object.extend((options || {}), {
59                         from: from,
60                         to: to,
61                         attribute: attr
62                     });
63                     this.start(opts);
64                 },
65                 setup: function () {
66                     HighchartsAdapter._extend(this.element);
67                     // If this is the first animation on this object, create the _highcharts_animation helper that
68                     // contain pointers to the animation objects.
69                     if (!this.element._highchart_animation) {
70                         this.element._highchart_animation = {};
71                     }
72
73                     // Store a reference to this animation instance.
74                     this.element._highchart_animation[this.key] = this;
75                 },
76                 update: function (position) {
77                     var paths = this.paths,
78                         element = this.element,
79                         obj;
80
81                     if (paths) {
82                         position = pathAnim.step(paths[0], paths[1], position, this.toD);
83                     }
84
85                     if (element.attr) { // SVGElement
86                         
87                         if (element.element) { // If not, it has been destroyed (#1405)
88                             element.attr(this.options.attribute, position);
89                         }
90                     
91                     } else { // HTML, #409
92                         obj = {};
93                         obj[this.options.attribute] = position;
94                         $(element).setStyle(obj);
95                     }
96                     
97                 },
98                 finish: function () {
99                     // Delete the property that holds this animation now that it is finished.
100                     // Both canceled animations and complete ones gets a 'finish' call.
101                     if (this.element && this.element._highchart_animation) { // #1405
102                         delete this.element._highchart_animation[this.key];
103                     }
104                 }
105             });
106         }
107     },
108     
109     /**
110      * Run a general method on the framework, following jQuery syntax
111      * @param {Object} el The HTML element
112      * @param {String} method Which method to run on the wrapped element
113      */
114     adapterRun: function (el, method) {
115         
116         // This currently works for getting inner width and height. If adding
117         // more methods later, we need a conditional implementation for each.
118         return parseInt($(el).getStyle(method), 10);
119         
120     },
121
122     /**
123      * Downloads a script and executes a callback when done.
124      * @param {String} scriptLocation
125      * @param {Function} callback
126      */
127     getScript: function (scriptLocation, callback) {
128         var head = $$('head')[0]; // Returns an array, so pick the first element.
129         if (head) {
130             // Append a new 'script' element, set its type and src attributes, add a 'load' handler that calls the callback
131             head.appendChild(new Element('script', { type: 'text/javascript', src: scriptLocation}).observe('load', callback));
132         }
133     },
134
135     /**
136      * Custom events in prototype needs to be namespaced. This method adds a namespace 'h:' in front of
137      * events that are not recognized as native.
138      */
139     addNS: function (eventName) {
140         var HTMLEvents = /^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/,
141             MouseEvents = /^(?:click|mouse(?:down|up|over|move|out))$/;
142         return (HTMLEvents.test(eventName) || MouseEvents.test(eventName)) ?
143             eventName :
144             'h:' + eventName;
145     },
146
147     // el needs an event to be attached. el is not necessarily a dom element
148     addEvent: function (el, event, fn) {
149         if (el.addEventListener || el.attachEvent) {
150             Event.observe($(el), HighchartsAdapter.addNS(event), fn);
151
152         } else {
153             HighchartsAdapter._extend(el);
154             el._highcharts_observe(event, fn);
155         }
156     },
157
158     // motion makes things pretty. use it if effects is loaded, if not... still get to the end result.
159     animate: function (el, params, options) {
160         var key,
161             fx;
162
163         // default options
164         options = options || {};
165         options.delay = 0;
166         options.duration = (options.duration || 500) / 1000;
167         options.afterFinish = options.complete;
168
169         // animate wrappers and DOM elements
170         if (hasEffect) {
171             for (key in params) {
172                 // The fx variable is seemingly thrown away here, but the Effect.setup will add itself to the _highcharts_animation object
173                 // on the element itself so its not really lost.
174                 fx = new Effect.HighchartsTransition($(el), key, params[key], options);
175             }
176         } else {
177             if (el.attr) { // #409 without effects
178                 for (key in params) {
179                     el.attr(key, params[key]);
180                 }
181             }
182             if (options.complete) {
183                 options.complete();
184             }
185         }
186
187         if (!el.attr) { // HTML element, #409
188             $(el).setStyle(params);
189         }
190     },
191
192     // this only occurs in higcharts 2.0+
193     stop: function (el) {
194         var key;
195         if (el._highcharts_extended && el._highchart_animation) {
196             for (key in el._highchart_animation) {
197                 // Cancel the animation
198                 // The 'finish' function in the Effect object will remove the reference
199                 el._highchart_animation[key].cancel();
200             }
201         }
202     },
203
204     // um.. each
205     each: function (arr, fn) {
206         $A(arr).each(fn);
207     },
208     
209     inArray: function (item, arr, from) {
210         return arr ? arr.indexOf(item, from) : -1;
211     },
212
213     /**
214      * Get the cumulative offset relative to the top left of the page. This method, unlike its
215      * jQuery and MooTools counterpart, still suffers from issue #208 regarding the position
216      * of a chart within a fixed container.
217      */
218     offset: function (el) {
219         return $(el).cumulativeOffset();
220     },
221
222     // fire an event based on an event name (event) and an object (el).
223     // again, el may not be a dom element
224     fireEvent: function (el, event, eventArguments, defaultFunction) {
225         if (el.fire) {
226             el.fire(HighchartsAdapter.addNS(event), eventArguments);
227         } else if (el._highcharts_extended) {
228             eventArguments = eventArguments || {};
229             el._highcharts_fire(event, eventArguments);
230         }
231
232         if (eventArguments && eventArguments.defaultPrevented) {
233             defaultFunction = null;
234         }
235
236         if (defaultFunction) {
237             defaultFunction(eventArguments);
238         }
239     },
240
241     removeEvent: function (el, event, handler) {
242         if ($(el).stopObserving) {
243             if (event) {
244                 event = HighchartsAdapter.addNS(event);
245             }
246             $(el).stopObserving(event, handler);
247         } if (window === el) {
248             Event.stopObserving(el, event, handler);
249         } else {
250             HighchartsAdapter._extend(el);
251             el._highcharts_stop_observing(event, handler);
252         }
253     },
254     
255     washMouseEvent: function (e) {
256         return e;
257     },
258
259     // um, grep
260     grep: function (arr, fn) {
261         return arr.findAll(fn);
262     },
263
264     // um, map
265     map: function (arr, fn) {
266         return arr.map(fn);
267     },
268
269     // extend an object to handle highchart events (highchart objects, not svg elements).
270     // this is a very simple way of handling events but whatever, it works (i think)
271     _extend: function (object) {
272         if (!object._highcharts_extended) {
273             Object.extend(object, {
274                 _highchart_events: {},
275                 _highchart_animation: null,
276                 _highcharts_extended: true,
277                 _highcharts_observe: function (name, fn) {
278                     this._highchart_events[name] = [this._highchart_events[name], fn].compact().flatten();
279                 },
280                 _highcharts_stop_observing: function (name, fn) {
281                     if (name) {
282                         if (fn) {
283                             this._highchart_events[name] = [this._highchart_events[name]].compact().flatten().without(fn);
284                         } else {
285                             delete this._highchart_events[name];
286                         }
287                     } else {
288                         this._highchart_events = {};
289                     }
290                 },
291                 _highcharts_fire: function (name, args) {
292                     var target = this;
293                     (this._highchart_events[name] || []).each(function (fn) {
294                         // args is never null here
295                         if (args.stopped) {
296                             return; // "throw $break" wasn't working. i think because of the scope of 'this'.
297                         }
298
299                         // Attach a simple preventDefault function to skip default handler if called
300                         args.preventDefault = function () {
301                             args.defaultPrevented = true;
302                         };
303                         args.target = target;
304
305                         // If the event handler return false, prevent the default handler from executing
306                         if (fn.bind(this)(args) === false) {
307                             args.preventDefault();
308                         }
309                     }
310 .bind(this));
311                 }
312             });
313         }
314     }
315 };
316 }());