fnordmetric 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. data/.document +5 -0
  2. data/Gemfile +18 -0
  3. data/Gemfile.lock +69 -0
  4. data/Procfile +2 -0
  5. data/Rakefile +28 -0
  6. data/VERSION +1 -0
  7. data/doc/example_server.rb +56 -0
  8. data/fnordmetric.gemspec +145 -0
  9. data/haml/app.haml +48 -0
  10. data/haml/widget.haml +9 -0
  11. data/lib/fnordmetric.rb +21 -0
  12. data/lib/fnordmetric/app.rb +70 -0
  13. data/lib/fnordmetric/average_metric.rb +7 -0
  14. data/lib/fnordmetric/cache.rb +20 -0
  15. data/lib/fnordmetric/combine_metric.rb +7 -0
  16. data/lib/fnordmetric/core.rb +66 -0
  17. data/lib/fnordmetric/count_metric.rb +13 -0
  18. data/lib/fnordmetric/dashboard.rb +30 -0
  19. data/lib/fnordmetric/engine.rb +3 -0
  20. data/lib/fnordmetric/event.rb +28 -0
  21. data/lib/fnordmetric/funnel_widget.rb +2 -0
  22. data/lib/fnordmetric/metric.rb +80 -0
  23. data/lib/fnordmetric/metric_api.rb +37 -0
  24. data/lib/fnordmetric/numbers_widget.rb +18 -0
  25. data/lib/fnordmetric/report.rb +29 -0
  26. data/lib/fnordmetric/sum_metric.rb +13 -0
  27. data/lib/fnordmetric/timeline_widget.rb +17 -0
  28. data/lib/fnordmetric/widget.rb +75 -0
  29. data/pub/fnordmetric/fnordmetric.css +53 -0
  30. data/pub/fnordmetric/fnordmetric.js +44 -0
  31. data/pub/fnordmetric/widget_numbers.js +71 -0
  32. data/pub/fnordmetric/widget_timeline.css +0 -0
  33. data/pub/fnordmetric/widget_timeline.js +110 -0
  34. data/pub/highcharts/adapters/mootools-adapter.js +12 -0
  35. data/pub/highcharts/adapters/mootools-adapter.src.js +243 -0
  36. data/pub/highcharts/adapters/prototype-adapter.js +14 -0
  37. data/pub/highcharts/adapters/prototype-adapter.src.js +284 -0
  38. data/pub/highcharts/highcharts.js +170 -0
  39. data/pub/highcharts/highcharts.src.js +11103 -0
  40. data/pub/highcharts/modules/exporting.js +22 -0
  41. data/pub/highcharts/modules/exporting.src.js +703 -0
  42. data/pub/highcharts/themes/dark-blue.js +268 -0
  43. data/pub/highcharts/themes/dark-green.js +268 -0
  44. data/pub/highcharts/themes/gray.js +262 -0
  45. data/pub/highcharts/themes/grid.js +97 -0
  46. data/pub/jquery-1.6.1.min.js +18 -0
  47. data/pub/sprite.png +0 -0
  48. data/readme.rdoc +274 -0
  49. data/spec/app_spec.rb +178 -0
  50. data/spec/cache_spec.rb +53 -0
  51. data/spec/combine_metric_spec.rb +19 -0
  52. data/spec/core_spec.rb +50 -0
  53. data/spec/count_metric_spec.rb +32 -0
  54. data/spec/dashboard_spec.rb +67 -0
  55. data/spec/event_spec.rb +46 -0
  56. data/spec/metric_spec.rb +118 -0
  57. data/spec/report_spec.rb +87 -0
  58. data/spec/spec_helper.rb +13 -0
  59. data/spec/sum_metric_spec.rb +33 -0
  60. data/spec/widget_spec.rb +107 -0
  61. metadata +271 -0
@@ -0,0 +1,53 @@
1
+ body, html{ height:100%; padding:0px;}
2
+ body{ background:#fff; color:#333; margin:0; padding:0; overflow-y:scroll; font: 12px/20px "Helvetica Neue", Helvetica, Arial, sans-serif; }
3
+
4
+ #wrap{ margin:0 40px; }
5
+
6
+ #tabs{ width:200px; position:fixed; height:100%; margin-top:70px; }
7
+ #tabs ul{ list-style-type:none; padding:0; margin:0; }
8
+ #tabs ul li{ height:34px; line-height:35px; border-bottom:1px dotted #ececec; cursor:pointer; color:#666; font-size:13px; }
9
+ #tabs ul li:after{ content:'›'; display:block; float:right; margin-right:15px; color:#ccc; font-size:16px; line-height:35px; }
10
+ #tabs ul li .picto{ margin-top:10px; margin-right:7px; }
11
+ #tabs ul li:hover, #tabs ul li:hover:after{ color:#000; }
12
+ #tabs ul li:hover .picto{ opacity:1; }
13
+
14
+ .picto{ display:block; height:14px; width:14px; float:left; background:url('/fnordmetric/sprite.png') no-repeat 14px 14px; opacity:0.7; }
15
+ .picto.piechart{ background-position:-42px -173px; width:9px; margin-right:5px; }
16
+
17
+ #viewport{ min-height:800px; float:left; margin-left:220px; padding-top:20px; border-right:1px solid #e0e0e0; border-left:1px solid #e0e0e0; }
18
+
19
+ .headbar{ height:36px; background:#f2f2f2; border-bottom:1px solid #e2e2e2; }
20
+ .headbar h2{ margin:8px; line-height:21px; float:left; height:20px; font-size:14px; }
21
+ .headbar .datepicker{ background:#fff; border:1px solid #999; height:20px; padding:0 7px; float:right; margin:8px -1px; min-width:100px; font-size:11px; font-style:italic; }
22
+ .headbar .button.mr{ margin-right:16px; }
23
+
24
+
25
+ .headbar .button{
26
+ margin:8px 0px; height:16px; float:right; display:block;
27
+ margin-right:-1px;
28
+ background:url('/fnordmetric/sprite.png') no-repeat 0 -49px #eee;
29
+ border:1px solid #999;
30
+ border-bottom-color:#888;
31
+ -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, .1);
32
+ cursor:pointer;
33
+
34
+ font-size: 11px;
35
+ font-weight:bold;
36
+ line-height:16px;
37
+ padding:2px 6px;
38
+ text-align:center;
39
+ text-decoration:none;
40
+ vertical-align:top;
41
+ white-space:nowrap;
42
+ }
43
+
44
+ .headbar .button:hover, .headbar.button.active{background:#ddd;border-bottom-color:#999;-webkit-box-shadow:0 1px 0 rgba(0, 0, 0, .05)}
45
+
46
+ .numbers_container, .number{ float:left; border-right:1px solid #ececec;}
47
+ .number{ margin-left:12px; padding-right:20px; margin-right:10px; }
48
+ .number .value{ color:#333; font-size:30px; display:block; margin-bottom:5px; }
49
+ .number .desc{ color:#999; font-size:12px; }
50
+ .number:last-child{ border-right:none; }
51
+ .numbers_container{ padding-right:0px; width:33.2%; }
52
+
53
+ .numbers_container .title{ padding:4px 10px 1px 10px; color:#333; font-size:13px; display:block; background:#f2f2f2; border-bottom:1px solid #e2e2e2; margin-bottom:15px; }
@@ -0,0 +1,44 @@
1
+ FnordMetric = {
2
+ d: document,
3
+ p: '/fnordmetric/',
4
+ id: function(id){return FnordMetric.d.getElementById(id)},
5
+ tag: function(element){return FnordMetric.d.getElementsByTagName(element)},
6
+ ce: function(element){return FnordMetric.d.createElement(element)},
7
+
8
+ init: function(){
9
+ if(widget_config.path_prefix){ FnordMetric.p = widget_config.path_prefix; }
10
+ },
11
+
12
+ js: function(url, callback){
13
+ var s = FnordMetric.ce('script');
14
+ s.type = "text/javascript";
15
+ s.onload = callback;
16
+ FnordMetric.init();
17
+ s.src = FnordMetric.p+url;
18
+ FnordMetric.tag('head')[0].appendChild(s);
19
+ },
20
+
21
+ css: function(url, callback){
22
+ var s = FnordMetric.ce('link');
23
+ s.type = "text/css";
24
+ s.rel = 'stylesheet';
25
+ s.href = FnordMetric.p+url;
26
+ s.onload = callback;
27
+ FnordMetric.tag('head')[0].appendChild(s);
28
+ },
29
+
30
+ render: function(elem, widget_config){
31
+ var f = FnordMetric.ce('iframe');
32
+ f.style.width = '100%'; f.style.height = widget_config.widget_height+'px';
33
+ f.frameBorder = 'none'; f.scrolling = 'no';
34
+ FnordMetric.id(elem).appendChild(f);
35
+ var s = f.contentDocument.createElement('script')
36
+ s.type = "text/javascript";
37
+ s.src = FnordMetric.p+'/fnordmetric/fnordmetric.js';
38
+ widget_config.path_prefix = FnordMetric.p;
39
+ s.onload = function(){ f.contentWindow.FnordMetric.js(widget_config.widget_url); }
40
+ f.contentWindow.widget_config = widget_config;
41
+ f.contentDocument.getElementsByTagName('head')[0].appendChild(s);
42
+ }
43
+
44
+ };
@@ -0,0 +1,71 @@
1
+ FnordMetric.css('/fnordmetric/fnordmetric.css', function(){});
2
+ FnordMetric.js('/jquery-1.6.1.min.js', function(){
3
+
4
+ drawLayout();
5
+ updateDataAndValues();
6
+
7
+ window.setInterval(updateDataAndValues, 5000);
8
+
9
+ function drawLayout(){
10
+ for(n in widget_config.metrics){
11
+ $('body').append(container=$('<div></div>').attr('class', 'numbers_container').attr('rel', widget_config.metrics[n]));
12
+ container.append($('<div></div>').addClass('title').html(widget_config.metrics[n]));
13
+ for(var k in widget_config.intervals){
14
+ container.append($('<div></div>').addClass('number').attr('rel', k).attr('data',0).append(
15
+ $('<span></span>').addClass('value').html(0)
16
+ ).append(
17
+ $('<span></span>').addClass('desc').html(k)
18
+ ));
19
+ }
20
+ }
21
+ }
22
+
23
+ function updateDataAndValues(){
24
+ for(n in widget_config.metrics){
25
+ for(k in widget_config.intervals){
26
+ var _url = FnordMetric.p+'/metric/'+widget_config.metrics[n]+'?'+widget_config.intervals[k];
27
+ $.get(_url, updateSingleNumber(widget_config.metrics[n], k));
28
+ }
29
+ }
30
+ }
31
+
32
+ function updateSingleNumber(metric_name, interval_name){
33
+ return function(json){
34
+ var _elem = $('.number[rel="'+interval_name+'"]', $('.numbers_container[rel="'+metric_name+'"]'));
35
+ _elem.attr('data', json.value);
36
+ updateValues(4);
37
+ };
38
+ }
39
+
40
+ function formatValue(value){
41
+ if(value < 10){ return value.toFixed(2); }
42
+ if(value > 1000){ return (value/1000.0).toFixed(1) + "k"; }
43
+ return value.toFixed(0);
44
+ }
45
+
46
+ function updateValues(diff_factor){
47
+ var still_running = false;
48
+ $('.number').each(function(){
49
+ var target_val = parseFloat($(this).attr('data'));
50
+ var current_val = parseFloat($(this).attr('data-current'));
51
+ if(!current_val){ current_val=0; }
52
+ var diff = (target_val-current_val)/diff_factor;
53
+ if(diff < 1){ diff=1; }
54
+ if(target_val > current_val){
55
+ still_running = true;
56
+ var new_val = current_val+diff;
57
+ if(new_val > target_val){ new_val = target_val; }
58
+ $(this).attr('data-current', new_val);
59
+ $('.value', this).html(formatValue(new_val));
60
+ }
61
+ });
62
+ if(still_running){
63
+ (function(df){
64
+ window.setTimeout(function(){ updateValues(df); }, 30);
65
+ })(diff_factor);
66
+ }
67
+ }
68
+
69
+
70
+
71
+ });
File without changes
@@ -0,0 +1,110 @@
1
+ FnordMetric.js('/jquery-1.6.1.min.js', function(){
2
+ FnordMetric.js('/highcharts/highcharts.js', function(){
3
+ FnordMetric.css('/fnordmetric/fnordmetric.css', function(){});
4
+ FnordMetric.css('/fnordmetric/widget_timeline.css', function(){});
5
+
6
+
7
+ drawLayout();
8
+ chart = new Highcharts.Chart({
9
+ chart: { renderTo: 'container', defaultSeriesType: widget_config.chart_type, height: 270 },
10
+ series: [],
11
+ title: { text: '' },
12
+ xAxis: {
13
+ type: 'datetime',
14
+ tickInterval: widget_config.tick * 1000,
15
+ title: (widget_config.x_title||''),
16
+ labels: { step: 2 }
17
+ },
18
+ yAxis: {
19
+ title: (widget_config.y_title||''),
20
+ maxPadding: 0
21
+ },
22
+ legend: {
23
+ layout: 'horizontal',
24
+ align: 'top',
25
+ verticalAlign: 'top',
26
+ x: -5,
27
+ y: -3,
28
+ margin: 25,
29
+ borderWidth: 0
30
+ },
31
+ });
32
+ redrawWithRange(true);
33
+
34
+ if(widget_config.autoupdate){
35
+ window.setInterval(function(){
36
+ redrawWithRange(false, true);
37
+ }, 3000);
38
+ }
39
+
40
+ function redrawWithRange(first_time, silent){
41
+ if(!silent){ $("#container").css('opacity', 0.5); }
42
+ redrawDatepicker();
43
+ var _query = '?at='+widget_config.start_timestamp+'-'+widget_config.end_timestamp+
44
+ '&tick='+widget_config.tick+(widget_config.delta ? '&delta=1' : '');
45
+ chart.series = [];
46
+ metrics_completed = 0;
47
+ for(n in widget_config.metrics){
48
+ $.ajax({
49
+ url: FnordMetric.p+'/metric/'+widget_config.metrics[n]+_query,
50
+ success: redrawMetric(first_time, n)
51
+ });
52
+ }
53
+ }
54
+
55
+ function redrawMetric(first_time, n){
56
+ return (function(json){
57
+ for(i in json.values){ json.values[i][0] = json.values[i][0]*1000; }
58
+ if(!first_time){
59
+ chart.get('series-'+n).setData(json.values);
60
+ } else {
61
+ chart.addSeries({name: widget_config.metrics[n], data: json.values, id: 'series-'+n });
62
+ }
63
+ if((metrics_completed += 1) == widget_config.metrics.length){
64
+ $("#container").css('opacity', 1);
65
+ }
66
+ });
67
+ }
68
+
69
+ function redrawDatepicker(){
70
+ $('.datepicker').html(
71
+ Highcharts.dateFormat('%d.%m.%y %H:%M', parseInt(widget_config.start_timestamp)*1000) +
72
+ '&nbsp;&dash;&nbsp;' +
73
+ Highcharts.dateFormat('%d.%m.%y %H:%M', parseInt(widget_config.end_timestamp)*1000)
74
+ );
75
+ }
76
+
77
+ function moveRange(direction){
78
+ v = widget_config.tick*direction*8;
79
+ widget_config.start_timestamp += v;
80
+ widget_config.end_timestamp += v;
81
+ redrawWithRange();
82
+ }
83
+
84
+ function drawLayout(){
85
+ $('body').append( $('<div></div>').attr('class', 'headbar').append(
86
+ $('<div></div>').attr('class', 'button mr').append($('<span></span>').html('refresh')).click(
87
+ function(){ redrawWithRange(); }
88
+ )
89
+ ).append(
90
+ // $('<div></div>').attr('class', 'button').append($('<span></span>').html('1h'))
91
+ //).append(
92
+ // $('<div></div>').attr('class', 'button').append($('<span></span>').html('1d'))
93
+ //).append(
94
+ $('<div></div>').attr('class', 'button mr').append($('<span></span>').html('&rarr;')).click(
95
+ function(){ moveRange(1); }
96
+ )
97
+ ).append(
98
+ $('<div></div>').attr('class', 'datepicker')
99
+ ).append(
100
+ $('<div></div>').attr('class', 'button').append($('<span></span>').html('&larr;')).click(
101
+ function(){ moveRange(-1); }
102
+ )
103
+ ).append(
104
+ $('<h2></h2>').html(widget_config.title)
105
+ ) );
106
+ $('body').append( $('<div></div>').attr('id', 'container') );
107
+ }
108
+
109
+ });
110
+ });
@@ -0,0 +1,12 @@
1
+ /*
2
+ Highcharts JS v2.1.6 (2011-07-08)
3
+ MooTools adapter
4
+
5
+ (c) 2010-2011 Torstein H?nsi
6
+
7
+ License: www.highcharts.com/license
8
+ */
9
+ (function(){var g=window,j=!!g.$merge,h=g.$extend||function(){return Object.append.apply(Object,arguments)};g.HighchartsAdapter={init:function(){var a=Fx.prototype,b=a.start,c=Fx.Morph.prototype,d=c.compute;a.start=function(f){var e=this.element;if(f.d)this.paths=Highcharts.pathAnim.init(e,e.d,this.toD);b.apply(this,arguments);return this};c.compute=function(f,e,k){var i=this.paths;if(i)this.element.attr("d",Highcharts.pathAnim.step(i[0],i[1],k,this.toD));else return d.apply(this,arguments)}},animate:function(a,
10
+ b,c){var d=a.attr,f=c&&c.complete;if(d&&!a.setStyle){a.getStyle=a.attr;a.setStyle=function(){var e=arguments;a.attr.call(a,e[0],e[1][0])};a.$family=a.uid=true}HighchartsAdapter.stop(a);c=new Fx.Morph(d?a:$(a),h({transition:Fx.Transitions.Quad.easeInOut},c));if(b.d)c.toD=b.d;f&&c.addEvent("complete",f);c.start(b);a.fx=c},each:function(a,b){return j?$each(a,b):a.each(b)},map:function(a,b){return a.map(b)},grep:function(a,b){return a.filter(b)},merge:function(){var a=arguments,b=[{}],c=a.length;if(j)a=
11
+ $merge.apply(null,a);else{for(;c--;)b[c+1]=a[c];a=Object.merge.apply(Object,b)}return a},addEvent:function(a,b,c){if(typeof b=="string"){if(b=="unload")b="beforeunload";if(!a.addEvent)if(a.nodeName)a=$(a);else h(a,new Events);a.addEvent(b,c)}},removeEvent:function(a,b,c){if(b){if(b=="unload")b="beforeunload";defined(c)?a.removeEvent(b,c):a.removeEvents(b)}else a.removeEvents()},fireEvent:function(a,b,c,d){b=new Event({type:b,target:a});b=h(b,c);b.preventDefault=function(){d=null};a.fireEvent&&a.fireEvent(b.type,
12
+ b);d&&d(b)},stop:function(a){a.fx&&a.fx.cancel()}}})();
@@ -0,0 +1,243 @@
1
+ /**
2
+ * @license Highcharts JS v2.1.6 (2011-07-08)
3
+ * MooTools adapter
4
+ *
5
+ * (c) 2010-2011 Torstein Hønsi
6
+ *
7
+ * License: www.highcharts.com/license
8
+ */
9
+
10
+ // JSLint options:
11
+ /*global Highcharts, Fx, $, $extend, $each, $merge, Events, Event */
12
+
13
+ (function() {
14
+
15
+ var win = window,
16
+ legacy = !!win.$merge,
17
+ $extend = win.$extend || function() {
18
+ return Object.append.apply(Object, arguments)
19
+ };
20
+
21
+ win.HighchartsAdapter = {
22
+ /**
23
+ * Initialize the adapter. This is run once as Highcharts is first run.
24
+ */
25
+ init: function() {
26
+ var fxProto = Fx.prototype,
27
+ fxStart = fxProto.start,
28
+ morphProto = Fx.Morph.prototype,
29
+ morphCompute = morphProto.compute;
30
+
31
+ // override Fx.start to allow animation of SVG element wrappers
32
+ fxProto.start = function(from, to) {
33
+ var fx = this,
34
+ elem = fx.element;
35
+
36
+ // special for animating paths
37
+ if (from.d) {
38
+ //this.fromD = this.element.d.split(' ');
39
+ fx.paths = Highcharts.pathAnim.init(
40
+ elem,
41
+ elem.d,
42
+ fx.toD
43
+ );
44
+ }
45
+ fxStart.apply(fx, arguments);
46
+
47
+ return this; // chainable
48
+ };
49
+
50
+ // override Fx.step to allow animation of SVG element wrappers
51
+ morphProto.compute = function(from, to, delta) {
52
+ var fx = this,
53
+ paths = fx.paths;
54
+
55
+ if (paths) {
56
+ fx.element.attr(
57
+ 'd',
58
+ Highcharts.pathAnim.step(paths[0], paths[1], delta, fx.toD)
59
+ );
60
+ } else {
61
+ return morphCompute.apply(fx, arguments);
62
+ }
63
+ };
64
+
65
+ },
66
+
67
+ /**
68
+ * Animate a HTML element or SVG element wrapper
69
+ * @param {Object} el
70
+ * @param {Object} params
71
+ * @param {Object} options jQuery-like animation options: duration, easing, callback
72
+ */
73
+ animate: function (el, params, options) {
74
+ var isSVGElement = el.attr,
75
+ effect,
76
+ complete = options && options.complete;
77
+
78
+ if (isSVGElement && !el.setStyle) {
79
+ // add setStyle and getStyle methods for internal use in Moo
80
+ el.getStyle = el.attr;
81
+ el.setStyle = function() { // property value is given as array in Moo - break it down
82
+ var args = arguments;
83
+ el.attr.call(el, args[0], args[1][0]);
84
+ }
85
+ // dirty hack to trick Moo into handling el as an element wrapper
86
+ el.$family = el.uid = true;
87
+ }
88
+
89
+ // stop running animations
90
+ HighchartsAdapter.stop(el);
91
+
92
+ // define and run the effect
93
+ effect = new Fx.Morph(
94
+ isSVGElement ? el : $(el),
95
+ $extend({
96
+ transition: Fx.Transitions.Quad.easeInOut
97
+ }, options)
98
+ );
99
+
100
+ // special treatment for paths
101
+ if (params.d) {
102
+ effect.toD = params.d;
103
+ }
104
+
105
+ // jQuery-like events
106
+ if (complete) {
107
+ effect.addEvent('complete', complete);
108
+ }
109
+
110
+ // run
111
+ effect.start(params);
112
+
113
+ // record for use in stop method
114
+ el.fx = effect;
115
+ },
116
+
117
+ /**
118
+ * MooTool's each function
119
+ *
120
+ */
121
+ each: function(arr, fn) {
122
+ return legacy ?
123
+ $each(arr, fn) :
124
+ arr.each(fn);
125
+ },
126
+
127
+ /**
128
+ * Map an array
129
+ * @param {Array} arr
130
+ * @param {Function} fn
131
+ */
132
+ map: function (arr, fn){
133
+ return arr.map(fn);
134
+ },
135
+
136
+ /**
137
+ * Grep or filter an array
138
+ * @param {Array} arr
139
+ * @param {Function} fn
140
+ */
141
+ grep: function(arr, fn) {
142
+ return arr.filter(fn);
143
+ },
144
+
145
+ /**
146
+ * Deep merge two objects and return a third
147
+ */
148
+ merge: function() {
149
+ var args = arguments,
150
+ args13 = [{}], // MooTools 1.3+
151
+ i = args.length,
152
+ ret;
153
+
154
+ if (legacy) {
155
+ ret = $merge.apply(null, args);
156
+ } else {
157
+ while (i--) {
158
+ args13[i + 1] = args[i];
159
+ }
160
+ ret = Object.merge.apply(Object, args13);
161
+ }
162
+
163
+ return ret;
164
+ },
165
+
166
+ /**
167
+ * Add an event listener
168
+ * @param {Object} el HTML element or custom object
169
+ * @param {String} type Event type
170
+ * @param {Function} fn Event handler
171
+ */
172
+ addEvent: function (el, type, fn) {
173
+ if (typeof type == 'string') { // chart broke due to el being string, type function
174
+
175
+ if (type == 'unload') { // Moo self destructs before custom unload events
176
+ type = 'beforeunload';
177
+ }
178
+
179
+ // if the addEvent method is not defined, el is a custom Highcharts object
180
+ // like series or point
181
+ if (!el.addEvent) {
182
+ if (el.nodeName) {
183
+ el = $(el); // a dynamically generated node
184
+ } else {
185
+ $extend(el, new Events()); // a custom object
186
+ }
187
+ }
188
+
189
+ el.addEvent(type, fn);
190
+ }
191
+ },
192
+
193
+ removeEvent: function(el, type, fn) {
194
+ if (type) {
195
+ if (type == 'unload') { // Moo self destructs before custom unload events
196
+ type = 'beforeunload';
197
+ }
198
+
199
+ if (defined(fn)) {
200
+ el.removeEvent(type, fn);
201
+ } else {
202
+ el.removeEvents(type);
203
+ }
204
+ } else {
205
+ el.removeEvents();
206
+ }
207
+ },
208
+
209
+ fireEvent: function(el, event, eventArguments, defaultFunction) {
210
+ // create an event object that keeps all functions
211
+ event = new Event({
212
+ type: event,
213
+ target: el
214
+ });
215
+ event = $extend(event, eventArguments);
216
+ // override the preventDefault function to be able to use
217
+ // this for custom events
218
+ event.preventDefault = function() {
219
+ defaultFunction = null;
220
+ };
221
+ // if fireEvent is not available on the object, there hasn't been added
222
+ // any events to it above
223
+ if (el.fireEvent) {
224
+ el.fireEvent(event.type, event);
225
+ }
226
+
227
+ // fire the default if it is passed and it is not prevented above
228
+ if (defaultFunction) {
229
+ defaultFunction(event);
230
+ }
231
+ },
232
+
233
+ /**
234
+ * Stop running animations on the object
235
+ */
236
+ stop: function (el) {
237
+ if (el.fx) {
238
+ el.fx.cancel();
239
+ }
240
+ }
241
+ }
242
+
243
+ })();