今回解説するeffects.
Web開発によく使われるライブラリとして、
今回の解説の見どころは、
effects.
それではコードを見ていきましょう。
0001:// script.aculo.us effects.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
0002:
0003:// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
0004:// Contributors:
0005:// Justin Palmer (http://encytemedia.com/)
0006:// Mark Pilgrim (http://diveintomark.org/)
0007:// Martin Bialasinki
0008://
0009:// script.aculo.us is freely distributable under the terms of an MIT-style license.
0010:// For details, see the script.aculo.us web site: http://script.aculo.us/
0011:
1~11行目は、
0012:// converts rgb() and #xxx to #xxxxxx format,
0013:// returns self (or first argument) if not convertable
0014:String.prototype.parseColor = function() {
0015: var color = '#';
0016: if (this.slice(0,4) == 'rgb(') {
0017: var cols = this.slice(4,this.length-1).split(',');
0018: var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
0019: } else {
0020: if (this.slice(0,1) == '#') {
0021: if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
0022: if (this.length==7) color = this.toLowerCase();
0023: }
0024: }
0025: return (color.length==7 ? color : (arguments[0] || this));
0026:};
0027:
0028:/*--------------------------------------------------------------------------*/
0029:
12~28行目のparseColorは、
16行目で、
17行目で、
18行目で、
20行目で、
21行目で、
22行目で、
25行目で、
0030:Element.collectTextNodes = function(element) {
0031: return $A($(element).childNodes).collect( function(node) {
0032: return (node.nodeType==3 ? node.nodeValue :
0033: (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
0034: }).flatten().join('');
0035:};
0036:
30~36行目のcollectTextNodesは、
31行目で、
32行目で、
33行目で、
34行目で、
0037:Element.collectTextNodesIgnoreClass = function(element, className) {
0038: return $A($(element).childNodes).collect( function(node) {
0039: return (node.nodeType==3 ? node.nodeValue :
0040: ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
0041: Element.collectTextNodesIgnoreClass(node, className) : ''));
0042: }).flatten().join('');
0043:};
0044:
37~44行目のcollectTextNodesIgnoreClassは、
0045:Element.setContentZoom = function(element, percent) {
0046: element = $(element);
0047: element.setStyle({fontSize: (percent/100) + 'em'});
0048: if (Prototype.Browser.WebKit) window.scrollBy(0,0);
0049: return element;
0050:};
0051:
45~51行目のsetContentZoomは、
47行目で、 48行目で、 52~55行目のgetInlineOpacityは、 53行目で、 56~66行目のforceRerenderingは、 67~71行目の_elementDoesNotExistErrorは、 72~104行目のTransitionsには、 105~113行目のDefaultOptionsは、 114~130行目のtagifyTextは、 116行目で、 119行目で、 120行目で、 121行目で、 122~125行目で、 124行目で、 127行目で、 131~149行目のmultipleは、 133~136行目で、 138行目で、 140~143行目で、 146行目で、 150~154行目のPAIRSは、 155~165行目のtoggleは、 157行目で、 158行目で、 161行目で、 166行目で、 Effect. 171~174行目のinitializeは、 172行目で、 173行目で、 175~177行目の_eachは、 178~209行目のaddは、 179行目で、 181行目で、 184~199行目で、 204行目で、 207行目で、 210~216行目のremoveは、 211行目で、 212行目で、 217~223行目のloopは、 218行目で、 219行目で、 Effect. 225行目で、 226~231行目のgetは、 233行目で、 全てのエフェクトの基礎となるクラスです。 236行目のpositionには、 237~271行目のstartは、 238~243行目のcodeForEventは、 245行目で、 246行目で、 247行目で、 248行目で、 249行目で、 250行目で、 251行目で、 252行目で、 254~265行目は後述します。 267行目で、 268行目で、 254~265行目でrenderメソッドを、 ここで、 272~289行目のloopは、 274行目で、 275行目で、 276行目で、 277行目で、 278行目で、 279行目で、 開始時刻後の場合、 282行目で、 283行目で、 284行目で、 290~295行目のcancelは、 296~299行目のeventは、 300~306行目のinspectは、 Effectクラスでは、 309~312行目のinitializeは、 310行目で、 311行目で、 313~315行目のupdateは、 314行目で、 316~326行目のfinishは、 配列のエフェクトのそれぞれに、 Effect. 328~336行目のinitializeは、 322行目で、 337~340行目のupdateは、 持続時間0で、 343~345行目のinitializeは、 346行目で、 要素の透明度が変化するエフェクトのクラスです。デフォルトでは現在の透明度から不透明になるフェードインエフェクトを作ります。 350~361行目のinitializeは初期化をする関数です。2番めの引数にオプションをとることができます。 354行目で、 356行目で、 362~365行目のupdateは、 要素の位置が変化するエフェクトのクラスです。デフォルトでは幅0で移動する 368~377行目のinitializeは、 371行目で、 378~386行目のsetupは、 379行目で、 380,381行目で、 382行目で、 387~393行目のupdateは、 395~400行目のEffect. 要素の表示の大きさが変化するエフェクトのクラスです。終了時の大きさの倍率をパーセントで指定 402~415行目のinitializeは、 416~446行目のsetupは、 417行目で、 418行目で、 421~426行目で、 428~434行目で、 436行目で、 438~445行目で、 439行目で、 441行目で、 443行目で、 447~452行目のupdateは、 448行目で、 449行目で、 451行目で、 453~455行目のfinishは、 454行目で、 456~474行目のsetDimensionsは、 458行目で、 459行目で、 460行目で、 461、 464、 467、 471行目で、 要素の背景色が変化するエフェクトのクラスです。デフォルトでは明るい黄色から元の背景色 476~481行目のinitializeは、 479行目で、 482~498行目のsetupは、 484行目で、 487行目で、 491行目で、 493行目で、 496行目の、 497行目の、 499~502行目のupdateは、 500行目で、 503~509行目のfinishは終了時に呼ばれる関数です。 504行目で、 要素に向かってメインウィンドウをスクロールするエフェクトのクラスです。 512行目で、 513行目で、 514行目で、 516行目で、 518行目で、 522行目で、0052:Element.getInlineOpacity = function(element){
0053: return $(element).style.opacity || '';
0054:};
0055:
0056:Element.forceRerendering = function(element) {
0057: try {
0058: element = $(element);
0059: var n = document.createTextNode(' ');
0060: element.appendChild(n);
0061: element.removeChild(n);
0062: } catch(e) { }
0063:};
0064:
0065:/*--------------------------------------------------------------------------*/
0066:
Effect
0067:var Effect = {
0068: _elementDoesNotExistError: {
0069: name: 'ElementDoesNotExistError',
0070: message: 'The specified DOM element does not exist, but is required for this effect to operate'
0071: },
0072: Transitions: {
0073: linear: Prototype.K,
0074: sinoidal: function(pos) {
0075: return (-Math.cos(pos*Math.PI)/2) + 0.5;
0076: },
0077: reverse: function(pos) {
0078: return 1-pos;
0079: },
0080: flicker: function(pos) {
0081: var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
0082: return pos > 1 ? 1 : pos;
0083: },
0084: wobble: function(pos) {
0085: return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
0086: },
0087: pulse: function(pos, pulses) {
0088: pulses = pulses || 5;
0089: return (
0090: ((pos % (1/pulses)) * pulses).round() == 0 ?
0091: ((pos * pulses * 2) - (pos * pulses * 2).floor()) :
0092: 1 - ((pos * pulses * 2) - (pos * pulses * 2).floor())
0093: );
0094: },
0095: spring: function(pos) {
0096: return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
0097: },
0098: none: function(pos) {
0099: return 0;
0100: },
0101: full: function(pos) {
0102: return 1;
0103: }
0104: },
linear 比例の関数(function(x) { return x }) sinoidal sin関数 reverse 逆比例の関数 flicker 乱数でゆらめいてから収束する関数 wobble 徐々に激しくなる震えのような関数 pulse 三角波のような関数 spring バネのようにビョーンとする関数 none 常に0の定数関数 full 常に1の定数関数 0105: DefaultOptions: {
0106: duration: 1.0, // seconds
0107: fps: 100, // 100= assume 66fps max.
0108: sync: false, // true for combining
0109: from: 0.0,
0110: to: 1.0,
0111: delay: 0.0,
0112: queue: 'parallel'
0113: },
0114: tagifyText: function(element) {
0115: var tagifyStyle = 'position:relative';
0116: if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';
0117:
0118: element = $(element);
0119: $A(element.childNodes).each( function(child) {
0120: if (child.nodeType==3) {
0121: child.nodeValue.toArray().each( function(character) {
0122: element.insertBefore(
0123: new Element('span', {style: tagifyStyle}).update(
0124: character == ' ' ? String.fromCharCode(160) : character),
0125: child);
0126: });
0127: Element.remove(child);
0128: }
0129: });
0130: },
0131: multiple: function(element, effect) {
0132: var elements;
0133: if (((typeof element == 'object') ||
0134: Object.isFunction(element)) &&
0135: (element.length))
0136: elements = element;
0137: else
0138: elements = $(element).childNodes;
0139:
0140: var options = Object.extend({
0141: speed: 0.1,
0142: delay: 0.0
0143: }, arguments[2] || { });
0144: var masterDelay = options.delay;
0145:
0146: $A(elements).each( function(element, index) {
0147: new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
0148: });
0149: },
0150: PAIRS: {
0151: 'slide': ['SlideDown','SlideUp'],
0152: 'blind': ['BlindDown','BlindUp'],
0153: 'appear': ['Appear','Fade']
0154: },
0155: toggle: function(element, effect) {
0156: element = $(element);
0157: effect = (effect || 'appear').toLowerCase();
0158: var options = Object.extend({
0159: queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
0160: }, arguments[2] || { });
0161: Effect[element.visible() ?
0162: Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
0163: }
0164:};
0165:
0166:Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;
0167:
Effect.
0168:/* ------------- core effects ------------- */
0169:
0170:Effect.ScopedQueue = Class.create(Enumerable, {
0171: initialize: function() {
0172: this.effects = [];
0173: this.interval = null;
0174: },
0175: _each: function(iterator) {
0176: this.effects._each(iterator);
0177: },
0178: add: function(effect) {
0179: var timestamp = new Date().getTime();
0180:
0181: var position = Object.isString(effect.options.queue) ?
0182: effect.options.queue : effect.options.queue.position;
0183:
0184: switch(position) {
0185: case 'front':
0186: // move unstarted effects after this effect
0187: this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
0188: e.startOn += effect.finishOn;
0189: e.finishOn += effect.finishOn;
0190: });
0191: break;
0192: case 'with-last':
0193: timestamp = this.effects.pluck('startOn').max() || timestamp;
0194: break;
0195: case 'end':
0196: // start effect after last queued effect has finished
0197: timestamp = this.effects.pluck('finishOn').max() || timestamp;
0198: break;
0199: }
0200:
0201: effect.startOn += timestamp;
0202: effect.finishOn += timestamp;
0203:
0204: if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
0205: this.effects.push(effect);
0206:
0207: if (!this.interval)
0208: this.interval = setInterval(this.loop.bind(this), 15);
0209: },
0210: remove: function(effect) {
0211: this.effects = this.effects.reject(function(e) { return e==effect });
0212: if (this.effects.length == 0) {
0213: clearInterval(this.interval);
0214: this.interval = null;
0215: }
0216: },
0217: loop: function() {
0218: var timePos = new Date().getTime();
0219: for(var i=0, len=this.effects.length;i<len;i++)
0220: this.effects[i] && this.effects[i].loop(timePos);
0221: }
0222:});
0223:
Effect.
0224:Effect.Queues = {
0225: instances: $H(),
0226: get: function(queueName) {
0227: if (!Object.isString(queueName)) return queueName;
0228:
0229: return this.instances.get(queueName) ||
0230: this.instances.set(queueName, new Effect.ScopedQueue());
0231: }
0232:};
Effect.
0233:Effect.Queue = Effect.Queues.get('global');
0234:
Effect.
0235:Effect.Base = Class.create({
0236: position: null,
0237: start: function(options) {
0238: function codeForEvent(options,eventName){
0239: return (
0240: (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') +
0241: (options[eventName] ? 'this.options.'+eventName+'(this);' : '')
0242: );
0243: }
0244: if (options && options.transition === false) options.transition = Effect.Transitions.linear;
0245: this.options = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });
0246: this.currentFrame = 0;
0247: this.state = 'idle';
0248: this.startOn = this.options.delay*1000;
0249: this.finishOn = this.startOn+(this.options.duration*1000);
0250: this.fromToDelta = this.options.to-this.options.from;
0251: this.totalTime = this.finishOn-this.startOn;
0252: this.totalFrames = this.options.fps*this.options.duration;
0253:
0254: eval('this.render = function(pos){ '+
0255: 'if (this.state=="idle"){this.state="running";'+
0256: codeForEvent(this.options,'beforeSetup')+
0257: (this.setup ? 'this.setup();':'')+
0258: codeForEvent(this.options,'afterSetup')+
0259: '};if (this.state=="running"){'+
0260: 'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+
0261: 'this.position=pos;'+
0262: codeForEvent(this.options,'beforeUpdate')+
0263: (this.update ? 'this.update(pos);':'')+
0264: codeForEvent(this.options,'afterUpdate')+
0265: '}}');
0266:
0267: this.event('beforeStart');
0268: if (!this.options.sync)
0269: Effect.Queues.get(Object.isString(this.options.queue) ?
0270: 'global' : this.options.queue.scope).add(this);
0271: },
ループ内不変式をevalで削除する
this.render = function(pos){
if (this.state=="idle"){
this.state="running";
if (this.options['beforeSetupInternal']) this.options['beforeSetupInternal'](this);
if (this.options['beforeSetup']) this.options['beforeSetup'](this);
if (this.setup) this.setup();
if (this.options['afterSetupInternal']) this.options['afterSetupInternal'](this);
if (this.options['afterSetup']) this.options['afterSetup'](this);
};
if (this.state=="running"){
pos=this.options.transition(pos)* this.fromToDelta + this.options.from;
this.position=pos;
if (this.options['beforeUpdateInternal']) this.options['beforeUpdateInternal'](this);
if (this.options['beforeUpdate']) this.options['beforeUpdate'](this);
if (this.update) this.update(pos);
if (this.options['afterUpdateInternal']) this.options['afterUpdateInternal'](this);
if (this.options['afterUpdate']) this.options['afterUpdate'](this);
}
};
this.render = function(pos){
if (this.state=="idle"){
this.state="running";
};
if (this.state=="running"){
pos=this.options.transition(pos)* this.fromToDelta + this.options.from;
this.position=pos;
this.update(pos);
}
};
0272: loop: function(timePos) {
0273: if (timePos >= this.startOn) {
0274: if (timePos >= this.finishOn) {
0275: this.render(1.0);
0276: this.cancel();
0277: this.event('beforeFinish');
0278: if (this.finish) this.finish();
0279: this.event('afterFinish');
0280: return;
0281: }
0282: var pos = (timePos - this.startOn) / this.totalTime,
0283: frame = (pos * this.totalFrames).round();
0284: if (frame > this.currentFrame) {
0285: this.render(pos);
0286: this.currentFrame = frame;
0287: }
0288: }
0289: },
0290: cancel: function() {
0291: if (!this.options.sync)
0292: Effect.Queues.get(Object.isString(this.options.queue) ?
0293: 'global' : this.options.queue.scope).remove(this);
0294: this.state = 'finished';
0295: },
0296: event: function(eventName) {
0297: if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
0298: if (this.options[eventName]) this.options[eventName](this);
0299: },
0300: inspect: function() {
0301: var data = $H();
0302: for(property in this)
0303: if (!Object.isFunction(this[property])) data.set(property, this[property]);
0304: return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
0305: }
0306:});
0307:
Effect.
0308:Effect.Parallel = Class.create(Effect.Base, {
0309: initialize: function(effects) {
0310: this.effects = effects || [];
0311: this.start(arguments[1]);
0312: },
0313: update: function(position) {
0314: this.effects.invoke('render', position);
0315: },
0316: finish: function(position) {
0317: this.effects.each( function(effect) {
0318: effect.render(1.0);
0319: effect.cancel();
0320: effect.event('beforeFinish');
0321: if (effect.finish) effect.finish(position);
0322: effect.event('afterFinish');
0323: });
0324: }
0325:});
0326:
Effect.
0327:Effect.Tween = Class.create(Effect.Base, {
0328: initialize: function(object, from, to) {
0329: object = Object.isString(object) ? $(object) : object;
0330: var args = $A(arguments), method = args.last(),
0331: options = args.length == 5 ? args[3] : null;
0332: this.method = Object.isFunction(method) ? method.bind(object) :
0333: Object.isFunction(object[method]) ? object[method].bind(object) :
0334: function(value) { object[method] = value };
0335: this.start(Object.extend({ from: from, to: to }, options || { }));
0336: },
0337: update: function(position) {
0338: this.method(position);
0339: }
0340:});
0341:
Effect.
0342:Effect.Event = Class.create(Effect.Base, {
0343: initialize: function() {
0344: this.start(Object.extend({ duration: 0 }, arguments[0] || { }));
0345: },
0346: update: Prototype.emptyFunction
0347:});
0348:
Effect.
0349:Effect.Opacity = Class.create(Effect.Base, {
0350: initialize: function(element) {
0351: this.element = $(element);
0352: if (!this.element) throw(Effect._elementDoesNotExistError);
0353: // make this work on IE on elements without 'layout'
0354: if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
0355: this.element.setStyle({zoom: 1});
0356: var options = Object.extend({
0357: from: this.element.getOpacity() || 0.0,
0358: to: 1.0
0359: }, arguments[1] || { });
0360: this.start(options);
0361: },
0362: update: function(position) {
0363: this.element.setOpacity(position);
0364: }
0365:});
0366:
Effect.
0367:Effect.Move = Class.create(Effect.Base, {
0368: initialize: function(element) {
0369: this.element = $(element);
0370: if (!this.element) throw(Effect._elementDoesNotExistError);
0371: var options = Object.extend({
0372: x: 0,
0373: y: 0,
0374: mode: 'relative'
0375: }, arguments[1] || { });
0376: this.start(options);
0377: },
0378: setup: function() {
0379: this.element.makePositioned();
0380: this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
0381: this.originalTop = parseFloat(this.element.getStyle('top') || '0');
0382: if (this.options.mode == 'absolute') {
0383: this.options.x = this.options.x - this.originalLeft;
0384: this.options.y = this.options.y - this.originalTop;
0385: }
0386: },
0387: update: function(position) {
0388: this.element.setStyle({
0389: left: (this.options.x * position + this.originalLeft).round() + 'px',
0390: top: (this.options.y * position + this.originalTop).round() + 'px'
0391: });
0392: }
0393:});
0394:
0395:// for backwards compatibility
0396:Effect.MoveBy = function(element, toTop, toLeft) {
0397: return new Effect.Move(element,
0398: Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
0399:};
0400:
Effect.
0401:Effect.Scale = Class.create(Effect.Base, {
0402: initialize: function(element, percent) {
0403: this.element = $(element);
0404: if (!this.element) throw(Effect._elementDoesNotExistError);
0405: var options = Object.extend({
0406: scaleX: true,
0407: scaleY: true,
0408: scaleContent: true,
0409: scaleFromCenter: false,
0410: scaleMode: 'box', // 'box' or 'contents' or { } with provided values
0411: scaleFrom: 100.0,
0412: scaleTo: percent
0413: }, arguments[2] || { });
0414: this.start(options);
0415: },
0416: setup: function() {
0417: this.restoreAfterFinish = this.options.restoreAfterFinish || false;
0418: this.elementPositioning = this.element.getStyle('position');
0419:
0420: this.originalStyle = { };
0421: ['top','left','width','height','fontSize'].each( function(k) {
0422: this.originalStyle[k] = this.element.style[k];
0423: }.bind(this));
0424:
0425: this.originalTop = this.element.offsetTop;
0426: this.originalLeft = this.element.offsetLeft;
0427:
0428: var fontSize = this.element.getStyle('font-size') || '100%';
0429: ['em','px','%','pt'].each( function(fontSizeType) {
0430: if (fontSize.indexOf(fontSizeType)>0) {
0431: this.fontSize = parseFloat(fontSize);
0432: this.fontSizeType = fontSizeType;
0433: }
0434: }.bind(this));
0435:
0436: this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
0437:
0438: this.dims = null;
0439: if (this.options.scaleMode=='box')
0440: this.dims = [this.element.offsetHeight, this.element.offsetWidth];
0441: if (/^content/.test(this.options.scaleMode))
0442: this.dims = [this.element.scrollHeight, this.element.scrollWidth];
0443: if (!this.dims)
0444: this.dims = [this.options.scaleMode.originalHeight,
0445: this.options.scaleMode.originalWidth];
0446: },
0447: update: function(position) {
0448: var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
0449: if (this.options.scaleContent && this.fontSize)
0450: this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
0451: this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
0452: },
0453: finish: function(position) {
0454: if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
0455: },
0456: setDimensions: function(height, width) {
0457: var d = { };
0458: if (this.options.scaleX) d.width = width.round() + 'px';
0459: if (this.options.scaleY) d.height = height.round() + 'px';
0460: if (this.options.scaleFromCenter) {
0461: var topd = (height - this.dims[0])/2;
0462: var leftd = (width - this.dims[1])/2;
0463: if (this.elementPositioning == 'absolute') {
0464: if (this.options.scaleY) d.top = this.originalTop-topd + 'px';
0465: if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
0466: } else {
0467: if (this.options.scaleY) d.top = -topd + 'px';
0468: if (this.options.scaleX) d.left = -leftd + 'px';
0469: }
0470: }
0471: this.element.setStyle(d);
0472: }
0473:});
0474:
Effect.
0475:Effect.Highlight = Class.create(Effect.Base, {
0476: initialize: function(element) {
0477: this.element = $(element);
0478: if (!this.element) throw(Effect._elementDoesNotExistError);
0479: var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { });
0480: this.start(options);
0481: },
0482: setup: function() {
0483: // Prevent executing on elements not in the layout flow
0484: if (this.element.getStyle('display')=='none') { this.cancel(); return; }
0485: // Disable background image during the effect
0486: this.oldStyle = { };
0487: if (!this.options.keepBackgroundImage) {
0488: this.oldStyle.backgroundImage = this.element.getStyle('background-image');
0489: this.element.setStyle({backgroundImage: 'none'});
0490: }
0491: if (!this.options.endcolor)
0492: this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
0493: if (!this.options.restorecolor)
0494: this.options.restorecolor = this.element.getStyle('background-color');
0495: // init color calculations
0496: this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
0497: this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
0498: },
0499: update: function(position) {
0500: this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
0501: return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });
0502: },
0503: finish: function() {
0504: this.element.setStyle(Object.extend(this.oldStyle, {
0505: backgroundColor: this.options.restorecolor
0506: }));
0507: }
0508:});
0509:
Effect.
0510:Effect.ScrollTo = function(element) {
0511: var options = arguments[1] || { },
0512: scrollOffsets = document.viewport.getScrollOffsets(),
0513: elementOffsets = $(element).cumulativeOffset(),
0514: max = (window.height || document.body.scrollHeight) - document.viewport.getHeight();
0515:
0516: if (options.offset) elementOffsets[1] += options.offset;
0517:
0518: return new Effect.Tween(null,
0519: scrollOffsets.top,
0520: elementOffsets[1] > max ? max : elementOffsets[1],
0521: options,
0522: function(p){ scrollTo(scrollOffsets.left, p.round()) }
0523: );
0524:};
0525: