Rename sys_mask.mask into sys_mask.fname
[misc/kostenrechnung] / lib / rico / ricoEffects.js
1  /*
2   *  (c) 2005-2007 Richard Cowin (http://openrico.org)
3   *
4   *  Rico is licensed under the Apache License, Version 2.0 (the "License"); you may not use this
5   *  file except in compliance with the License. You may obtain a copy of the License at
6   *   http://www.apache.org/licenses/LICENSE-2.0
7   */
8
9 Rico.animate = function(effect){
10   new Rico.Effect.Animator().play(effect, arguments[1]);
11 };
12
13 /** @namespace */
14 Rico.Effect = {};
15 Rico.Effect.easeIn = function(step){
16   return Math.sqrt(step);
17 };
18 Rico.Effect.easeOut = function(step){
19   return step*step;
20 };
21
22 /** @namespace */
23 Rico.Stepping = {};
24 Rico.Stepping.easeIn = Rico.Effect.easeIn;
25 Rico.Stepping.easeOut = Rico.Effect.easeOut;
26
27 Rico.Effect.Animator = Class.create(
28 /** @lends Rico.Effect.Animator# */
29 {
30 /**
31  * @class Class to animate effects.
32  * @constructs
33  */
34   initialize : function(effect) {
35     this.animateMethod = this.animate.bind(this);
36     this.options = arguments[1] || {};
37     this.stepsLeft = 0;
38     if (!effect) return;
39     this.reset(effect, arguments[1]);
40   },
41   reset: function(effect){
42     this.effect = effect;
43     if (arguments[1]) this.setOptions(arguments[1]);
44     this.stepsLeft = this.options.steps;
45     this.duration = this.options.duration;
46   },
47   setOptions: function(options){
48     this.options = Object.extend({
49       steps: 10,
50       duration: 200,
51       rate: function(steps){ return steps;}
52     }, options || {});
53   },
54   play: function(effect) {
55     this.setOptions(arguments[1]);
56     if (effect) {
57       if (effect.step) {
58         this.reset(effect, arguments[1]);
59       } else {
60         $H(effect).keys().each((function(e){
61           var effectClass = {fadeOut:Rico.Effect.FadeOut}[e];
62           this.reset(new effectClass(effect[e]));
63         }).bind(this));
64       }
65     }
66     this.animate();
67   },
68   stop: function() {
69     if (this.timer) clearTimeout(this.timer);
70     this.stepsLeft = 0;
71     if (this.effect && this.effect.finish) this.effect.finish();
72     if (this.options.onFinish) this.options.onFinish();
73   },
74   pause: function() {
75     this.interupt = true;
76   },
77   resume: function() {
78     this.interupt = false;
79     if (this.stepsLeft >0)
80       this.animate();
81   },
82   animate: function() {
83     if (this.interupt)
84       return;
85     if (this.stepsLeft <=0) {
86       this.stop();
87       return;
88     }
89     if (this.timer) clearTimeout(this.timer);
90     this.effect.step(this.options.rate(this.stepsLeft));
91     this.startNextStep();
92   },
93   startNextStep: function() {
94     var stepDuration = Math.round(this.duration/this.stepsLeft) ;
95     this.duration -= stepDuration;
96     this.stepsLeft--;
97     this.timer = setTimeout(this.animateMethod, stepDuration);
98   },
99   isPlaying: function(){
100     return this.stepsLeft != 0 && !this.interupt;
101   }
102 });
103
104 Rico.Effect.Group = Class.create(
105 /** @lends Rico.Effect.Group# */
106 {
107 /**
108  * @class Class to assist in applying an effect to a group of objects.
109  * @constructs
110  */
111   initialize: function(effects){
112     this.effects = effects;
113   },
114   step: function(stepsToGo){
115     this.effects.each(function(e){e.step(stepsToGo);});
116   },
117   finish: function(){
118     this.effects.each(function(e){if (e.finish) e.finish();});
119   }
120 });
121
122 Rico.Effect.SizeAndPosition = Class.create(
123 /** @lends Rico.Effect.SizeAndPosition# */
124 {
125 /**
126  * @class Animate size and position
127  * @constructs
128  */
129   initialize: function(element, x, y, w, h) {
130     Object.extend(this, new Rico.Effect.SizeAndPositionFade(element, x, y, w, h));
131   }
132 });
133
134 Rico.Effect.SizeAndPositionFade = Class.create(
135 /** @lends Rico.Effect.SizeAndPositionFade# */
136 {
137 /**
138  * @class
139  * @constructs
140  */
141   initialize: function(element, x, y, w, h, value) {
142     this.element = $(element);
143     this.x = typeof(x)=='number' ? x : this.element.offsetLeft;
144     this.y = typeof(y)=='number' ? y : this.element.offsetTop;
145     if (!Prototype.Browser.IE || (document.compatMode && document.compatMode.indexOf("CSS")!=-1)) {
146       this.pw = RicoUtil.nan2zero(Element.getStyle(this.element,'padding-left'))+RicoUtil.nan2zero(Element.getStyle(this.element,'padding-right'));
147       this.pw += RicoUtil.nan2zero(Element.getStyle(this.element,'border-left-width'))+RicoUtil.nan2zero(Element.getStyle(this.element,'border-right-width'));
148       this.ph = RicoUtil.nan2zero(Element.getStyle(this.element,'padding-top'))+RicoUtil.nan2zero(Element.getStyle(this.element,'padding-bottom'));
149       this.ph += RicoUtil.nan2zero(Element.getStyle(this.element,'border-top-width'))+RicoUtil.nan2zero(Element.getStyle(this.element,'border-bottom-width'));
150     } else {
151       this.pw=0;
152       this.ph=0;
153     }
154     this.w = typeof(w)=='number' ? w : this.element.offsetWidth;
155     this.h = typeof(h)=='number' ? h : this.element.offsetHeight;
156     this.opacity = Element.getStyle(this.element, 'opacity') || 1.0;
157     this.target = arguments.length > 5 ? Math.min(value, 1.0) : this.opacity;
158   },
159   step: function(stepsToGo) {
160     var left = this.element.offsetLeft + ((this.x - this.element.offsetLeft)/stepsToGo);
161     var top = this.element.offsetTop + ((this.y - this.element.offsetTop)/stepsToGo);
162     var width = this.element.offsetWidth + ((this.w - this.element.offsetWidth)/stepsToGo) - this.pw;
163     var height = this.element.offsetHeight + ((this.h - this.element.offsetHeight)/stepsToGo) - this.ph;
164     var style = this.element.style;
165     var curOpacity = Element.getStyle(this.element, 'opacity');
166     var newOpacity = curOpacity + (this.target - curOpacity) / stepsToGo;
167     Rico.Effect.setOpacity(this.element, Math.min(Math.max(0,newOpacity),1.0));
168     style.left = left + "px";
169     style.top = top + "px";
170     style.width = width + "px";
171     style.height = height + "px";
172   }
173 });
174
175 Rico.AccordionEffect = Class.create(
176 /** @lends Rico.Effect.AccordionEffect# */
177 {
178 /**
179  * @class
180  * @constructs
181  */
182   initialize: function(toClose, toOpen, height) {
183     this.toClose   = toClose;
184     this.toOpen    = toOpen;
185     toOpen.style.height = "0px";
186     Element.show(toOpen);
187     Element.makeClipping(toOpen);
188     Element.makeClipping(toClose);
189     Rico.Controls.disableNativeControls(toClose);
190     this.endHeight = height;
191   },
192   step: function(framesLeft) {
193     var cHeight = Math.max(1,this.toClose.offsetHeight - parseInt((parseInt(this.toClose.offsetHeight,10))/framesLeft,10));
194     var closeHeight = cHeight + "px";
195     var openHeight = (this.endHeight - cHeight) + "px";
196     this.toClose.style.height = closeHeight;
197     this.toOpen.style.height = openHeight;
198   },
199   finish: function(){
200     Element.hide(this.toClose);
201     this.toOpen.style.height = this.endHeight + "px";
202     this.toClose.style.height = "0px";
203     Element.undoClipping(this.toOpen);
204     Element.undoClipping(this.toClose);
205     Rico.Controls.enableNativeControls(this.toOpen);
206   }
207 });
208
209 Rico.Effect.SizeFromBottom = Class.create(
210 /** @lends Rico.Effect.SizeFromBottom# */
211 {
212 /**
213  * @class
214  * @constructs
215  */
216   initialize: function(element, y, h) {
217     this.element = $(element);
218     this.y = typeof(y)=='number' ? y : this.element.offsetTop;
219     this.h = typeof(h)=='number' ? h : this.element.offsetHeight;
220     this.options  = arguments[3] || {};
221   },
222   step: function(framesToGo) {
223     var top = this.element.offsetTop + ((this.y - this.element.offsetTop)/framesToGo) + "px";
224     var height = this.element.offsetHeight + ((this.h - this.element.offsetHeight)/framesToGo) + "px";
225     var style = this.element.style;
226     style.height = height;
227     style.top = top;
228   }
229 });
230
231 Rico.Effect.Position = Class.create(
232 /** @lends Rico.Effect.Position# */
233 {
234 /**
235  * @class
236  * @constructs
237  */
238   initialize: function(element, x, y) {
239     this.element = $(element);
240     this.x = typeof(x)=='number' ? x : this.element.offsetLeft;
241     this.destTop = typeof(y)=='number' ? y : this.element.offsetTop;
242   },
243   step: function(stepsToGo) {
244     var left = this.element.offsetLeft + ((this.x - this.element.offsetLeft)/stepsToGo) + "px";
245     var top = this.element.offsetTop + ((this.destTop - this.element.offsetTop)/stepsToGo) + "px";
246     var style = this.element.style;
247     style.left = left;
248     style.top = top;
249   }
250 });
251
252 Rico.Effect.FadeTo = Class.create(
253 /** @lends Rico.Effect.FadeTo# */
254 {
255 /**
256  * @class
257  * @constructs
258  */
259   initialize: function(element, value){
260     this.element = element;
261     this.opacity = Element.getStyle(this.element, 'opacity') || 1.0;
262     this.target = Math.min(value, 1.0);
263   },
264   step: function(framesLeft) {
265     var curOpacity = Element.getStyle(this.element, 'opacity');
266     var newOpacity = curOpacity + (this.target - curOpacity)/framesLeft;
267     Rico.Effect.setOpacity(this.element, Math.min(Math.max(0,newOpacity),1.0));
268   }
269 });
270
271 Rico.Effect.FadeOut = Class.create(
272 /** @lends Rico.Effect.FadeOut# */
273 {
274 /**
275  * @class
276  * @constructs
277  */
278   initialize: function(element){
279     this.effect = new Rico.Effect.FadeTo(element, 0.0);
280   },
281   step: function(framesLeft) {
282     this.effect.step(framesLeft);
283   }
284 });
285
286 Rico.Effect.FadeIn = Class.create(
287 /** @lends Rico.Effect.FadeIn# */
288 {
289 /**
290  * @class
291  * @constructs
292  */
293   initialize: function(element){
294     var options = arguments[1] || {};
295     var startValue = options.startValue || 0;
296     Rico.Effect.setOpacity(element, startValue);
297     this.effect = new Rico.Effect.FadeTo(element, 1.0);
298   },
299   step: function(framesLeft) {
300     this.effect.step(framesLeft);
301   }
302 });
303
304 Rico.Effect.setOpacity= function(element, value) {
305   if (element.setOpacity) {
306      element.setOpacity(value);  // use prototype function
307   } else {
308      element.style.filter = "alpha(opacity="+Math.round(value*100)+")";
309      element.style.opacity = value;
310   }
311 }
312
313 Rico.Effect.SizeFromTop = Class.create(
314 /** @lends Rico.Effect.SizeFromTop# */
315 {
316 /**
317  * @class
318  * @constructs
319  */
320   initialize: function(element, scrollElement, y, h) {
321      this.element = $(element);
322      this.h = typeof(h)=='number' ? h : this.element.offsetHeight;
323   //   element.style.top = y;
324      this.scrollElement = scrollElement;
325      this.options  = arguments[4] || {};
326      this.baseHeight = this.options.baseHeight ||  Math.max(this.h, this.element.offsetHeight);
327   },
328   step: function(framesToGo) {
329     var rawHeight = this.element.offsetHeight + ((this.h - this.element.offsetHeight)/framesToGo);
330     var height = rawHeight + "px";
331     var scroll = (rawHeight - this.baseHeight) + "px";
332     this.scrollElement.style.top = scroll;
333     this.element.style.height = height;
334   }
335 });
336
337
338 Rico.Effect.Height = Class.create(
339 /** @lends Rico.Effect.Height# */
340 {
341 /**
342  * @class
343  * @constructs
344  */
345   initialize: function(element, endHeight) {
346     this.element = element;
347     this.endHeight = endHeight;
348   },
349   step: function(stepsLeft) {
350     var height;
351     if (this.element.constructor != Array){
352       height = this.element.offsetHeight + ((this.endHeight - this.element.offsetHeight)/stepsLeft) + "px";
353       this.element.style.height = height;
354     } else {
355       height = this.element[0].offsetHeight + ((this.endHeight - this.element[0].offsetHeight)/stepsLeft) + "px";
356       this.element.each(function(e){e.style.height = height;});
357     }
358   }
359 });
360
361 Rico.Effect.SizeWidth = Class.create(
362 /** @lends Rico.Effect.SizeWidth# */
363 {
364 /**
365  * @class
366  * @constructs
367  */
368   initialize: function(element, endWidth) {
369     this.element = element;
370     this.endWidth = endWidth;
371   },
372   step: function(stepsLeft) {
373      var delta = Math.abs(this.endWidth - parseInt(this.element.offsetWidth,10))/(stepsLeft);
374      this.element.style.width = (this.element.offsetWidth - delta) + "px";
375   }
376 });
377
378 /**
379  * @namespace
380  * these are to support non Safari browsers and keep controls from bleeding through on absolute positioned element.
381  */
382 Rico.Controls = {
383   editors: [],
384   scrollSelectors: [],
385
386   disableNativeControls: function(element) {
387     Rico.Controls.defaultDisabler.disableNative(element);
388   },
389   enableNativeControls: function(element){
390     Rico.Controls.defaultDisabler.enableNative(element);
391   },
392   prepareForSizing: function(element){
393     Element.makeClipping(element);
394     Rico.Controls.disableNativeControls(element);
395   },
396   resetSizing: function(element){
397     Element.undoClipping(element);
398     Rico.Controls.enableNativeControls(element);
399   },
400   registerScrollSelectors: function(selectorSet) {
401     selectorSet.each(function(s){Rico.Controls.scrollSelectors.push(Rico.selector(s));});
402   }
403 };
404
405 Rico.Controls.Disabler = Class.create(
406 /** @lends Rico.Controls.Disabler# */
407 {
408 /**
409  * @class
410  * @constructs
411  */
412   initialize: function(){
413     this.options = Object.extend({
414       excludeSet: [],
415       hidables: Rico.Controls.editors
416     }, arguments[0] || {});
417   },
418   disableNative: function(element) {
419     if (!(/Konqueror|Safari|KHTML/.test(navigator.userAgent))){
420       if (!navigator.appVersion.match(/\bMSIE\b/))
421         this.blockControls(element).each(function(e){Element.makeClipping(e);});
422       else
423         this.hidableControls(element).each(function(e){e.disable();});
424     }
425   },
426   enableNative: function(element){
427     if (!(/Konqueror|Safari|KHTML/.test(navigator.userAgent))){
428       if (!navigator.appVersion.match(/\bMSIE\b/))
429         this.blockControls(element).each(function(e){Element.undoClipping(e);});
430       else
431         this.hidableControls(element).each(function(e){e.enable();});
432     }
433   },
434   blockControls: function(element){
435     try{
436       var includes = [];
437       if (this.options.includeSet)
438         includes = this.options.includeSet;
439       else{
440         var selectors = this.options.includeSelectors || Rico.Controls.scrollSelectors;
441         includes = selectors.map(function(s){return s.findAll(element);}).flatten();
442       }
443       return includes.select(function(e){return (Element.getStyle(e, 'display') != 'none') && !this.options.excludeSet.include(e);}.bind(this));
444     } catch(e) { return []; }
445   },
446   hidableControls: function(element){
447     if (element)
448       return this.options.hidables.select(function(e){return Element.childOf(e, element);});
449     else
450       return this.options.hidables;
451   }
452 });
453
454 Rico.Controls.defaultDisabler = new Rico.Controls.Disabler();
455 Rico.Controls.blankDisabler = new Rico.Controls.Disabler({includeSet:[],hidables:[]});
456
457 Rico.Controls.HidableInput = Class.create(
458 /** @lends Rico.Controls.HidableInput# */
459 {
460 /**
461  * @class
462  * @constructs
463  */
464   initialize: function(field, view){
465     this.field = field;
466     this.view = view;
467     this.enable();
468     Rico.Controls.editors.push(this);
469   },
470   enable: function(){
471     Element.hide(this.view);
472     Element.show(this.field);
473   },
474   disable: function(){
475     this.view.value = $F(this.field);
476     if (this.field.offsetWidth > 1) {
477       this.view.style.width =  parseInt(this.field.offsetWidth,10)  + "px";
478       Element.hide(this.field);
479       Element.show(this.view);
480     }
481   }
482 });
483
484
485
486 /** @ignore */
487 Element.forceRefresh = function(item) {
488   try {
489     var n = document.createTextNode(' ');
490     item.appendChild(n); item.removeChild(n);
491   } catch(e) { }
492 };
493
494 Rico.includeLoaded('ricoEffects.js');