/* [INCLUDE FILE] sketchstar/animation_player_v2 */
/**
 * Animation frame state constants' object
 */
var AnimationFrameState ={

// animation frame states
STATE_NONE:0,
STATE_LOADING:1,
STATE_LOADED:2

};


/**
 * Frame object holding information about current
 * image location and state
 */
var AnimationFrame = Class.create({

/**
 * Class constructor
 *
 * @param string src       Image source location
 * @param integer duration Duration of the frame in miliseconds
 * @return AnimationFrame
 */
initialize:function(src,duration)
{
// current frame duration
this._duration = 100;

// current image load state
this._state = AnimationFrameState.STATE_NONE;

// set frame source url
this._src = src;

// reset HTMLImageElement
this._img = null;

if (duration){
// set frame duration
this._duration = parseInt(duration,10);
}
},

/**
 * Get current loading state of the frame image
 *
 * @return integer
 */
getState:function()
{
return this._state;
},

/**
 * Get duration of the frame
 *
 * @return integer
 */
getDuration:function()
{
return this._duration;
},

/**
 * Get image object
 *
 * @return HTMLImageElement
 */
getImage:function()
{
return this._img;
},

/**
 * Get unage location source
 *
 * @return string
 */
getImageSrc:function()
{
return this._src;
},

/**
 * Load image if not already lloaded or loading
 *
 * @param object options Callback options object{onStart:function(){},onComplete:function(){}}
 * @return void
 */
load:function(options)
{
if (!this._img){
if (options.onStart){
options.onStart();
}

this._state = AnimationFrameState.STATE_LOADING;

this._img = new Element('img',{
"src":this._src
});
this._img.setStyle({
"zIndex":1
});

this._img.onload = function(event){
this._state = AnimationFrameState.STATE_LOADED;

if (options.onComplete){
options.onComplete();
}
}.bind(this);
}
}

});


/**
 * Animation object holding frame objects and
 * information about animation dimensions
 */
var Animation = Class.create({

/**
 * Class constructor
 *
 * @param array frames Array of frames [optional]
 * @return Animation
 */
initialize:function(frames)
{
// list of animation frames
this._frames = [];
this._duration = null;

// animation dimension information
this._size ={
width:400,
height:300
};
if (Object.isArray(frames)){
for (var i = 0;i < frames.length;i++){
this._frames.push(frames[i]);
}
}

return this;
},

getDuration:function()
{   
        if(this._duration == null){
            this._duration = 0;
            for (var i = 0;i < this._frames.length;i++){
                this._duration += this._frames[i].getDuration();
           }
       }       
return this._duration;
},

/**
 * Add frame to the current list of frames
 *
 * @param AnimationFrame frame Frame object to add
 * @return Animation
 */
addFrame:function(url,duration)
{
var frame = new AnimationFrame(url,duration);
this._frames.push(frame);
return this;
},

/**
 * Get list of current animation frames
 *
 * @return array
 */
getFrames:function()
{
return this._frames;
},


loadFromUrl:function(url)
{
var ajax = new Ajax.Request(url,{
method:'post',
asynchronous:false,
onSuccess:function(r){
var frames = r.responseText.evalJSON();

if (Object.isArray(frames) && (frames.length > 0)){
for (var i = 0;i < frames.length;i++){
var frame = frames[i];
this.addFrame(frame.url,frame.duration);
}
}
}.bind(this)
});
},

/**
 * Set animation size (width and height)
 *
 * @param integer width  Width of animation frames
 * @param integer height Height of animation frames
 * @return Animation
 */
setSize:function(width,height)
{
this.setWidth(width);
this.setHeight(height);
return this;
},

/**
 * Set width of animation frames
 *
 * @param integer width  Width of animation frames
 */
setWidth:function(width)
{
this._size.width = parseInt(width,10);
return this;
},

/**
 * Get width of animation frames
 *
 * @return integer
 */
getWidth:function()
{
return this._size.width;
},

/**
 * Set height of animation frames
 *
 * @param integer height Height of animation frames
 */
setHeight:function(height)
{
this._size.height = parseInt(height,10);
return this;
},

/**
 * Get height of animation frames
 *
 * @return integer
 */
getHeight:function()
{
return this._size.height;
}

});


/**
 * Animation player state constants' object
 */
var AnimationPlayerState ={

// animation player state
STATE_STOPPED:0,
STATE_PLAYING:1,
STATE_PAUSED:2,
STATE_SEEKING:3

};


/**
 * Animation player used to playback Animation objects
 */
var AnimationPlayer = Class.create({

/**
 * Class constructor
 *
 * @param HTMLDivElement|string container Id or div itself to hold the player
 * @param object options                  Custom option override
 * @parem Animation animation             Animation object (with frames)
 * @return AnimationPlayer
 */
initialize:function(container,options,animation)
{
// main container div
this._container = null;

// find the animation target container
this._target_container = $(container);

// animation image (frame) container
this._animation_container = null;

// Animation object
this._animation = animation;

// information about animation frames
this._frames ={
"list":[],
"count":0,
"loaded":0,
"current":null
};

// current percentage of loaded animations
this._load_progress = 0;

// large center loading image options
this._loading ={
"img":null,
"width":0,
"height":0
};

// small corner loading image options
this._loading_small ={
"img":null,
"width":0,
"height":0
};

// Control.Slider object
this._slider = null;

// slider buffer div
this._slider_buffer = null;

// window timeout pointer
this._timeout = null;

// current player state
this._state = AnimationPlayerState.STATE_STOPPED;

// custom player options
this.options ={
"play":true,
"loop":false,
"slider":true,
"preload":false,
"loading":{
"large":{
"url":null
},
"small":{
"url":null,
"position":'topright'
}
},
"dataSource":null,
"onAnimationFinished":null,
"onAnimationLoaded":null,
"onFrameChanged":null,
"onSliderSlide":null,
"onSliderChange":null,
"onInitialized":null
};

// override options with user defined ones
Object.extend(this.options,options ||{});

// throw an exception if there are no frames in the animation
if (animation){
if (animation.getFrames().length === 0){
throw "Animation does not contain frames to play!";
}else{
this._initPlayer();
}
}else{
if (this.options.dataSource){
this._loadFromUrl(this.options.dataSource);
}else{
throw "Animation does not contain frames to play nor has the data source been defined";
}
}
},

/**
 * Initialize player after all required elements have been created
 *
 * @return void
 */
_initPlayer:function()
{
// create player container
this._initContainer();

// reset background image of the target container
this._target_container.setStyle({
"backgroundImage":"none"
});

// append player container to the target container
this._target_container.appendChild(this._container);

// save animation frames
this._frames.list = this._animation.getFrames();

// save frame count
this._frames.count = this._frames.list.length;

// create center loading div (if defined in options)
if (this.options.loading.large.url){
this._initLoading();
}

// create small corner loading div (if defined in options)
if (this.options.loading.small.url){
this._initLoadingSmall();
}

// create slider (if defined in options)
if (this.options.slider){
this._initSlider();
}

// execute user defined callback
if (this.options.onInitialized && Object.isFunction(this.options.onInitialized)){
this.options.onInitialized(this);
}

// preload images if defined in options
if (this.options.preload){
// preload all frames
this.preload();
}else{
// play if auto play is enabled
if (this.options.play){
// if no frames need to be preloaded
if (this.options.preload === false){
this.play();
}else{
// preload the required number of frames and play
this.preload();
}
}
}
},

/**
 * Load animation frames from
 */
_loadFromUrl:function(url)
{
var ajax = new Ajax.Request(url,{
method:'post',
asynchronous:true,
onSuccess:function(r){
var frames = r.responseText.evalJSON();

if (Object.isArray(frames) && (frames.length > 0)){
this._animation = new Animation();

frames.each(function(frame){
this._animation.addFrame(frame.url,frame.duration);
}.bind(this));

this._initPlayer();
}
}.bind(this)
});
},

/**
 * Initialize animation player container
 *
 * @return AnimationPlayer
 */
_initContainer:function()
{
// create another container inside the wrapper div
this._container = new Element('div',{
"class":'animation_player_container'
});

// set width and height of the inner container to the one of animation
this._container.setStyle({
"width":this._animation.getWidth() + 1 + 'px',
"position":'relative'
});

// create another container inside the wrapper div
this._animation_container = new Element('div',{
"class":'animation_container'
});
this._container.appendChild(this._animation_container);

// set width and height of the inner container to the one of animation
this._animation_container.setStyle({
"width":this._animation.getWidth() + 'px',
"height":this._animation.getHeight() + 'px',
"position":'relative'
});

// initialize slider container
this._slider_container = null;

return this;
},

/**
 * Initialize large center loading div
 *
 * @return AnimationPlayer
 */
_initLoading:function()
{
// extract the image source
var loading_img_src = this.options.loading.large.url;

// create the image element
this._loading.img = new Element('img',{
"src":loading_img_src
});

this._loading.img.onload = function(event){
// save dimension parameters
this._loading.width = this._loading.img.width;
this._loading.height = this._loading.img.height;

// center and hide the loading image
var top = (this._animation.getHeight() / 2) - (this._loading.height / 2);
var left = (this._animation.getWidth() / 2) - (this._loading.width / 2);

// set image style
this._loading.img.setStyle({
"display":'none',
"position":'absolute',
"top":top + 'px',
"left":left + 'px',
"zIndex":1000
});
this._animation_container.appendChild(this._loading.img);
}.bind(this);

return this;
},

/**
 * Initialize small corner loading div
 *
 * @return AnimationPlayer
 */
_initLoadingSmall:function()
{
var loading_small_image_src = this.options.loading.small.url;

this._loading_small.img = new Element('img',{
"src":loading_small_image_src
});

this._loading_small.img.onload = function(event){
this._loading_small.width = this._loading_small.img.width;
this._loading_small.height = this._loading_small.img.height;

var distance = 5;

this._loading_small.img.setStyle({
"display":'none',
"position":'absolute',
"zIndex":1000
});

// position the loading image
switch (this.options.loading.small.position){
case 'topleft':
this._loading_small.img.setStyle({
"top":distance + 'px',
"left":distance + 'px'
});
break;

case 'bottomleft':
this._loading_small.img.setStyle({
"top":this._animation.getHeight() - this._loading_small.height - distance + 'px',
"left":distance + 'px'
});
break;

case 'bottomright':
this._loading_small.img.setStyle({
"top":this._animation.getHeight() - this._loading_small.height - distance + 'px',
"right":distance + 'px'
});
break;

case 'center':
// center and hide the loading image
var top = (this._animation.getHeight() / 2) - (this._loading_small.height / 2);
var left = (this._animation.getWidth() / 2) - (this._loading_small.width / 2);

// set image style
this._loading_small.img.setStyle({
"top":parseInt(top) + 'px',
"left":parseInt(left) + 'px'
});
break;

case 'topright':
default:
this._loading_small.img.setStyle({
"top":distance + 'px',
"right":distance + 'px'
});
break;

}

this._container.appendChild(this._loading_small.img);
}.bind(this);

return this;
},

/**
 * Initialize slider element
 *
 * @return AnimationPlayer
 */
_initSlider:function()
{
// return if the slider has already been created
if (this._slider){
return;
}

// create the slider container
this._slider_container = new Element('div',{
"class":'animation_player_slider_container'
});
this._container.appendChild(this._slider_container);

// create the track div
var track = new Element('div',{
"class":'animation_player_slider_track'
});
this._slider_container.appendChild(track);

// create the slider buffer div
this._slider_buffer = new Element('div',{
"class":'animation_player_buffer'
});
track.appendChild(this._slider_buffer);

// create the slider div
var slider = new Element('div',{
"class":'animation_player_slider_slider'
});
track.appendChild(slider);

// calculate one percent in pixels
var one_perc = track.getWidth() / this._frames.count;

// instantiate the slider object
this._slider = new Control.Slider(slider,track,{
axis:'hodizontal',
range:$R(0,this._frames.count),
increment:one_perc,
alignX:0,
alignY:-2,
disabled:false
});

// define jump-to-position functionality
this._slider_buffer.onclick = function(event){
if (this._slider.disabled){
return false;
}

var e = event || window.event;

// get x position of pointer click
var x = e.layerX;

// get progress percentage of where the user clicked
var percentage = x / this._slider.trackLength;

// calculate index of the frame to display
var frame_index = parseInt(this.getAnimationFrameCount() * percentage,10);

// check how many frames in a row have been loaded
var first_frames_loaded = this.getStartingFramesLoaded();

// only allow clicks inside loaded frames area
if (frame_index <= first_frames_loaded){
this._slider.setValue(frame_index);
this._showFrame(frame_index);

// if the player was playing before then continue playing
if (this._state == AnimationPlayerState.STATE_PLAYING){
this.play();
}
}else{
// cancel the click
Event.stop(event);
return false;
}
}.bind(this);

// disable clicks on the trackbar
Event.stopObserving(this._slider.track,"mousedown",this._slider.eventMouseDown);

// update frame on dragging the slider
this._slider.options.onSlide = function(position){
// convert position to the integer
position = parseInt(position,10);

// return false if the position hasn't changed
if (position == this._frames.current){
return;
}

var starting_frames_loaded = this.getStartingFramesLoaded();
if (position > starting_frames_loaded){
position = starting_frames_loaded;
this._slider.setValue(position);
}

this._state = AnimationPlayerState.STATE_SEEKING;
this._showFrame(position);

if (this.options.onSliderSlide && Object.isFunction(this.options.onSliderSlide)){
this.options.onSliderSlide(position,this);
}
}.bind(this);

// execute user defined event handler
this._slider.options.onChange = function(position){
// convert position to the integer
position = parseInt(position,10);

// if custom even handler is defined
if (this.options.onSliderChange && Object.isFunction(this.options.onSliderChange)){
// execute user defined callback
this.options.onSliderChange(position,this);
}

if (this._slider.dragging && this.options.onSliderChangeManual && Object.isFunction(this.options.onSliderChangeManual)){
this.options.onSliderChangeManual(position,this);
}
}.bind(this);

return this;
},

/**
 * Preload all unloaded frames
 *
 * @return AnimationPlayer
 */
preload:function()
{
// show large center loading while preloading
this.showLoading();

// loop through all frames
this._frames.list.each(function(frame){
// check if the frame has not yet been loaded
if (frame.getState() == AnimationFrameState.STATE_NONE){
// start loading the frame
frame.load({
onComplete:function(){
// mark frame as loaded
this._onFrameLoaded();

// if all frames have been loaded
if (this.getFramesLoaded() == this.getAnimationFrameCount()){
// hide large center loading
this.hideLoading();

// start playing (if required)
if (this.options.play){
this.play();
}else{
this.stop();
}

// not all frames have been yet loaded
}else{
// if preload is number of frames
if (Object.isInteger(this.options.preload)){
// start playback if number of first frames loaded is the same or greater than the required number
if ((this.options.preload > 0) && (this.getStartingFramesLoaded() >= this.options.preload) && this.options.play){
this.play();
}

}else{
// if preload is percentage
if (Object.isPercentage(this.options.preload)){
var loadprogress = this.getStartingFramesLoadProgress();
var preload = Object.percentageToInt(this.options.preload);

// start playback if percentage of first frames loaded is the same or greater than the required amount
if ((preload > 0) && (loadprogress >= preload) && this.options.play){
this.play();
}
}
}
}
}.bind(this)
});
}
}.bind(this));

return this;
},

/**
 * Event happening when a frame has finished loading
 *
 * @return AnimationPlayer
 */
_onFrameLoaded:function()
{
// increment number of frames that have been loaded
this._frames.loaded++;

// set loading progress to the percentage of loaded animations
this._load_progress = parseInt(this._frames.loaded / this.getAnimationFrameCount() * 100,10);

// update slider buffer
this._updateSliderBufferProgress();

// if all frames have been loaded
if ((this._frames.loaded == this._frames.count)){
// hide all loading images
this.hideLoading();
this.hideLoadingSmall();

if (this._slider_buffer){
// flash the slider buffer
new Effect.Highlight(this._slider_buffer,{
startcolor:'#22FC11',
endcolor:'#A0A6FF',
duration:1.5
});
}

// execute user defined event handler
if (this.options.onAnimationLoaded && Object.isFunction(this.options.onAnimationLoaded)){
this.options.onAnimationLoaded(this);
}
}

return this;
},

/**
 * Update the width of the slider buffer
 *
 * @return AnimationPlayer
 */
_updateSliderBufferProgress:function()
{
// if the slider and buffer have been initialized
if (this._slider_buffer){
// calculate number of animations loaded in a row
var loaded = this.getStartingFramesLoaded();

// calculate buffer width percentage
var percentage = parseInt(loaded / this.getAnimationFrameCount() * 100,10);

// change width of the buffer
this._slider_buffer.setStyle({
"width":this.getLoadProgress() + '%'
});
}

return this;
},

/**
 * Load all frames that have not yet been loaded
 *
 * @return AnimationPlayer
 */
_loadPendingFrames:function()
{
// return if all frames have been loaded
if (this.getFramesLoaded() == this.getAnimationFrameCount()){
return false;
}

// loop through all frames
this._frames.list.each(function(frame){
// if the frame has not been loaded
if (frame.getState() == AnimationFrameState.STATE_NONE){
// load it
frame.load({
onComplete:function(){
// mark frame as loaded
this._onFrameLoaded();

// load the rest of pending frames
if (this.getFramesLoaded() < this.getAnimationFrameCount()){
this._loadPendingFrames();
}
}.bind(this)
});
}
}.bind(this));

return this;
},

/**
 * Show frame with the specified index
 *
 * @param integer index Index of the frame to display
 * @return AnimationPlayer|boolean
 */
_showFrame:function(index)
{
// return if trying to display the current frame
if (index == this._frames.current){
return false;
}

// get the AnimationFrame object
var frame = this._frames.list[index];

// if frame was found
if (frame){
if (!frame.getImage()){
return false;
}

// set current index to the new index
this._frames.current = index;

this.hideLoading();

// clear the container
this._animation_container.update();

// append frames HTMLImageElement to the container
this._animation_container.appendChild(frame.getImage());

// if slider exists,set it's position to the index
if (this._slider){
this._slider.setValue(index);
}

// execute user defined event handler
if (this.options.onFrameChanged && Object.isFunction(this.options.onFrameChanged)){
this.options.onFrameChanged(index,this);
}
}

return this;
},

/**
 * Show next frame in the queue
 *
 * @return AnimationPlayer|boolean
 */
_showNextFrame:function()
{
// clear the existing timeout
clearTimeout(this._timeout);

// show first frame if the playback is stopped or hasn't started
if (this._frames.current === null){
var next_index = 0;

// the the index of the next frame in queue
}else{
var next_index = this._frames.current + 1;

// if current frame is already the last frame
if (next_index >= this._frames.count){
// execute user defined event handler
if (this.options.onAnimationFinished && Object.isFunction(this.options.onAnimationFinished)){
this.options.onAnimationFinished(this);
}

// start from the first frame is looping is enabled
if (this.options.loop){
var next_index = 0;

// lopping is disabled
}else{
// set state to STOPPED
this._state = AnimationPlayerState.STATE_STOPPED;
return false;
}
}
}

// get frame to be played
var frame = this._frames.list[next_index];

// if frame was found
if (frame){
// if frame's HTMLImageElement is already loaded
if (frame.getState() == AnimationFrameState.STATE_LOADED){
// hide the corner loading
this.hideLoadingSmall();

// if animation is currently playing
if (this._state == AnimationPlayerState.STATE_PLAYING){
// display the specified frame
this._showFrame(next_index);

// queue the following frame
this._timeout = setTimeout(this._showNextFrame.bind(this),frame.getDuration());
}

// frame's HTMLImageElement has not yet been loaded
}else{
// still loading
if (frame.getState() == AnimationFrameState.STATE_LOADING){
// show corner loading
this.showLoadingSmall();

// retry after 10 miliseconds
setTimeout(this._showNextFrame.bind(this),10);

// frame has not even started loading
}else{
// show corner loading
this.showLoadingSmall();

// load the frame
frame.load({
onComplete:function(){
// mark frame as loaded
this._onFrameLoaded();

// display the frame if the animation is playing
if (this._state == AnimationPlayerState.STATE_PLAYING){
// display the specified frame
this._showFrame(next_index);

// queue the following frame
this._timeout = setTimeout(this._showNextFrame.bind(this),frame.getDuration());
}

// preload other pending frames if there are any left
if (this.getFramesLoaded() < this.getAnimationFrameCount()){
this._loadPendingFrames();
}
}.bind(this)
});
}
}
}

return this;
},

/**
 * Get the animation object
 *
 * @return Animation
 */
getAnimation:function()
{
return this._animation;
},

/**
 * Get current load progress (0 - 100)
 *
 * @return integer
 */
getLoadProgress:function()
{
return this._load_progress;
},

/**
 * Get total (non-sequential) number of frames loaded
 *
 * @return integer
 */
getFramesLoaded:function()
{
return this._frames.loaded;
},

/**
 * Get number of frames loaded from the start with no unloaded inbetween
 *
 * @return integer
 */
getStartingFramesLoaded:function()
{
// assume that none has been loaded
var loaded = 0;

// loop throught each frame and check the state
this._frames.list.each(function(frame){
if (frame.getState() == AnimationFrameState.STATE_LOADED){
loaded++;
}
});

return loaded;
},

getStartingFramesLoadProgress:function()
{
var total = this.getAnimationFrameCount();
var loaded = this.getStartingFramesLoaded();
return parseInt(loaded / total * 100,10);
},

/**
 * Get number of frames in the current animation
 *
 * @return integer
 */
getAnimationFrameCount:function()
{
return this._frames.count;
},

/**
 * Start playback
 *
 * @return AnimationPlayer
 */
play:function()
{
//return if the animation is already playing
if (this._state == AnimationPlayerState.STATE_PLAYING){
return false;
}

// start from the first frame if the playback is stopped
if (this._state == AnimationPlayerState.STATE_STOPPED){
this._frames.current = null;
}

// set state to PLAYING
this._state = AnimationPlayerState.STATE_PLAYING;

// display the next frame after the current one
this._showNextFrame();

return this;
},

/**
 * Pause playback
 *
 * @return AnimationPlayer
 */
pause:function()
{
// don't play the following frames
clearTimeout(this._timeout);

// set state to PAUSED
this._state = AnimationPlayerState.STATE_PAUSED;

// load pending frames while waiting
this._loadPendingFrames();

return this;
},

/**
 * Stop playback
 *
 * @return AnimationPlayer
 */
stop:function()
{
// don't play the following frames
clearTimeout(this._timeout);

// set state to STOPPED
this._state = AnimationPlayerState.STATE_STOPPED;

// load pending frames while waiting
this._loadPendingFrames();

// reset current frame
this._frames.current = null;

// go to the first frame
this._showFrame(0);

return this;
},

/**
 * Stop/resume the playback
 *
 * @return AnimationPlayer
 */
playPause:function()
{
// play if it's not already playing
if (this._state != AnimationPlayerState.STATE_PLAYING){
this.play();
}else{
this.pause();
}

return this;
},

/**
 * Show large center loading image
 *
 * @return AnimationPlayer
 */
showLoading:function()
{
// if the loading image exists
if (this._loading.img){
// hide the small corner loading image
this.hideLoadingSmall();

// display the center loading image
this._loading.img.style.display = 'block';
}

return this;
},


/**
 * Hide large center loading image
 *
 * @return AnimationPlayer
 */
hideLoading:function()
{
// if the loading image exists
if (this._loading.img){
// hide the large center loading image
this._loading.img.style.display = 'none';
}

return this;
},

/**
 * Show small corner loading image
 *
 * @return AnimationPlayer
 */
showLoadingSmall:function()
{
// if the loading image exists
if (this._loading_small.img){
// if the large center loading image is not currently being displayed
if (this._loading.img && (this._loading.img.style.display != 'block')){
// display the small corner loading image
this._loading_small.img.style.display = 'block';
}
}

return this;
},


/**
 * Hide small corner loading image
 *
 * @return AnimationPlayer
 */
hideLoadingSmall:function()
{
// if the loading image exists
if (this._loading_small.img){
// hide the small corner loading image
this._loading_small.img.style.display = 'none';
}

return this;
},

/**
 * Show the slider if it is initialized
 *
 * @return AnimationPlayer
 */
showSlider:function()
{
if (this._slider_container){
this._slider_container.style.display = 'block';
}
return this;
},

/**
 * Hide the slider if it is initialized
 *
 * @return AnimationPlayer
 */
hideSlider:function()
{
if (this._slider_container){
this._slider_container.style.display = 'none';
}
return this;
},

/**
 * Hide/show slider based on its current state
 *
 * @return AnimationPlayer
 */
toggleSlider:function()
{
if (this._slider_container.style.display == 'none'){
this.showSlider();
}else{
this.hideSlider();
}
return this;
},

enableSlider:function()
{
this._slider.setEnabled();
},

disableSlider:function()
{
this._slider.setDisabled();
},

toggleSliderEnabled:function()
{
if (this._slider.disabled){
this._slider.setEnabled();
}else{
this._slider.setDisabled();
}
},

/**
 * Get current player state
 *
 * @return integer
 */
getState:function()
{
return this._state;
},

/**
 *
 */
destroy:function()
{
try{
clearTimeout(this._timeout);
Element.remove(this._container);
}catch (e){
throw "Player already destroyed!";
}
}

});


Object.extend(Object,{
isInteger:function(object){
if (!Object.isUndefined(object)){
return (/^[0-9]+$/).test(object.toString());
}
return false;
},

isPercentage:function(object){
if (!Object.isUndefined(object)){
return (/^[0-9]+\%$/).test(object.toString());
}
return false;
},

percentageToInt:function(object){
return parseInt(object.toString().gsub('%',''),10);
}
});/* [INCLUDE FILE] sketchstar/top3panel_v2 */
var top3panel = Class.create({
initialize:function(src)
{
        this._isPng = null;
        this._durations = null;
        this._minDuration = 3000;
this._url = src;
this._animations = null;
this.profile = false;
this._idx = 0;
this._players = null;
this._animations = null;
this._connectorPos = new Array(0,105,210);
this._thumb1 = null;
this._thumb2 = null;
this._thumb3 = null;
this._uThumb1 = null;
this._uThumb2 = null;
this._uThumb3 = null;
this._milisecs = null;
this._timeout = null;
this._players = null;
this._options ={"play":true,"loop":true,"slider":false,"preload":false,
"loading":{
"large":{
"url":"/sketch-star/ss2/img/layout/animationPlayer/loadingAnimation.gif"
},
"small":{
"url":"/sketch-star/ss2/img/layout/animationPlayer/stars_loading_bw.gif",
"position":'center'
}
},
onAnimationFinished :function(player){          
                if(player._animation.getDuration() >= this._minDuration){
                    this.moveToNext().bind(this);
               }
}.bind(this)
};
this.load(this._url);
},
load :function(url)
{
var myAjax = new Ajax.Request(
url,{
method:'post',
onSuccess:function(r){
var json = r.responseText.evalJSON();
if (json.success){
this.onAnimationsload(json.result).bind(this);
}else{
alert('OOps! No animations to load');
}
}.bind(this),
onFailure:function(r){alert('Failure:' + r.responseText);}
});
},
onAnimationsload :function(list)
{
        //hide preloaded content if available
        preloadedContentExists = false;
        for(i = 1;i < 2;i++){
            if($('hide'+i) != undefined){
                $('hide'+i).remove();
                preloadedContentExists = true;
           }
       }
this._animations = new Array();
this._players = new Array();
this._isPng = new Array();
this._durations = new Array();
var i = 1;
list.each(function(l){
var animation = new Animation();
var isGif = false;
l.frames.each(function(f){
 animation.addFrame(f[0],f[1]);
 if(f[1]==0){
isGif = true;
 }
})
var container = $('container'+i);
if(isGif){
var gifPlayer = new Element('img',{
src :'/sketch-star/ss-lib/frame.php?id='+l.id+'&frame=65535'
});
container.appendChild(gifPlayer);
this._isPng.push(false);
this._players.push(null);
this._durations.push(this.timeToSecs(l.length));
}else{
var player = new AnimationPlayer(container,this._options,animation);
this._durations.push(animation.getDuration());
this._players.push(player);
this._isPng.push(true);
player.stop();
}
this._animations.push(l);
i++;
}.bind(this));
if(!preloadedContentExists){
            this.initDisplay();
}else{
            this.showAnimation();
}
},
initDisplay :function(){
for(i = 0;i < 3;i ++){
if (this._animations[i]){
/*
Editor's picks
*/
if($('ep1')!=null){
if(this._animations[i].editorsPick == 1){
var ep=$('ep'+(i+1));
ep.setStyle({display:'block'});
}else{
var ep=$('ep'+(i+1));
ep.setStyle({display:'none'});
}
}
/*Thumbnails*/
var thumb = "<a href='/sketch-star/en/player.php?id="+this._animations[i].id+"'><img class='thumb' id='thumb"+this._animations[i].id+"' src='/sketch-star/ss-lib/thumb.php?id="+this._animations[i].id+"' title='See &#145;"+this._animations[i].titleFull+"&#146;by "+this._animations[i].authorNameFull+"'></a>";
$('thumb'+(i+1)).update(thumb);
addThumbnailEventHandler($('thumb' + this._animations[i].id));
/*Titles*/
$('title'+(i+1)).update("<a href='/sketch-star/en/player.php?id="+this._animations[i].id+"' title='See &lsquo;"+this._animations[i].titleFull+"&rsquo;by "+this._animations[i].authorNameFull+"'>"+this._animations[i].title+"</a>");
if(!this.profile){
/*Author thumbnail*/
var uThumb = new Element('img',{
src :'/players/en/resize.php?uid=' + this._animations[i].authorId+'&w=16&h=16',
title :"Visit " + this._animations[i].authorNameFull+"'s profile"
});
uThumb.setStyle({cursor:'pointer'});
uThumb.onclick=function(){this.gotoProfile(this._animations[i].authorId).bind(this);}
$('authorThumb'+(i+1)).appendChild(uThumb);
/*Author name*/
$('authorName'+(i+1)).update('<a href="/sketch-star/en/profile.php?id='+this._animations[i].authorId+'">'+this._animations[i].authorNameFull+'</a>');
$('authorName'+(i+1)).title= "Visit "+this._animations[i].authorNameFull+"'s profile";
/*Times and votes*/
if ($('time'+(i+1))){
$('time'+(i+1)).update(this._animations[i].length.substr(3));
}
if ($('views'+(i+1))){
$('views'+(i+1)).update(this._animations[i].views);
}
if ($('date'+(i+1))){
$('date'+(i+1)).update(this._animations[i].finished);
}
if ($('votes'+(i+1))){
$('votes'+(i+1)).update(this._animations[i].votes);
}

}else{
/*Times and Finished*/
$('votes'+(i+1)).update(this._animations[i].votes);
$('views'+(i+1)).update(this._animations[i].views);
$('time'+(i+1)).update(this._animations[i].finished);
}
}
}
this.showAnimation();
},
showAnimation :function(){
clearTimeout(this._timeout);
if (this._animations[this._idx] != undefined){
var a = this._animations[this._idx];
}else{
var a = this._animations[0];
this._idx = 0;
}
for(var i=1;i<4;i++){
$('T3Pn'+(i)).className='';
}

$('T3Pn'+(this._idx+1)).className = "selected";
$('connector').setStyle({top :this._connectorPos[this._idx]+'px'});
for(i=1;i<4;i++){
$('container'+i).style.display = "none";
}
$('container'+(this._idx+1)).style.display = "block";
$('container'+(this._idx+1)).style.cursor = "pointer";
if(this._players[this._idx] != null){
            this._players[this._idx].stop();
            this._players[this._idx].play();
}
var link = this._animations[this._idx].id;
$('container'+(this._idx+1)).onclick = function(){this.gotoAnimation(link)}.bind(this);
if(!this._isPng[this._idx] || (this._durations[this._idx] < this._minDuration)){
            var milisecs = this.timeToSecs(a.length) * 1000;
            if (milisecs < this._minDuration){
                milisecs = this._minDuration;
           }
            this._timeout = setTimeout(function(){this.moveToNext()}.bind(this),milisecs);
}
},
timeToSecs:function(time)
{
var secs = 0;
var spl = time.split(':');
spl[0]=spl[0]*1;
spl[1]=spl[1]*1;
secs += (spl[0] * 60);
secs += spl[1] ;
return secs;
},
moveTo:function(num){
clearTimeout(this._timeout);
this._idx = num-1;
this.showAnimation();
return false;
},
moveToNext:function(){
clearTimeout(this._timeout);
this._idx ++;
this.showAnimation();
return false;
},
moveToPrev:function(){
clearTimeout(this._timeout);
this._idx --;
this.showAnimation();
return false;
},
setConnectorPos:function(posArray){
this._connectorPos = posArray;
},
gotoAnimation :function(id){
window.location="/sketch-star/en/player.php?id="+id;
},
gotoProfile :function(id){
window.location="/sketch-star/en/profile.php?id="+id;
}
})/* [INCLUDE FILE] oas/oas_gamepages */
//configuration
OAS_url ='http://ads.miniclip.com/RealMedia/ads/';
OAS_listpos = 'Bottom2,Middle';
OAS_sitepage = 'miniclip.com/gamepages';
//end of configuration
OAS_version = 10;
OAS_rn = '001234567890';OAS_rns = '1234567890';
OAS_rn = new String (Math.random());OAS_rns = OAS_rn.substring (2,11);
function OAS_NORMAL(pos){
document.write('<A HREF="' + OAS_url + 'click_nx.ads/' + OAS_sitepage + '/1' + OAS_rns + '@' + OAS_listpos + '!' + pos + OAS_query + '" TARGET=_top>');
document.write('<IMG SRC="' + OAS_url + 'adstream_nx.ads/' + OAS_sitepage + '/1' + OAS_rns + '@' + OAS_listpos + '!' + pos + OAS_query + '" BORDER=0 ALT="Click!"></A>');
}

OAS_version = 11;
if (navigator.userAgent.indexOf('Mozilla/3') != -1)
OAS_version = 10;
if (OAS_version >= 11)
document.write('<SC'+'RIPT LANGUAGE=JavaScript1.1 SRC="' + OAS_url + 'adstream_mjx.ads/' + OAS_sitepage + '/1' + OAS_rns + '@' + OAS_listpos + OAS_query + '"><\/SCRIPT>');

 document.write('');
function OAS_AD(pos){
if (OAS_version >= 11 && typeof(OAS_RICH) !='undefined')
  OAS_RICH(pos);
else
  OAS_NORMAL(pos);
}/* [INCLUDE FILE] sketchstar/swfobject */
/**
 * SWFObject v1.5:Flash Player detection and embed - http://blog.deconcept.com/swfobject/
 *
 * SWFObject is (c) 2007 Geoff Stearns and is released under the MIT License:
 * http://www.opensource.org/licenses/mit-license.php
 *
 */
if(typeof deconcept=="undefined"){var deconcept=new Object();}if(typeof deconcept.util=="undefined"){deconcept.util=new Object();}if(typeof deconcept.SWFObjectUtil=="undefined"){deconcept.SWFObjectUtil=new Object();}deconcept.SWFObject=function(_1,id,w,h,_5,c,_7,_8,_9,_a){if(!document.getElementById){return;}this.DETECT_KEY=_a?_a:"detectflash";this.skipDetect=deconcept.util.getRequestParameter(this.DETECT_KEY);this.params=new Object();this.variables=new Object();this.attributes=new Array();if(_1){this.setAttribute("swf",_1);}if(id){this.setAttribute("id",id);}if(w){this.setAttribute("width",w);}if(h){this.setAttribute("height",h);}if(_5){this.setAttribute("version",new deconcept.PlayerVersion(_5.toString().split(".")));}this.installedVer=deconcept.SWFObjectUtil.getPlayerVersion();if(!window.opera&&document.all&&this.installedVer.major>7){deconcept.SWFObject.doPrepUnload=true;}if(c){this.addParam("bgcolor",c);}var q=_7?_7:"high";this.addParam("quality",q);this.setAttribute("useExpressInstall",false);this.setAttribute("doExpressInstall",false);var _c=(_8)?_8:window.location;this.setAttribute("xiRedirectUrl",_c);this.setAttribute("redirectUrl","");if(_9){this.setAttribute("redirectUrl",_9);}};deconcept.SWFObject.prototype={useExpressInstall:function(_d){this.xiSWFPath=!_d?"expressinstall.swf":_d;this.setAttribute("useExpressInstall",true);},setAttribute:function(_e,_f){this.attributes[_e]=_f;},getAttribute:function(_10){return this.attributes[_10];},addParam:function(_11,_12){this.params[_11]=_12;},getParams:function(){return this.params;},addVariable:function(_13,_14){this.variables[_13]=_14;},getVariable:function(_15){return this.variables[_15];},getVariables:function(){return this.variables;},getVariablePairs:function(){var _16=new Array();var key;var _18=this.getVariables();for(key in _18){_16[_16.length]=key+"="+_18[key];}return _16;},getSWFHTML:function(){var _19="";if(navigator.plugins&&navigator.mimeTypes&&navigator.mimeTypes.length){if(this.getAttribute("doExpressInstall")){this.addVariable("MMplayerType","PlugIn");this.setAttribute("swf",this.xiSWFPath);}_19="<embed type=\"application/x-shockwave-flash\" src=\""+this.getAttribute("swf")+"\" width=\""+this.getAttribute("width")+"\" height=\""+this.getAttribute("height")+"\" style=\""+this.getAttribute("style")+"\"";_19+=" id=\""+this.getAttribute("id")+"\" name=\""+this.getAttribute("id")+"\" ";var _1a=this.getParams();for(var key in _1a){_19+=[key]+"=\""+_1a[key]+"\" ";}var _1c=this.getVariablePairs().join("&");if(_1c.length>0){_19+="flashvars=\""+_1c+"\"";}_19+="/>";}else{if(this.getAttribute("doExpressInstall")){this.addVariable("MMplayerType","ActiveX");this.setAttribute("swf",this.xiSWFPath);}_19="<object id=\""+this.getAttribute("id")+"\" classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\" width=\""+this.getAttribute("width")+"\" height=\""+this.getAttribute("height")+"\" style=\""+this.getAttribute("style")+"\">";_19+="<param name=\"movie\" value=\""+this.getAttribute("swf")+"\" />";var _1d=this.getParams();for(var key in _1d){_19+="<param name=\""+key+"\" value=\""+_1d[key]+"\" />";}var _1f=this.getVariablePairs().join("&");if(_1f.length>0){_19+="<param name=\"flashvars\" value=\""+_1f+"\" />";}_19+="</object>";}return _19;},write:function(_20){if(this.getAttribute("useExpressInstall")){var _21=new deconcept.PlayerVersion([6,0,65]);if(this.installedVer.versionIsValid(_21)&&!this.installedVer.versionIsValid(this.getAttribute("version"))){this.setAttribute("doExpressInstall",true);this.addVariable("MMredirectURL",escape(this.getAttribute("xiRedirectUrl")));document.title=document.title.slice(0,47)+" - Flash Player Installation";this.addVariable("MMdoctitle",document.title);}}if(this.skipDetect||this.getAttribute("doExpressInstall")||this.installedVer.versionIsValid(this.getAttribute("version"))){var n=(typeof _20=="string")?document.getElementById(_20):_20;n.innerHTML=this.getSWFHTML();return true;}else{if(this.getAttribute("redirectUrl")!=""){document.location.replace(this.getAttribute("redirectUrl"));}}return false;}};deconcept.SWFObjectUtil.getPlayerVersion=function(){var _23=new deconcept.PlayerVersion([0,0,0]);if(navigator.plugins&&navigator.mimeTypes.length){var x=navigator.plugins["Shockwave Flash"];if(x&&x.description){_23=new deconcept.PlayerVersion(x.description.replace(/([a-zA-Z]|\s)+/,"").replace(/(\s+r|\s+b[0-9]+)/,".").split("."));}}else{if(navigator.userAgent&&navigator.userAgent.indexOf("Windows CE")>=0){var axo=1;var _26=3;while(axo){try{_26++;axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash."+_26);_23=new deconcept.PlayerVersion([_26,0,0]);}catch(e){axo=null;}}}else{try{var axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");}catch(e){try{var axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");_23=new deconcept.PlayerVersion([6,0,21]);axo.AllowScriptAccess="always";}catch(e){if(_23.major==6){return _23;}}try{axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash");}catch(e){}}if(axo!=null){_23=new deconcept.PlayerVersion(axo.GetVariable("$version").split(" ")[1].split(","));}}}return _23;};deconcept.PlayerVersion=function(_29){this.major=_29[0]!=null?parseInt(_29[0]):0;this.minor=_29[1]!=null?parseInt(_29[1]):0;this.rev=_29[2]!=null?parseInt(_29[2]):0;};deconcept.PlayerVersion.prototype.versionIsValid=function(fv){if(this.major<fv.major){return false;}if(this.major>fv.major){return true;}if(this.minor<fv.minor){return false;}if(this.minor>fv.minor){return true;}if(this.rev<fv.rev){return false;}return true;};deconcept.util={getRequestParameter:function(_2b){var q=document.location.search||document.location.hash;if(_2b==null){return q;}if(q){var _2d=q.substring(1).split("&");for(var i=0;i<_2d.length;i++){if(_2d[i].substring(0,_2d[i].indexOf("="))==_2b){return _2d[i].substring((_2d[i].indexOf("=")+1));}}}return "";}};deconcept.SWFObjectUtil.cleanupSWFs=function(){var _2f=document.getElementsByTagName("OBJECT");for(var i=_2f.length-1;i>=0;i--){_2f[i].style.display="none";for(var x in _2f[i]){if(typeof _2f[i][x]=="function"){_2f[i][x]=function(){};}}}};if(deconcept.SWFObject.doPrepUnload){if(!deconcept.unloadSet){deconcept.SWFObjectUtil.prepUnload=function(){__flash_unloadHandler=function(){};__flash_savedUnloadHandler=function(){};window.attachEvent("onunload",deconcept.SWFObjectUtil.cleanupSWFs);};window.attachEvent("onbeforeunload",deconcept.SWFObjectUtil.prepUnload);deconcept.unloadSet=true;}}if(!document.getElementById&&document.all){document.getElementById=function(id){return document.all[id];};}var getQueryParamValue=deconcept.util.getRequestParameter;var FlashObject=deconcept.SWFObject;var SWFObject=deconcept.SWFObject;/* [INCLUDE FILE] sketchstar/validation */
function validate_required(field,alerttxt){
with (field){
if (value==null||value==""){
alert(alerttxt);return false;
  }else{
return true;
}
}
}

function validate_form(thisform){
with (thisform){
if (validate_required(query,"Your search is empty!")==false){
query.focus();
}else{
with (query){
window.location = '/sketch-star/en/search/' + urlencode(value);
}
}
return false;
}
}

function urlencode(str){
    str = (str+'').toString();
    return encodeURIComponent(str).replace(/!/g,'%21').replace(/'/g,'%27').replace(/\(/g,'%28').replace(/\)/g,'%29').replace(/\*/g,'%2A').replace(/%20/g,'+');
}/* [INCLUDE FILE] sketchstar/basicEffects */
function FadeEffect(element,time){
       new Effect.Fade(element,
      {duration:time});
}

function ShowEffect(element,time){
new Effect.Appear(element,
{duration:time,from:0,to:1.0});
}

function ShowSemiEffect(element,time){
new Effect.Appear(element,
{duration:time,from:0,to:.8})
}

function updateElement(element,newContent){
$(element).replace(newContent);
}


function FadeOutPlayBtn(id){
var div='hover'+id;
$(div).hide();

}

function FadeInPlayBtn(id){
var div='hover'+id;
$(div).show();
}/* [INCLUDE FILE] sketchstar/animationFunctions */
/**
 * Setup Event handlers
 * 
 * All Sketch Star events should be defined here in future!
 * 
 * @author Phil Bayfield
 */
Event.observe(window,'load',function(){

/**
 * Thumbnail hover
 */
$$('.thumb').each(function(elem){
if (elem.id != ''){
addThumbnailEventHandler(elem,'animated');
}
});

/**
 * Topic thumbnail hover
 */
$$('.topicthumb').each(function(elem){
if (elem.id != ''){
addThumbnailEventHandler(elem,'topicanimated');
}
});

/**
 * Vote for animation
 */
if ($('addVote') !== null){
$('addVote').onclick = addVoteEvent;
}

/**
 * Add favorite animation
 */
if ($('addFavoriteAnimation') !== null){
$('addFavoriteAnimation').onclick = addFavoriteAnimationEvent;
}

/**
 * Download animation
 */
if ($('downloadAnimation') !== null){
$('downloadAnimation').onclick = function(event){
// Update the button
$('downloadText').update('<span class="disabled">Downloading...</span>');
}
}

/**
 * Clear search inputs on focus 
 */
$$('.query').each(function(elem){
var inputTitle = elem.value;
elem.onfocus = function(event){
// if default value,clear it
if (elem.value == inputTitle) elem.clear()
};
elem.onblur = function(event){
// if the user didn't enter anything,return default value
if (elem.value == '') elem.value = inputTitle;
};
});

/**
 * Seach forum submission events
 */
$$('.searchForm').each(function(elem){
elem.onsubmit = function(event){
var valid = false;
var value = '';
var path  = '';
elem.getInputs('text').each(function(input){
if (valid == false && input.value != '' && input.value != 'Any' && input.value != 'Search for Animations...'){
valid = true;
value = input.value;
path  = input.id;
}
});
if (valid){
if (path != ''){
path += '/';
}
window.location = '/sketch-star/en/search/' + path + urlencode(value);
}else{
alert('Please enter a valid search term!');
}
// Stop the event
event = event || window.event;// Fix IE7
Event.stop(event);
};
});

/**
 * Add overlay confirmation fade in to buttons
 */
$$('.showOverlay').each(function(elem){
elem.onclick = function(event){
// Fade out any existing overlays
$$('.showOverlay').each(function(helem){
if (helem.rel != elem.rel){
new Effect.Fade($('overlay' + helem.rel),{
duration:0
});
}
});
// Fade in overlay on current element
new Effect.Appear($('overlay' + elem.rel),{
duration:0.8,
from:0,
to:0.8
});
// Stop the event
event = event || window.event;// Fix IE7
Event.stop(event);
};
});

/**
 * Hide overlay confirmation box
 */
$$('.hideOverlay').each(function(elem){
elem.onclick = function(event){
// Fade out
new Effect.Fade($('overlay' + elem.rel),{
duration:0
});
// Stop the event
event = event || window.event;// Fix IE7
Event.stop(event);
};
});

/**
 * Remove favorite animation
 */
$$('.removeFavoriteAnimation').each(function(elem){
elem.onclick = function(event){
var id = elem.rel;
var options ={
method:'post',
parameters:{action:'RemoveFavoriteAnimation',favId:id},
onSuccess:function(response){
var res = response.responseText;
// Success
if (res == 1){
window.location.reload();
// Not a favorite
}else if(res == 0){
alert('This animation is not one of your favorites!');
// Not logged in
}else{
alert('You must be logged in to remove this animation from your favorites!');
}
// Fade out the overlay
new Effect.Fade($('overlay' + id),{
duration:0
});
},
onFailure:function(response){
        alert('An error occurred:' + response.statusText);
}
};
new Ajax.Request('/sketch-star/ss-lib/ajax.php',options);
// Stop the event
event = event || window.event;// Fix IE7
Event.stop(event);
};
});

/**
 * Add to topic
 */
$$('.addToTopic').each(function(elem){
elem.onclick = function(event){
var params = elem.rel;
var ids = params.split(':');
var options ={
method:'post',
parameters:{action:'AddToTopic',topic:ids[0],animation :ids[1]},
onSuccess:function(response){
var res = response.responseText;
res = eval('(' + res + ')');
if (res.success == true){
elem.addClassName('disabled');
elem.update('<span>Already in Topic</span>');
elem.onclick = function(event){
// Stop the event
event = event || window.event;// Fix IE7
Event.stop(event);
};
alert('You have successfully added your animation to the ' + res.topic.topic_name + ' topic.');
}else{
switch (res.error){
default:
case 1:
case 2:
case 3:
case 4:
alert('An error occurred,please try again.');
break;
case 5:
alert('This animation is already in the topic.');
case 6:
alert('You cannot add this animation to this topic.')
break;
}
}
},
onFailure:function(response){
alert('An error occurred:' + response.statusText);
}
};
new Ajax.Request('/sketch-star/ss-lib/ajax.php',options);
// Stop the event
event = event || window.event;// Fix IE7
Event.stop(event);
};
});

/**
 * Enter contest
 */
$$('.enterContest').each(function(elem){
elem.onclick = function(event){
var params = elem.rel;
var ids = params.split(':');
var options ={
method:'post',
parameters:{action:'EnterContest',contest:ids[0],animation :ids[1]},
onSuccess:function(response){
var res = response.responseText;
res = eval('(' + res + ')');
if (res.success == true){
elem.addClassName('disabled');
elem.update('<span>Already Entered</span>');
elem.onclick = function(event){
// Stop the event
event = event || window.event;// Fix IE7
Event.stop(event);
};
alert('You have successfully entered the ' + res.contest.contest_title + ' contest.');
}else{
switch (res.error){
default:
case 1:
case 2:
case 3:
case 4:
alert('An error occurred,please try again.');
break;
case 5:
alert('You have already entered this contest.');
case 6:
alert('You cannot enter this animation in this contest.')
break;
}
}
},
onFailure:function(response){
alert('An error occurred:' + response.statusText);
}
};
new Ajax.Request('/sketch-star/ss-lib/ajax.php',options);
// Stop the event
event = event || window.event;// Fix IE7
Event.stop(event);
};
});

/**
 * Remove favorite artist
 */
$$('.removeFavoriteArtist').each(function(elem){
elem.onclick = function(event){
var id = elem.rel;
var options ={
method:'post',
parameters:{action:'RemoveFavoriteArtist',favId:id},
onSuccess:function(response){
var res = response.responseText;
// Success
if (res == 1){
window.location.reload();
// Not a favorite
}else if(res == 0){
alert('This artist is not one of your favorites!');
// Not logged in
}else{
alert('You must be logged in to remove this artist from your favorites!');
}
// Fade out the overlay
new Effect.Fade($('overlay' + id),{
duration:0
});
},
onFailure:function(response){
alert('An error occurred:' + response.statusText);
}
};
new Ajax.Request('/sketch-star/ss-lib/ajax.php',options);
// Stop the event
event = event || window.event;// Fix IE7
Event.stop(event);
};
});

/**
 * Make EP (Admin)
 */
$$('.makeStaffPick').each(function(elem){
elem.onclick = function(event){
if (elem.hasClassName('disabled')){
return false;
}
elem.addClassName('disabled');
elem.removeClassName('makeStaffPick');
var id = elem.id.replace('ep_','');
var options ={
method:'post',
parameters:{action:'MakeEditorsPick',animationId:id},
onSuccess:function(response){
var res = response.responseText.evalJSON();
if (res.success == true){
elem.select('span span').first().update('Already editor\'s pick');
$('animationTitle').insert({before:'<img class="editorsPick" src="/sketch-star/ss2/img/layout/top3panel/epBadgeModuleSmall.gif" alt="Editor\'s Pick" title="Editor\'s Pick"/>'});
}else{
alert('Failed to mark as editors pick,please try again.');
elem.addClassName('makeStaffPick');
elem.removeClassName('disabled');
}
},
onFailure:function(response){
alert('An error occurred,please try again.');
}
};
new Ajax.Request('/sketch-star/ss-lib/ajax.php',options);
// Stop the event
event = event || window.event;// Fix IE7
Event.stop(event);
}
});

/**
 * Make COTC (Admin)
 */
$$('.makeCreamOfTheCrop').each(function(elem){
elem.onclick = function(event){
if (elem.hasClassName('disabled')){
return false;
}
elem.addClassName('disabled');
elem.removeClassName('makeCreamOfTheCrop');
var id = elem.id.replace('hof_','');
var options ={
method:'post',
parameters:{action:'MakeCreamOfTheCrop',animationId:id},
onSuccess:function(response){
var res = response.responseText.evalJSON();
if (res.success == true){
alert('The animation has been added to the Cream Of The Crop gallery.');
}else{
alert('Failed to add the animation to the Cream Of The Crop gallery,please try again.');
elem.addClassName('makeCreamOfTheCrop');
elem.removeClassName('disabled');
}
},
onFailure:function(response){
alert('An error occurred,please try again.');
}
};
new Ajax.Request('/sketch-star/ss-lib/ajax.php',options);
// Stop the event
event = event || window.event;// Fix IE7
Event.stop(event);
}
});

});


/**
 * Add thumbnail event handlers to an element
 * 
 * @param elem
 * @return void
 */
function addThumbnailEventHandler(elem,type){
if (type === undefined){
type = 'animated';
}
var id = elem.id.replace('thumb','');
if (checkNumeric(id)){
elem.observe('mouseover',function(){
elem.src = '/sketch-star/ss-lib/thumb.php?type=' + type + '&id=' + id;
});
elem.observe('mouseout',function(){
elem.src = '/sketch-star/ss-lib/thumb.php?id=' + id;
});
}
}

/**
 * Event handler function for voting
 * 
 * @param event
 * @return void
 */
function addVoteEvent(event){
var id = this.rel;
var origHTML = $('voteText').innerHTML;
$('voteText').update('<span class="disabled">voting...</span>');
var options ={
method:'post',
parameters:{action:'AddVote',animationId:id},
onSuccess:function(response){
var res = response.responseText;
// Ok response
if (res == 1){
$('voteMake').hide();
new Effect.Appear('voteThanks',{
duration:1
});
var votes = $('numVotes').innerHTML.replace(',','');
var newVotes = Number(votes) + 1;
        updateElement('numVotes',numberFormat(newVotes,0,'.',','));
        // Error occured
}else if (res == 2){
$('voteText').update(origHTML);
$('addFavoriteAnimation').onclick = addFavoriteAnimationEvent;
alert('An error occured,please try again!');
// Already fav response
}else if (res == 0){
$('voteText').update(origHTML);
$('addFavoriteAnimation').onclick = addFavoriteAnimationEvent;
alert('You have already voted for this animation!');
// Otherwise not logged in
}else{
$('voteText').update(origHTML);
$('addVote').onclick = addFavoriteAnimationEvent;
alert('You must be logged in to vote for this animation!');
}
},
    onFailure:function(response){
    $('voteText').update(origHTML);
    $('addVote').onclick = addVoteEvent;
        alert('An error occurred:' + response.statusText);
    }
};
new Ajax.Request('/sketch-star/ss-lib/ajax.php',options);
// Stop event
event = event || window.event;// Fix IE7
Event.stop(event);
}

/**
 * Event handler function for add favorite animation
 * 
 * @param event
 * @return void
 */
function addFavoriteAnimationEvent(event){
var id = this.rel;
var origHTML = $('favoriteText').innerHTML;
$('favoriteText').update('<span class="disabled">loving...</span>');
var options ={
method:'post',
parameters:{action:'AddFavoriteAnimation',favId:id},
onSuccess:function(response){
var res = response.responseText;
// Ok response
if (res == 1){
$('favoriteMake').hide();
new Effect.Appear('favoriteThanks',{
duration:1
});
var favs = $('numFavs').innerHTML.replace(',','');
var newFavs = Number(favs) + 1;
        updateElement('numFavs',numberFormat(newFavs,0,'.',','));
$('favRow').show();
// Already fav response
}else if(res == 0){
$('favoriteText').update(origHTML);
$('addFavoriteAnimation').onclick = addFavoriteAnimationEvent;
alert('You have already added this animation to your favorites!');
// Otherwise not logged in
}else{
$('favoriteText').update(origHTML);
$('addFavoriteAnimation').onclick = addFavoriteAnimationEvent;
alert('You must be logged in to add this animation to your favorites!');
}
    },
    onFailure:function(response){
    $('favoriteText').update(origHTML);
    $('addFavoriteAnimation').onclick = addFavoriteAnimationEvent;
        alert('An error occurred:' + response.statusText);
    }
};
new Ajax.Request('/sketch-star/ss-lib/ajax.php',options);
// Stop event
event = event || window.event;// Fix IE7
Event.stop(event);
}

/**
 * Check a value is numeric
 * 
 * @param mixed value
 * @return bool
 */
function checkNumeric (value){
    return (typeof(value) === 'number' || typeof(value) === 'string') && value !== '' && !isNaN(value);
}

/**
 * **********************
 * Legacy functions below
 * **********************
 */
function FadeEffect(element,time){
       new Effect.Fade(element,
      {duration:time});
}

function ShowEffect(element,time){
new Effect.Appear(element,
{duration:time,from:0,to:1.0});
}

function ShowSemiEffect(element,time){
new Effect.Appear(element,
{duration:time,from:0,to:.8})
}

function updateElement(element,newContent){
$(element).replace(newContent);
}


function FadeOutPlayBtn(id){
var div='hover'+id;
$(div).hide();

}

function FadeInPlayBtn(id){
var div='hover'+id;
$(div).show();
}

function addCommas(nStr)
{
nStr += '';
x = nStr.split('.');
x1 = x[0];
x2 = x.length > 1 ? '.' + x[1] :'';
var rgx = /(\d+)(\d{3})/;
while (rgx.test(x1)){
x1 = x1.replace(rgx,'$1' + ',' + '$2');
}
return x1 + x2;
}

var lastId=1;
var lastFavId=1;

function reportConfirmation(id){
var answer = confirm("This animation will be reported to moderators as unacceptable. Are you sure you want to do this?")
if (answer){
report(id);
}
}

function report(id){
FadeEffect('report',0);
var options ={
method:"post",
    parameters:"id="+id,
onSuccess:function (oXHR,oJson){
    },
    onFailure:function (oXHR,oJson){
        alert("An error occurred:" + oXHR.statusText);
    }
};
var oRequest = new Ajax.Request("/sketch-star/ss2/scripts/report.php?id="+id,options);
}

function deleteAnimation(id){
    var options ={
method:"post",
    parameters:"id="+id,
onSuccess:function (oXHR,oJson){
            //alert("returns:" + oXHR.statusText);
            window.location.reload();
    },
    onFailure:function (oXHR,oJson){
        alert("An error occurred:" + oXHR.statusText);
    }
};
var oRequest = new Ajax.Request("/sketch-star/ss2/scripts/delete.php?id="+id,options);
}

function vote(id){
$('voteText').update('<span class="disabled">voting...</span>');
var options ={
method:"post",
    parameters:"id="+id,
onSuccess:function (oXHR,oJson){
            FadeEffect('voteMake',0);
        ShowEffect('voteThanks',1);
            var votes= $('numVotes').innerHTML.replace(",","");
var newVotes = Number(votes)+1;
        updateElement('numVotes',addCommas(newVotes));
    },
    onFailure:function (oXHR,oJson){
        alert("An error occurred:" + oXHR.statusText);
    }
};

var oRequest = new Ajax.Request("/sketch-star/ss2/scripts/vote.php?id="+id,options);
}

function addRemoveFan(id){
var span = $('addRemoveButton').select('span').first();
if (span.className == 'add'){
var action = 'AddFavoriteArtist';
}else{
var action = 'RemoveFavoriteArtist';
}
var options ={
method:'post',
parameters:{action:action,favId:id},
onSuccess:function(response){
var res = response.responseText;
if (res == 1){
var fancount = $('fanCount');
var countTxt = fancount.innerHTML.replace(/[^0-9]/,'');
if (span.className == 'add'){
span.className = 'remove';
span.update('Remove from Favorites');
fancount.innerHTML = numberFormat(parseInt(countTxt) + 1,0,'.',',');
}else{
span.className = 'add';
span.update('Become a Fan');
fancount.innerHTML = numberFormat(parseInt(countTxt) - 1,0,'.',',');
}
}else if(res == 0){
alert('You are already a fan of this artist!');
}else{
alert('You must be logged in to become a fan of this artist!' + res);
}
},
onFailure:function(response){
alert('An error occurred:' + response.statusText);
}
};
new Ajax.Request('/sketch-star/ss-lib/ajax.php',options);
}

function numberFormat (number,decimals,dec_point,thousands_sep){
var n = number,prec = decimals;
    var toFixedFix = function (n,prec){
        var k = Math.pow(10,prec);       return (Math.round(n*k)/k).toString();
   };
    n = !isFinite(+n) ? 0 :+n;
    prec = !isFinite(+prec) ? 0 :Math.abs(prec);   var sep = (typeof thousands_sep === 'undefined') ? ',' :thousands_sep;
    var dec = (typeof dec_point === 'undefined') ? '.' :dec_point;
    var s = (prec > 0) ? toFixedFix(n,prec) :toFixedFix(Math.round(n),prec);
    var abs = toFixedFix(Math.abs(n),prec);
    var _,i;
    if (abs >= 1000){
        _ = abs.split(/\D/);       i = _[0].length % 3 || 3;
        _[0] = s.slice(0,i + (n < 0)) +
        _[0].slice(i).replace(/(\d{3})/g,sep+'$1');
        s = _.join(dec);
}else{
        s = s.replace('.',dec);
   }
    var decPos = s.indexOf(dec);   if (prec >= 1 && decPos !== -1 && (s.length-decPos-1) < prec){
        s += new Array(prec-(s.length-decPos-1)).join(0)+'0';
   }
    else if (prec >= 1 && decPos === -1){
        s += dec+new Array(prec).join(0)+'0';
}
    return s;
}

function onFinish(obj){
ShowEffect('numVotes',1);
}

function alertLogin(){
    alert("You must be logged in to do that");
}

var is_open=false;
var open_share=false;

function showShareButton(){
    if(open_share){
        open_share=false;
FadeEffect('sharePanel',0);
FadeEffect('shareButton',0);
ShowEffect('shareButtonOpen',0);
}else{
ShowEffect('sharePanel',0);
ShowEffect('shareButton',0);
FadeEffect('shareButtonOpen',0);
open_share=true;
}
}

function showSFPanel(){
    ShowEffect('sendPanel',0);
}

function showSFButton(){
if(open){
is_open=false;
FadeEffect('sendPanel',0);
FadeEffect('sendButton',0);
ShowEffect('sendButtonOpen',0);
}else{
ShowEffect('sendPanel',0);
ShowEffect('sendButton',0);
FadeEffect('sendButtonOpen',0);
is_open=true;
}
}

function go_to_upgrade(){
var answer = confirm("This feature is only available to members. Click 'OK' to find out more about Sketch Star memberships.")
if (answer){
window.location = "/sketch-star/en/upgrade.php";
return false;
}
return false;
}

/* TOP NAV - Highlight current section link */
function highlightCurrentLink(link){
    var link = 'http://' + window.location.host + link;
    var a = document.getElementsByTagName("A");   
    for(var i = 0;i < a.length;i++){
        if(a[i].href == link){
    a[i].className = a[i].className + " current";
}
   }
}

/* MY ANIMATIONS - Functions to toggle help text on headings */
function showHelp(divName){
ShowEffect(divName+'Exp',0);
$(divName+'Back').setStyle(
{backgroundImage:'url(/sketch-star/ss2/img/layout/longPanelWB.png)'}
);
$(divName+'Button').className="dummieClass";
$(divName+'Button').title="Hide Explanation";
$(divName+'Button').src='/sketch-star/ss2/img/layout/collapseIcon.gif';
}

function hideHelp(divName){
FadeEffect(divName+'Exp',0);
$(divName+'Back').setStyle(
{backgroundImage:'url(/sketch-star/ss2/img/layout/longPanel.png)'}
);
$(divName+'Button').title=$(divName+'Button').alt;
$(divName+'Button').className="";
$(divName+'Button').src='/sketch-star/ss2/img/layout/helpIcon.gif';
}

function toggleHelp(divName){
if($(divName+'Button').className==""){
showHelp(divName);
}else{
hideHelp(divName);
}
}

//preload
var preloadImg=new Element('img',{
src :'/sketch-star/ss2/img/layout/longPanelWB.png'
});
var preloadButtImg=new Element('img',{
src :'/sketch-star/ss2/img/layout/collapseIcon.gif'
});/* [INCLUDE FILE] sketchstar/effects */
// script.aculo.us effects.js v1.8.1,Thu Jan 03 22:07:12 -0500 2008

// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us,http://mir.aculo.us)
// Contributors:
//  Justin Palmer (http://encytemedia.com/)
//  Mark Pilgrim (http://diveintomark.org/)
//  Martin Bialasinki
// 
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details,see the script.aculo.us web site:http://script.aculo.us/ 

// converts rgb() and #xxx to #xxxxxx format, 
// returns self (or first argument) if not convertable  
String.prototype.parseColor = function(){ 
  var color = '#';
  if (this.slice(0,4) == 'rgb('){ 
    var cols = this.slice(4,this.length-1).split(','); 
    var i=0;do{color += parseInt(cols[i]).toColorPart()}while (++i<3); 
 }else{ 
    if (this.slice(0,1) == '#'){ 
      if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase(); 
      if (this.length==7) color = this.toLowerCase(); 
   } 
 } 
  return (color.length==7 ? color :(arguments[0] || this)); 
};

/*--------------------------------------------------------------------------*/

Element.collectTextNodes = function(element){ 
  return $A($(element).childNodes).collect(function(node){
    return (node.nodeType==3 ? node.nodeValue :
      (node.hasChildNodes() ? Element.collectTextNodes(node) :''));
 }).flatten().join('');
};

Element.collectTextNodesIgnoreClass = function(element,className){ 
  return $A($(element).childNodes).collect(function(node){
    return (node.nodeType==3 ? node.nodeValue :
      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? 
        Element.collectTextNodesIgnoreClass(node,className) :''));
 }).flatten().join('');
};

Element.setContentZoom = function(element,percent){
  element = $(element); 
  element.setStyle({fontSize:(percent/100) + 'em'});  
  if (Prototype.Browser.WebKit) window.scrollBy(0,0);
  return element;
};

Element.getInlineOpacity = function(element){
  return $(element).style.opacity || '';
};

Element.forceRerendering = function(element){
  try{
    element = $(element);
    var n = document.createTextNode(' ');
    element.appendChild(n);
    element.removeChild(n);
 }catch(e){}
};

/*--------------------------------------------------------------------------*/

var Effect ={
  _elementDoesNotExistError:{
    name:'ElementDoesNotExistError',
    message:'The specified DOM element does not exist,but is required for this effect to operate'
 },
  Transitions:{
    linear:Prototype.K,
    sinoidal:function(pos){
      return (-Math.cos(pos*Math.PI)/2) + 0.5;
   },
    reverse:function(pos){
      return 1-pos;
   },
    flicker:function(pos){
      var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
      return pos > 1 ? 1 :pos;
   },
    wobble:function(pos){
      return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
   },
    pulse:function(pos,pulses){
      pulses = pulses || 5;
      return (
        ((pos % (1/pulses)) * pulses).round() == 0 ? 
              ((pos * pulses * 2) - (pos * pulses * 2).floor()) :
          1 - ((pos * pulses * 2) - (pos * pulses * 2).floor())
       );
   },
    spring:function(pos){
      return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
   },
    none:function(pos){
      return 0;
   },
    full:function(pos){
      return 1;
   }
 },
  DefaultOptions:{
    duration:  1.0,  // seconds
    fps:       100,  // 100= assume 66fps max.
    sync:      false,// true for combining
    from:      0.0,
    to:        1.0,
    delay:     0.0,
    queue:     'parallel'
 },
  tagifyText:function(element){
    var tagifyStyle = 'position:relative';
    if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';
    
    element = $(element);
    $A(element.childNodes).each(function(child){
      if (child.nodeType==3){
        child.nodeValue.toArray().each(function(character){
          element.insertBefore(
            new Element('span',{style:tagifyStyle}).update(
              character == ' ' ? String.fromCharCode(160) :character),
              child);
       });
        Element.remove(child);
     }
   });
 },
  multiple:function(element,effect){
    var elements;
    if (((typeof element == 'object') || 
        Object.isFunction(element)) && 
       (element.length))
      elements = element;
    else
      elements = $(element).childNodes;
      
    var options = Object.extend({
      speed:0.1,
      delay:0.0
   },arguments[2] ||{});
    var masterDelay = options.delay;

    $A(elements).each(function(element,index){
      new effect(element,Object.extend(options,{delay:index * options.speed + masterDelay}));
   });
 },
  PAIRS:{
    'slide': ['SlideDown','SlideUp'],
    'blind': ['BlindDown','BlindUp'],
    'appear':['Appear','Fade']
 },
  toggle:function(element,effect){
    element = $(element);
    effect = (effect || 'appear').toLowerCase();
    var options = Object.extend({
      queue:{position:'end',scope:(element.id || 'global'),limit:1}
   },arguments[2] ||{});
    Effect[element.visible() ? 
      Effect.PAIRS[effect][1] :Effect.PAIRS[effect][0]](element,options);
 }
};

Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;

/* ------------- core effects ------------- */

Effect.ScopedQueue = Class.create(Enumerable,{
  initialize:function(){
    this.effects  = [];
    this.interval = null;   
 },
  _each:function(iterator){
    this.effects._each(iterator);
 },
  add:function(effect){
    var timestamp = new Date().getTime();
    
    var position = Object.isString(effect.options.queue) ? 
      effect.options.queue :effect.options.queue.position;
    
    switch(position){
      case 'front':
        // move unstarted effects after this effect  
        this.effects.findAll(function(e){return e.state=='idle'}).each(function(e){
            e.startOn  += effect.finishOn;
            e.finishOn += effect.finishOn;
         });
        break;
      case 'with-last':
        timestamp = this.effects.pluck('startOn').max() || timestamp;
        break;
      case 'end':
        // start effect after last queued effect has finished
        timestamp = this.effects.pluck('finishOn').max() || timestamp;
        break;
   }
    
    effect.startOn  += timestamp;
    effect.finishOn += timestamp;

    if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
      this.effects.push(effect);
    
    if (!this.interval)
      this.interval = setInterval(this.loop.bind(this),15);
 },
  remove:function(effect){
    this.effects = this.effects.reject(function(e){return e==effect});
    if (this.effects.length == 0){
      clearInterval(this.interval);
      this.interval = null;
   }
 },
  loop:function(){
    var timePos = new Date().getTime();
    for(var i=0,len=this.effects.length;i<len;i++) 
      this.effects[i] && this.effects[i].loop(timePos);
 }
});

Effect.Queues ={
  instances:$H(),
  get:function(queueName){
    if (!Object.isString(queueName)) return queueName;
    
    return this.instances.get(queueName) ||
      this.instances.set(queueName,new Effect.ScopedQueue());
 }
};
Effect.Queue = Effect.Queues.get('global');

Effect.Base = Class.create({
  position:null,
  start:function(options){
    function codeForEvent(options,eventName){
      return (
        (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' :'') +
        (options[eventName] ? 'this.options.'+eventName+'(this);' :'')
     );
   }
    if (options && options.transition === false) options.transition = Effect.Transitions.linear;
    this.options      = Object.extend(Object.extend({},Effect.DefaultOptions),options ||{});
    this.currentFrame = 0;
    this.state        = 'idle';
    this.startOn      = this.options.delay*1000;
    this.finishOn     = this.startOn+(this.options.duration*1000);
    this.fromToDelta  = this.options.to-this.options.from;
    this.totalTime    = this.finishOn-this.startOn;
    this.totalFrames  = this.options.fps*this.options.duration;
    
    eval('this.render = function(pos){'+
      'if (this.state=="idle"){this.state="running";'+
      codeForEvent(this.options,'beforeSetup')+
      (this.setup ? 'this.setup();':'')+ 
      codeForEvent(this.options,'afterSetup')+
      '};if (this.state=="running"){'+
      'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+
      'this.position=pos;'+
      codeForEvent(this.options,'beforeUpdate')+
      (this.update ? 'this.update(pos);':'')+
      codeForEvent(this.options,'afterUpdate')+
      '}}');
    
    this.event('beforeStart');
    if (!this.options.sync)
      Effect.Queues.get(Object.isString(this.options.queue) ? 
        'global' :this.options.queue.scope).add(this);
 },
  loop:function(timePos){
    if (timePos >= this.startOn){
      if (timePos >= this.finishOn){
        this.render(1.0);
        this.cancel();
        this.event('beforeFinish');
        if (this.finish) this.finish();
        this.event('afterFinish');
        return; 
     }
      var pos   = (timePos - this.startOn) / this.totalTime,
          frame = (pos * this.totalFrames).round();
      if (frame > this.currentFrame){
        this.render(pos);
        this.currentFrame = frame;
     }
   }
 },
  cancel:function(){
    if (!this.options.sync)
      Effect.Queues.get(Object.isString(this.options.queue) ? 
        'global' :this.options.queue.scope).remove(this);
    this.state = 'finished';
 },
  event:function(eventName){
    if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
    if (this.options[eventName]) this.options[eventName](this);
 },
  inspect:function(){
    var data = $H();
    for(property in this)
      if (!Object.isFunction(this[property])) data.set(property,this[property]);
    return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
 }
});

Effect.Parallel = Class.create(Effect.Base,{
  initialize:function(effects){
    this.effects = effects || [];
    this.start(arguments[1]);
 },
  update:function(position){
    this.effects.invoke('render',position);
 },
  finish:function(position){
    this.effects.each(function(effect){
      effect.render(1.0);
      effect.cancel();
      effect.event('beforeFinish');
      if (effect.finish) effect.finish(position);
      effect.event('afterFinish');
   });
 }
});

Effect.Tween = Class.create(Effect.Base,{
  initialize:function(object,from,to){
    object = Object.isString(object) ? $(object) :object;
    var args = $A(arguments),method = args.last(),
      options = args.length == 5 ? args[3] :null;
    this.method = Object.isFunction(method) ? method.bind(object) :
      Object.isFunction(object[method]) ? object[method].bind(object) :
      function(value){object[method] = value};
    this.start(Object.extend({from:from,to:to},options ||{}));
 },
  update:function(position){
    this.method(position);
 }
});

Effect.Event = Class.create(Effect.Base,{
  initialize:function(){
    this.start(Object.extend({duration:0},arguments[0] ||{}));
 },
  update:Prototype.emptyFunction
});

Effect.Opacity = Class.create(Effect.Base,{
  initialize:function(element){
    this.element = $(element);
    if (!this.element) throw(Effect._elementDoesNotExistError);
    // make this work on IE on elements without 'layout'
    if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
      this.element.setStyle({zoom:1});
    var options = Object.extend({
      from:this.element.getOpacity() || 0.0,
      to:  1.0
   },arguments[1] ||{});
    this.start(options);
 },
  update:function(position){
    this.element.setOpacity(position);
 }
});

Effect.Move = Class.create(Effect.Base,{
  initialize:function(element){
    this.element = $(element);
    if (!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({
      x:   0,
      y:   0,
      mode:'relative'
   },arguments[1] ||{});
    this.start(options);
 },
  setup:function(){
    this.element.makePositioned();
    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
    if (this.options.mode == 'absolute'){
      this.options.x = this.options.x - this.originalLeft;
      this.options.y = this.options.y - this.originalTop;
   }
 },
  update:function(position){
    this.element.setStyle({
      left:(this.options.x  * position + this.originalLeft).round() + 'px',
      top: (this.options.y  * position + this.originalTop).round()  + 'px'
   });
 }
});

// for backwards compatibility
Effect.MoveBy = function(element,toTop,toLeft){
  return new Effect.Move(element,
    Object.extend({x:toLeft,y:toTop},arguments[3] ||{}));
};

Effect.Scale = Class.create(Effect.Base,{
  initialize:function(element,percent){
    this.element = $(element);
    if (!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({
      scaleX:true,
      scaleY:true,
      scaleContent:true,
      scaleFromCenter:false,
      scaleMode:'box',       // 'box' or 'contents' or{}with provided values
      scaleFrom:100.0,
      scaleTo:  percent
   },arguments[2] ||{});
    this.start(options);
 },
  setup:function(){
    this.restoreAfterFinish = this.options.restoreAfterFinish || false;
    this.elementPositioning = this.element.getStyle('position');
    
    this.originalStyle ={};
    ['top','left','width','height','fontSize'].each(function(k){
      this.originalStyle[k] = this.element.style[k];
   }.bind(this));
      
    this.originalTop  = this.element.offsetTop;
    this.originalLeft = this.element.offsetLeft;
    
    var fontSize = this.element.getStyle('font-size') || '100%';
    ['em','px','%','pt'].each(function(fontSizeType){
      if (fontSize.indexOf(fontSizeType)>0){
        this.fontSize     = parseFloat(fontSize);
        this.fontSizeType = fontSizeType;
     }
   }.bind(this));
    
    this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
    
    this.dims = null;
    if (this.options.scaleMode=='box')
      this.dims = [this.element.offsetHeight,this.element.offsetWidth];
    if (/^content/.test(this.options.scaleMode))
      this.dims = [this.element.scrollHeight,this.element.scrollWidth];
    if (!this.dims)
      this.dims = [this.options.scaleMode.originalHeight,
                   this.options.scaleMode.originalWidth];
 },
  update:function(position){
    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
    if (this.options.scaleContent && this.fontSize)
      this.element.setStyle({fontSize:this.fontSize * currentScale + this.fontSizeType});
    this.setDimensions(this.dims[0] * currentScale,this.dims[1] * currentScale);
 },
  finish:function(position){
    if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
 },
  setDimensions:function(height,width){
    var d ={};
    if (this.options.scaleX) d.width = width.round() + 'px';
    if (this.options.scaleY) d.height = height.round() + 'px';
    if (this.options.scaleFromCenter){
      var topd  = (height - this.dims[0])/2;
      var leftd = (width  - this.dims[1])/2;
      if (this.elementPositioning == 'absolute'){
        if (this.options.scaleY) d.top = this.originalTop-topd + 'px';
        if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
     }else{
        if (this.options.scaleY) d.top = -topd + 'px';
        if (this.options.scaleX) d.left = -leftd + 'px';
     }
   }
    this.element.setStyle(d);
 }
});

Effect.Highlight = Class.create(Effect.Base,{
  initialize:function(element){
    this.element = $(element);
    if (!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({startcolor:'#ffff99'},arguments[1] ||{});
    this.start(options);
 },
  setup:function(){
    // Prevent executing on elements not in the layout flow
    if (this.element.getStyle('display')=='none'){this.cancel();return;}
    // Disable background image during the effect
    this.oldStyle ={};
    if (!this.options.keepBackgroundImage){
      this.oldStyle.backgroundImage = this.element.getStyle('background-image');
      this.element.setStyle({backgroundImage:'none'});
   }
    if (!this.options.endcolor)
      this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
    if (!this.options.restorecolor)
      this.options.restorecolor = this.element.getStyle('background-color');
    // init color calculations
    this._base  = $R(0,2).map(function(i){return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16)}.bind(this));
    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));
 },
  update:function(position){
    this.element.setStyle({backgroundColor:$R(0,2).inject('#',function(m,v,i){
      return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart());}.bind(this))});
 },
  finish:function(){
    this.element.setStyle(Object.extend(this.oldStyle,{
      backgroundColor:this.options.restorecolor
   }));
 }
});

Effect.ScrollTo = function(element){
  var options = arguments[1] ||{},
    scrollOffsets = document.viewport.getScrollOffsets(),
    elementOffsets = $(element).cumulativeOffset(),
    max = (window.height || document.body.scrollHeight) - document.viewport.getHeight(); 

  if (options.offset) elementOffsets[1] += options.offset;

  return new Effect.Tween(null,
    scrollOffsets.top,
    elementOffsets[1] > max ? max :elementOffsets[1],
    options,
    function(p){scrollTo(scrollOffsets.left,p.round())}
 );
};

/* ------------- combination effects ------------- */

Effect.Fade = function(element){
  element = $(element);
  var oldOpacity = element.getInlineOpacity();
  var options = Object.extend({
    from:element.getOpacity() || 1.0,
    to:  0.0,
    afterFinishInternal:function(effect){
      if (effect.options.to!=0) return;
      effect.element.hide().setStyle({opacity:oldOpacity});
   }
 },arguments[1] ||{});
  return new Effect.Opacity(element,options);
};

Effect.Appear = function(element){
  element = $(element);
  var options = Object.extend({
  from:(element.getStyle('display') == 'none' ? 0.0 :element.getOpacity() || 0.0),
  to:  1.0,
  // force Safari to render floated elements properly
  afterFinishInternal:function(effect){
    effect.element.forceRerendering();
 },
  beforeSetup:function(effect){
    effect.element.setOpacity(effect.options.from).show();
 }},arguments[1] ||{});
  return new Effect.Opacity(element,options);
};

Effect.Puff = function(element){
  element = $(element);
  var oldStyle ={
    opacity:element.getInlineOpacity(),
    position:element.getStyle('position'),
    top: element.style.top,
    left:element.style.left,
    width:element.style.width,
    height:element.style.height
 };
  return new Effect.Parallel(
   [new Effect.Scale(element,200,
     {sync:true,scaleFromCenter:true,scaleContent:true,restoreAfterFinish:true}),
     new Effect.Opacity(element,{sync:true,to:0.0})],
     Object.extend({duration:1.0,
      beforeSetupInternal:function(effect){
        Position.absolutize(effect.effects[0].element)
     },
      afterFinishInternal:function(effect){
         effect.effects[0].element.hide().setStyle(oldStyle);}
    },arguments[1] ||{})
  );
};

Effect.BlindUp = function(element){
  element = $(element);
  element.makeClipping();
  return new Effect.Scale(element,0,
    Object.extend({scaleContent:false,
      scaleX:false,
      restoreAfterFinish:true,
      afterFinishInternal:function(effect){
        effect.element.hide().undoClipping();
     }
   },arguments[1] ||{})
 );
};

Effect.BlindDown = function(element){
  element = $(element);
  var elementDimensions = element.getDimensions();
  return new Effect.Scale(element,100,Object.extend({
    scaleContent:false,
    scaleX:false,
    scaleFrom:0,
    scaleMode:{originalHeight:elementDimensions.height,originalWidth:elementDimensions.width},
    restoreAfterFinish:true,
    afterSetup:function(effect){
      effect.element.makeClipping().setStyle({height:'0px'}).show();
   }, 
    afterFinishInternal:function(effect){
      effect.element.undoClipping();
   }
 },arguments[1] ||{}));
};

Effect.SwitchOff = function(element){
  element = $(element);
  var oldOpacity = element.getInlineOpacity();
  return new Effect.Appear(element,Object.extend({
    duration:0.4,
    from:0,
    transition:Effect.Transitions.flicker,
    afterFinishInternal:function(effect){
      new Effect.Scale(effect.element,1,{
        duration:0.3,scaleFromCenter:true,
        scaleX:false,scaleContent:false,restoreAfterFinish:true,
        beforeSetup:function(effect){
          effect.element.makePositioned().makeClipping();
       },
        afterFinishInternal:function(effect){
          effect.element.hide().undoClipping().undoPositioned().setStyle({opacity:oldOpacity});
       }
     })
   }
 },arguments[1] ||{}));
};

Effect.DropOut = function(element){
  element = $(element);
  var oldStyle ={
    top:element.getStyle('top'),
    left:element.getStyle('left'),
    opacity:element.getInlineOpacity()};
  return new Effect.Parallel(
    [new Effect.Move(element,{x:0,y:100,sync:true}),
      new Effect.Opacity(element,{sync:true,to:0.0})],
    Object.extend(
     {duration:0.5,
        beforeSetup:function(effect){
          effect.effects[0].element.makePositioned();
       },
        afterFinishInternal:function(effect){
          effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
       }
     },arguments[1] ||{}));
};

Effect.Shake = function(element){
  element = $(element);
  var options = Object.extend({
    distance:20,
    duration:0.5
 },arguments[1] ||{});
  var distance = parseFloat(options.distance);
  var split = parseFloat(options.duration) / 10.0;
  var oldStyle ={
    top:element.getStyle('top'),
    left:element.getStyle('left')};
    return new Effect.Move(element,
     {x: distance,y:0,duration:split,afterFinishInternal:function(effect){
    new Effect.Move(effect.element,
     {x:-distance*2,y:0,duration:split*2, afterFinishInternal:function(effect){
    new Effect.Move(effect.element,
     {x: distance*2,y:0,duration:split*2, afterFinishInternal:function(effect){
    new Effect.Move(effect.element,
     {x:-distance*2,y:0,duration:split*2, afterFinishInternal:function(effect){
    new Effect.Move(effect.element,
     {x: distance*2,y:0,duration:split*2, afterFinishInternal:function(effect){
    new Effect.Move(effect.element,
     {x:-distance,y:0,duration:split,afterFinishInternal:function(effect){
        effect.element.undoPositioned().setStyle(oldStyle);
 }})}})}})}})}})}});
};

Effect.SlideDown = function(element){
  element = $(element).cleanWhitespace();
  // SlideDown need to have the content of the element wrapped in a container element with fixed height!
  var oldInnerBottom = element.down().getStyle('bottom');
  var elementDimensions = element.getDimensions();
  return new Effect.Scale(element,100,Object.extend({
    scaleContent:false,
    scaleX:false,
    scaleFrom:window.opera ? 0 :1,
    scaleMode:{originalHeight:elementDimensions.height,originalWidth:elementDimensions.width},
    restoreAfterFinish:true,
    afterSetup:function(effect){
      effect.element.makePositioned();
      effect.element.down().makePositioned();
      if (window.opera) effect.element.setStyle({top:''});
      effect.element.makeClipping().setStyle({height:'0px'}).show();
   },
    afterUpdateInternal:function(effect){
      effect.element.down().setStyle({bottom:
        (effect.dims[0] - effect.element.clientHeight) + 'px'});
   },
    afterFinishInternal:function(effect){
      effect.element.undoClipping().undoPositioned();
      effect.element.down().undoPositioned().setStyle({bottom:oldInnerBottom});}
   },arguments[1] ||{})
 );
};

Effect.SlideUp = function(element){
  element = $(element).cleanWhitespace();
  var oldInnerBottom = element.down().getStyle('bottom');
  var elementDimensions = element.getDimensions();
  return new Effect.Scale(element,window.opera ? 0 :1,
   Object.extend({scaleContent:false,
    scaleX:false,
    scaleMode:'box',
    scaleFrom:100,
    scaleMode:{originalHeight:elementDimensions.height,originalWidth:elementDimensions.width},
    restoreAfterFinish:true,
    afterSetup:function(effect){
      effect.element.makePositioned();
      effect.element.down().makePositioned();
      if (window.opera) effect.element.setStyle({top:''});
      effect.element.makeClipping().show();
   }, 
    afterUpdateInternal:function(effect){
      effect.element.down().setStyle({bottom:
        (effect.dims[0] - effect.element.clientHeight) + 'px'});
   },
    afterFinishInternal:function(effect){
      effect.element.hide().undoClipping().undoPositioned();
      effect.element.down().undoPositioned().setStyle({bottom:oldInnerBottom});
   }
  },arguments[1] ||{})
 );
};

// Bug in opera makes the TD containing this element expand for a instance after finish 
Effect.Squish = function(element){
  return new Effect.Scale(element,window.opera ? 1 :0,{
    restoreAfterFinish:true,
    beforeSetup:function(effect){
      effect.element.makeClipping();
   }, 
    afterFinishInternal:function(effect){
      effect.element.hide().undoClipping();
   }
 });
};

Effect.Grow = function(element){
  element = $(element);
  var options = Object.extend({
    direction:'center',
    moveTransition:Effect.Transitions.sinoidal,
    scaleTransition:Effect.Transitions.sinoidal,
    opacityTransition:Effect.Transitions.full
 },arguments[1] ||{});
  var oldStyle ={
    top:element.style.top,
    left:element.style.left,
    height:element.style.height,
    width:element.style.width,
    opacity:element.getInlineOpacity()};

  var dims = element.getDimensions();   
  var initialMoveX,initialMoveY;
  var moveX,moveY;
  
  switch (options.direction){
    case 'top-left':
      initialMoveX = initialMoveY = moveX = moveY = 0;
      break;
    case 'top-right':
      initialMoveX = dims.width;
      initialMoveY = moveY = 0;
      moveX = -dims.width;
      break;
    case 'bottom-left':
      initialMoveX = moveX = 0;
      initialMoveY = dims.height;
      moveY = -dims.height;
      break;
    case 'bottom-right':
      initialMoveX = dims.width;
      initialMoveY = dims.height;
      moveX = -dims.width;
      moveY = -dims.height;
      break;
    case 'center':
      initialMoveX = dims.width / 2;
      initialMoveY = dims.height / 2;
      moveX = -dims.width / 2;
      moveY = -dims.height / 2;
      break;
 }
  
  return new Effect.Move(element,{
    x:initialMoveX,
    y:initialMoveY,
    duration:0.01,
    beforeSetup:function(effect){
      effect.element.hide().makeClipping().makePositioned();
   },
    afterFinishInternal:function(effect){
      new Effect.Parallel(
        [new Effect.Opacity(effect.element,{sync:true,to:1.0,from:0.0,transition:options.opacityTransition}),
          new Effect.Move(effect.element,{x:moveX,y:moveY,sync:true,transition:options.moveTransition}),
          new Effect.Scale(effect.element,100,{
            scaleMode:{originalHeight:dims.height,originalWidth:dims.width},
            sync:true,scaleFrom:window.opera ? 1 :0,transition:options.scaleTransition,restoreAfterFinish:true})
       ],Object.extend({
             beforeSetup:function(effect){
               effect.effects[0].element.setStyle({height:'0px'}).show();
            },
             afterFinishInternal:function(effect){
               effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);
            }
          },options)
     )
   }
 });
};

Effect.Shrink = function(element){
  element = $(element);
  var options = Object.extend({
    direction:'center',
    moveTransition:Effect.Transitions.sinoidal,
    scaleTransition:Effect.Transitions.sinoidal,
    opacityTransition:Effect.Transitions.none
 },arguments[1] ||{});
  var oldStyle ={
    top:element.style.top,
    left:element.style.left,
    height:element.style.height,
    width:element.style.width,
    opacity:element.getInlineOpacity()};

  var dims = element.getDimensions();
  var moveX,moveY;
  
  switch (options.direction){
    case 'top-left':
      moveX = moveY = 0;
      break;
    case 'top-right':
      moveX = dims.width;
      moveY = 0;
      break;
    case 'bottom-left':
      moveX = 0;
      moveY = dims.height;
      break;
    case 'bottom-right':
      moveX = dims.width;
      moveY = dims.height;
      break;
    case 'center': 
      moveX = dims.width / 2;
      moveY = dims.height / 2;
      break;
 }
  
  return new Effect.Parallel(
    [new Effect.Opacity(element,{sync:true,to:0.0,from:1.0,transition:options.opacityTransition}),
      new Effect.Scale(element,window.opera ? 1 :0,{sync:true,transition:options.scaleTransition,restoreAfterFinish:true}),
      new Effect.Move(element,{x:moveX,y:moveY,sync:true,transition:options.moveTransition})
   ],Object.extend({           
         beforeStartInternal:function(effect){
           effect.effects[0].element.makePositioned().makeClipping();
        },
         afterFinishInternal:function(effect){
           effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle);}
      },options)
 );
};

Effect.Pulsate = function(element){
  element = $(element);
  var options    = arguments[1] ||{};
  var oldOpacity = element.getInlineOpacity();
  var transition = options.transition || Effect.Transitions.sinoidal;
  var reverser   = function(pos){return transition(1-Effect.Transitions.pulse(pos,options.pulses))};
  reverser.bind(transition);
  return new Effect.Opacity(element,
    Object.extend(Object.extend({ duration:2.0,from:0,
      afterFinishInternal:function(effect){effect.element.setStyle({opacity:oldOpacity});}
   },options),{transition:reverser}));
};

Effect.Fold = function(element){
  element = $(element);
  var oldStyle ={
    top:element.style.top,
    left:element.style.left,
    width:element.style.width,
    height:element.style.height};
  element.makeClipping();
  return new Effect.Scale(element,5,Object.extend({  
    scaleContent:false,
    scaleX:false,
    afterFinishInternal:function(effect){
    new Effect.Scale(element,1,{
      scaleContent:false,
      scaleY:false,
      afterFinishInternal:function(effect){
        effect.element.hide().undoClipping().setStyle(oldStyle);
     }});
 }},arguments[1] ||{}));
};

Effect.Morph = Class.create(Effect.Base,{
  initialize:function(element){
    this.element = $(element);
    if (!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({
      style:{}
   },arguments[1] ||{});
    
    if (!Object.isString(options.style)) this.style = $H(options.style);
    else{
      if (options.style.include(':'))
        this.style = options.style.parseStyle();
      else{
        this.element.addClassName(options.style);
        this.style = $H(this.element.getStyles());
        this.element.removeClassName(options.style);
        var css = this.element.getStyles();
        this.style = this.style.reject(function(style){
          return style.value == css[style.key];
       });
        options.afterFinishInternal = function(effect){
          effect.element.addClassName(effect.options.style);
          effect.transforms.each(function(transform){
            effect.element.style[transform.style] = '';
         });
       }
     }
   }
    this.start(options);
 },
  
  setup:function(){
    function parseColor(color){
      if (!color || ['rgba(0,0,0,0)','transparent'].include(color)) color = '#ffffff';
      color = color.parseColor();
      return $R(0,2).map(function(i){
        return parseInt(color.slice(i*2+1,i*2+3),16) 
     });
   }
    this.transforms = this.style.map(function(pair){
      var property = pair[0],value = pair[1],unit = null;

      if (value.parseColor('#zzzzzz') != '#zzzzzz'){
        value = value.parseColor();
        unit  = 'color';
     }else if (property == 'opacity'){
        value = parseFloat(value);
        if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
          this.element.setStyle({zoom:1});
     }else if (Element.CSS_LENGTH.test(value)){
          var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
          value = parseFloat(components[1]);
          unit = (components.length == 3) ? components[2] :null;
     }

      var originalValue = this.element.getStyle(property);
      return{
        style:property.camelize(),
        originalValue:unit=='color' ? parseColor(originalValue) :parseFloat(originalValue || 0),
        targetValue:unit=='color' ? parseColor(value) :value,
        unit:unit
     };
   }.bind(this)).reject(function(transform){
      return (
        (transform.originalValue == transform.targetValue) ||
        (
          transform.unit != 'color' &&
          (isNaN(transform.originalValue) || isNaN(transform.targetValue))
       )
     )
   });
 },
  update:function(position){
    var style ={},transform,i = this.transforms.length;
    while(i--)
      style[(transform = this.transforms[i]).style] = 
        transform.unit=='color' ? '#'+
          (Math.round(transform.originalValue[0]+
            (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
          (Math.round(transform.originalValue[1]+
            (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
          (Math.round(transform.originalValue[2]+
            (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
        (transform.originalValue +
          (transform.targetValue - transform.originalValue) * position).toFixed(3) + 
            (transform.unit === null ? '' :transform.unit);
    this.element.setStyle(style,true);
 }
});

Effect.Transform = Class.create({
  initialize:function(tracks){
    this.tracks  = [];
    this.options = arguments[1] ||{};
    this.addTracks(tracks);
 },
  addTracks:function(tracks){
    tracks.each(function(track){
      track = $H(track);
      var data = track.values().first();
      this.tracks.push($H({
        ids:    track.keys().first(),
        effect: Effect.Morph,
        options:{style:data}
     }));
   }.bind(this));
    return this;
 },
  play:function(){
    return new Effect.Parallel(
      this.tracks.map(function(track){
        var ids = track.get('ids'),effect = track.get('effect'),options = track.get('options');
        var elements = [$(ids) || $$(ids)].flatten();
        return elements.map(function(e){return new effect(e,Object.extend({sync:true},options))});
     }).flatten(),
      this.options
   );
 }
});

Element.CSS_PROPERTIES = $w(
  'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + 
  'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
  'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
  'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
  'fontSize fontWeight height left letterSpacing lineHeight ' +
  'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
  'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
  'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
  'right textIndent top width wordSpacing zIndex');
  
Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;

String.__parseStyleElement = document.createElement('div');
String.prototype.parseStyle = function(){
  var style,styleRules = $H();
  if (Prototype.Browser.WebKit)
    style = new Element('div',{style:this}).style;
  else{
    String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
    style = String.__parseStyleElement.childNodes[0].style;
 }
  
  Element.CSS_PROPERTIES.each(function(property){
    if (style[property]) styleRules.set(property,style[property]);
 });
  
  if (Prototype.Browser.IE && this.include('opacity'))
    styleRules.set('opacity',this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);

  return styleRules;
};

if (document.defaultView && document.defaultView.getComputedStyle){
  Element.getStyles = function(element){
    var css = document.defaultView.getComputedStyle($(element),null);
    return Element.CSS_PROPERTIES.inject({},function(styles,property){
      styles[property] = css[property];
      return styles;
   });
 };
}else{
  Element.getStyles = function(element){
    element = $(element);
    var css = element.currentStyle,styles;
    styles = Element.CSS_PROPERTIES.inject({},function(results,property){
      results[property] = css[property];
      return results;
   });
    if (!styles.opacity) styles.opacity = element.getOpacity();
    return styles;
 };
};

Effect.Methods ={
  morph:function(element,style){
    element = $(element);
    new Effect.Morph(element,Object.extend({style:style},arguments[2] ||{}));
    return element;
 },
  visualEffect:function(element,effect,options){
    element = $(element)
    var s = effect.dasherize().camelize(),klass = s.charAt(0).toUpperCase() + s.substring(1);
    new Effect[klass](element,options);
    return element;
 },
  highlight:function(element,options){
    element = $(element);
    new Effect.Highlight(element,options);
    return element;
 }
};

$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
  'pulsate shake puff squish switchOff dropOut').each(
  function(effect){
    Effect.Methods[effect] = function(element,options){
      element = $(element);
      Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element,options);
      return element;
   }
 }
);

$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(
  function(f){Effect.Methods[f] = Element[f];}
);

Element.addMethods(Effect.Methods);