fnordmetric 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
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
+ })();