zul/src/main/resources/web/js/zul/WScroll.ts
/* WScroll.ts
Purpose:
Description:
A wave scrollbar control
History:
Wed, Feb 22, 2012 4:16:53 PM, Created by jumperchen
Copyright (C) 2012 Potix Corporation. All Rights Reserved.
*/
function easing(x: number, t: number, b: number, c: number, d: number): number {
return -c * ((t = t / d - 1) * t * t * t - 1) + b; // easeOutQuart
}
function snap(dg: zk.Draggable, pointer: zk.Offset): zk.Offset {
var x = pointer[0],
y = pointer[1];
if (dg._isVer) {
var move = y;
if (move - dg._start < 0) {
move = pointer[1] = dg._start;
} else if (move > dg._end) {
move = pointer[1] = dg._end;
}
if (dg._lastPos) { // optimized
if (Math.abs(dg._lastPos - move) < 3)
return pointer;
}
dg._lastPos = move;
} else {
var move = x;
if (move - dg._start < 0) {
move = pointer[0] = dg._start;
} else if (move > dg._end) {
move = pointer[0] = dg._end;
}
if (dg._lastPos) { // optimized
if (Math.abs(dg._lastPos - move) < 3)
return pointer;
}
dg._lastPos = move;
}
return pointer;
}
function starteffect(dg: zk.Draggable): void {
var ctrl = dg.control as unknown as zul.WScroll,
opts = ctrl.opts;
dg._steps = opts.startStep;
dg._endStep = opts.endStep - opts.viewport;
dg._scale = ctrl._scale;
dg._epos = ctrl.epos;
dg._lastPos = dg._start;
if (ctrl._isVer) {
dg._isVer = true;
dg._start = opts.startPosition;
if (zk(ctrl.eend).isVisible()) {
dg._end = ctrl.eend.offsetTop + Math.ceil(dg.handle!.offsetHeight / 2);
if (dg._end > opts.viewportSize + dg._start)
dg._end = opts.viewportSize + dg._start;
} else {
dg._end = opts.viewportSize + dg._start;
}
dg._end -= dg.node!.offsetHeight - ctrl._gap;
} else {
dg._isVer = false;
dg._start = opts.startPosition;
if (zk(ctrl.eend).isVisible()) {
dg._end = ctrl.eend.offsetLeft + Math.ceil(dg.handle!.offsetWidth / 2);
if (dg._end > opts.viewportSize + dg._start)
dg._end = opts.viewportSize + dg._start;
} else {
dg._end = opts.viewportSize + dg._start;
}
dg._end -= dg.node!.offsetWidth - ctrl._gap;
}
jq(dg._epos).show().delay(200).fadeIn(500);
if (dg._timer) {
clearTimeout(dg._timer);
}
var lastP,
lastS: number[] = [],
timeout = 30,
duration = timeout * 20,
t = 10,
running = function (orient: 'top' | 'left'): void {
var norient = zk.parseFloat(dg.node!.style[orient]),
diff = norient - zk.parseFloat(dg._epos!.style[orient]);
if (lastP == norient) {
lastS.push(dg._lastSteps);
if (lastS.length > 4 && lastS.shift() == dg._lastSteps) {
lastS[0] = dg._lastSteps;
clearTimeout(dg._timer);
dg._timer = setTimeout(function () {running(orient);}, 100);
return;
}
} else t = 10; // reset
lastP = norient;
var down = diff > 0,
total = down ? Math.max(0, diff / dg._scale) : Math.min(0, diff / dg._scale),
step = Math.round(zul.WScroll.easing(t / duration, t, 0, total, duration));
if (down) {
if (total > 1)
step = Math.max(1, step);
} else {
if (-total > 1)
step = Math.min(-1, step);
}
if (diff == 0 && step == 0) {
if (norient == dg._start)
step = -dg._steps;
else if (norient == dg._end)
step = dg._endStep - dg._steps;
}
dg._steps += step;
if (down) {
if (dg._steps > dg._endStep)
dg._steps = dg._endStep;
} else {
if (dg._steps < 0)
dg._steps = 0;
}
dg._epos!.style[orient] = dg._start + (dg._scale * dg._steps) + 'px';
t += timeout;
if (dg._lastSteps != dg._steps) {
dg._lastSteps = dg._steps;
var func = orient == 'top' ? ctrl.opts.onScrollY : ctrl.opts.onScrollX;
if (typeof func == 'function') {
func.call((dg.control as unknown as zul.WScroll).widget, dg._steps + ctrl.opts.offset);
}
}
clearTimeout(dg._timer);
dg._timer = setTimeout(function () {running(orient);}, timeout);
};
dg._timer = setTimeout(function () {running((dg._isVer ? 'top' : 'left'));}, 50);
}
function endeffect(dg: zk.Draggable): void {
var ctrl = dg.control as unknown as zul.WScroll;
if (dg._timer) {
clearTimeout(dg._timer);
}
var move: number, end: number;
if (dg._isVer) {
move = zk.parseInt(dg._epos!.style.top);
end = dg._end;
if (move > end)
move = end;
jq(dg.node).animate({top: move + 'px'}, 400, 'swing');
} else {
move = zk.parseInt(dg._epos!.style.left);
end = dg._end;
if (move > end)
move = end;
jq(dg.node).animate({left: move + 'px'}, 400, 'swing');
}
ctrl.opts.startStep = dg._steps;
ctrl._syncButtonStatus();
var $jq = jq(dg._epos),
old = $jq.css('opacity'); // fix old IE version
jq(dg._epos).delay(300).fadeOut(500).css('opacity', old);
}
function ignoredrag(dg: zk.Draggable, p: zk.Offset, evt: zk.Event): boolean {
return (dg.control as unknown as WScroll).edragBody != evt.domTarget;
}
export interface WScrollOptions {
orient: 'vertical' | 'horizontal';
startPosition: number;
startStep: number;
offset: number;
anchor?: HTMLElement | null; // eslint-disable-line zk/noNull
viewport: number;
endStep: number;
viewportSize: number;
syncSize: boolean;
onScrollX?(this: zk.Widget, delta: number): void;
onScrollY?(this: zk.Widget, delta: number): void;
}
/**
* A wave Scrollbar used to scroll the specific content and provides four controls
* to navigate the content, such as Home/Previous/Next/End, and also supports the
* mousewheel control.
*/
@zk.WrapClass('zul.WScroll')
export class WScroll extends zk.Object {
/** The control object for this scrolling that user can scroll the whole content
* @type DOMElement
*/
//control: null,
/** The widget object that owns the control object.
* @type zk.Widget
*/
//widget: null,
/**
* The opts of this scrollbar controls.
* <h4>startPosition</h4>
* int startPosition
* <p>Specifies the start position according to the scrolling area, like offset top for
* the vertical scrolling and offset left for the horizental scrolling.
*
* <h4>startStep</h4>
* int startStep
* <p>Specifies the start step for the scrolling.
* <p>Note: it cannot be negative.
*
* <h4>endStep</h4>
* int endStep
* <p>Specifies how many steps for the scrolling.
* <p>Note: it cannot be negative.
*
* <h4>viewport</h4>
* int viewport
* <p>Specifies how many steps will show in the viewport.
* <p>Note: it cannot be negative.
*
* <h4>viewportSize</h4>
* int viewportSize
* <p>Specifies how many pixels for the viewport size, like offsetHeight for
* vertical scrolling and offsetWidth for horizental scrolling.
* <p>Note: it cannot be negative.
*
* <h4>orient</h4>
* String orient
* <p>Specifies either 'vertical' or 'horizontal' to indicate that it can be
* scrolled only in the vertical or horizontal direction.
* @defaultValue 'horizontal'
*
* <h4>anchor</h4>
* DOMElement anchor
* <p>Specifies the anchor that indicates the scrollbar will be its child node.
* @defaultValue the parent node of the control.
*
* <h4>syncSize</h4>
* boolean syncSize
* <p>Specifies whether to sync the scrolling area size at initial phase.
* @defaultValue `true`.
*
* <h4>onScrollY</h4>
* ```ts
* void onScrollY(int step);
* ```
* <p>Specifies the callback function for the vertical scrolling, when user
* changes the vertical scrolling step.
*
* <h4>onScrollX</h4>
* ```ts
* void onScrollX(int step);
* ```
* <p>Specifies the callback function for the horizental scrolling, when user
* changes the horizental scrolling step.
*
* <h4>offset</h4>
* int offset
* <p>Specifies the offset for the scrolling step to shift when the callback
* functions (onScrollX and onScrollY) are invoked.
* For example, if the offset is 2, then the steps in the onScrollX/Y event
* will start at 2.
* @defaultValue `0`
*/
//opts: null,
opts: WScrollOptions;
/** @internal */
_gap = 0;
control: HTMLElement;
widget: zk.Widget & {_cols?: number};
/** @internal */
_scale!: number; // Initialized before use.
/** @internal */
_isVer: boolean;
uid: string;
zcls: string;
anchor: HTMLElement;
// The following properties are initialized in _initDragdrop which is called
// by the constructor.
node!: HTMLElement;
edrag!: HTMLElement;
edragBody!: ChildNode;
epos!: HTMLElement;
eend!: HTMLElement;
drag!: zk.Draggable;
constructor(control: HTMLElement, opts: WScrollOptions) {
super();
this.control = control;
this.opts = zk.$default(opts, {
orient: 'horizontal',
startPosition: 0,
startStep: 0,
offset: 0
});
this.anchor = this.opts.anchor || control.parentNode as HTMLElement;
this.widget = zk.Widget.$(control)!;
this.uid = this.widget.uuid;
this.zcls = /*safe*/ this.widget.getZclass();
this._isVer = opts.orient == 'vertical';
// ZK-2178: viewportSize is 0 if biglistbox has not model
if (!opts.viewportSize && opts.viewportSize != 0)
throw 'Handle required for a viewport size: {viewportSize: size}';
this.redraw(this.anchor);
this._initDragdrop();
this._listenMouseEvent();
if (this.opts.syncSize)
this.syncSize();
this._syncButtonStatus();
}
/**
* Syncs the scrolling area and control bar size.
* @param opts - the opts can override the initail opts data for resizing.
*/
syncSize(opts?: Record<string, unknown>): void {
if (opts) {
this.opts = zk.copy(this.opts, opts);
}
this.edrag.style.display = '';
if (this._isVer) {
const opts = this.opts,
top = opts.startPosition,
start = opts.startStep,
view = opts.viewport,
end = opts.endStep,
rest = end - view,
edragHeight = this.edrag.offsetHeight - this._gap;
let vsize = opts.viewportSize;
if (rest <= 0) {
this.eend.style.display = this.edrag.style.display = 'none';
if (typeof this.opts.onScrollY == 'function')
this.opts.onScrollY.call(this.widget, opts.offset); //reset scrolling
return;
}
vsize -= edragHeight;
if (vsize > rest) {
this.epos.style.height = edragHeight + 'px';
this._scale = 1;
var es = this.eend.style;
es.display = '';
es.top = top + edragHeight + rest + 'px';
} else {
var rate = vsize / rest,
height = Math.max(edragHeight * rate, 5);
this.epos.style.height = height + 'px';
this._scale = rate;
this.eend.style.display = 'none'; // no end point
if (vsize < 10)
this.edrag.style.display = 'none';
}
var top1 = top + (this._scale * start),
top2 = top + vsize;
if (top1 > top2)
top1 = top2;
this.epos.style.top = this.edrag.style.top = top1 + 'px';
} else {
const opts = this.opts,
left = opts.startPosition,
start = opts.startStep,
view = opts.viewport,
end = opts.endStep,
rest = end - view,
edragWidth = this.edrag.offsetWidth - this._gap;
let vsize = opts.viewportSize;
if (rest <= 0) {
this.eend.style.display = this.edrag.style.display = 'none';
if (typeof this.opts.onScrollX == 'function')
this.opts.onScrollX.call(this.widget, opts.offset); //reset scrolling
return;
}
vsize -= edragWidth;
if (vsize > rest) {
this.epos.style.width = edragWidth + 'px';
this._scale = 1;
var es = this.eend.style;
es.display = '';
es.left = left + edragWidth + rest + 'px';
} else {
var rate = vsize / rest,
width = Math.max(edragWidth * rate, 5);
this.epos.style.width = width + 'px';
this._scale = rate;
this.eend.style.display = 'none'; // no end point
if (vsize < 10)
this.edrag.style.display = 'none';
}
var left1 = left + (this._scale * start),
left2 = left + vsize;
if (left1 > left2)
left1 = left2;
this.epos.style.left = this.edrag.style.left = left1 + 'px';
}
}
/** @internal */
_listenMouseEvent(): void {
if (this._isVer) {
// @ts-expect-error: zk.Event is not completely compatible with JQueryMousewheelEventObject
jq(this.control).mousewheel(this._mousewheelY.bind(this));
} else if (!zk.opera) { // ie and opera unsupported
// @ts-expect-error: zk.Event is not completely compatible with JQueryMousewheelEventObject
jq(this.control).mousewheel(this._mousewheelX.bind(this));
}
var $drag = jq(this.edrag);
$drag.children('div')
.on('mouseover', this._mouseOver.bind(this))
.on('mouseout', this._mouseOut.bind(this))
.on('mouseup', this._mouseUp.bind(this))
.on('mousedown', this._mouseDown.bind(this));
$drag.on('click', zk.$void);
}
/** @internal */
_unlistenMouseEvent(): void {
if (this._isVer)
// @ts-expect-error: unmousewheel expects 0 arguments, but got 1
jq(this.control).unmousewheel(this._mousewheelY.bind(this));
else if (!zk.opera) // ie and opera unsupported
// @ts-expect-error: unmousewheel expects 0 arguments, but got 1
jq(this.control).unmousewheel(this._mousewheelX.bind(this));
var $drag = jq(this.edrag);
$drag.children('div')
.off('mouseover', this._mouseOver.bind(this))
.off('mouseout', this._mouseOut.bind(this))
.off('mouseup', this._mouseUp.bind(this))
.off('mousedown', this._mouseDown.bind(this));
$drag.off('click', zk.$void);
}
/** @internal */
_mouseOver(evt: zk.Event): void {
var cls = evt.target.className,
index = cls.lastIndexOf('-'),
key = cls.substring(index + 1),
$drag = jq(this.edrag);
if ($drag.hasClass(cls + '-clk')) {
$drag.removeClass(cls + '-clk');
}
switch (key) {
case 'home':
case 'up':
if (this.opts.startStep > 0)
$drag.addClass(cls + '-over');
break;
case 'down':
case 'end':
var opts = this.opts;
if (opts.startStep < opts.endStep - opts.viewport) {
$drag.addClass(cls + '-over');
}
break;
}
}
/** @internal */
_mouseOut(evt: zk.Event): void {
var cls = evt.target.className,
$drag = jq(this.edrag);
$drag.removeClass(cls + '-over');
if ($drag.hasClass(cls + '-clk')) {
$drag.removeClass(cls + '-clk');
}
}
/** @internal */
_mouseUp(evt: zk.Event): void {
jq(this.edrag).removeClass(evt.target.className + '-clk');
}
/** @internal */
_mouseDown(evt: zk.Event): void {
var cls = evt.target.className,
index = cls.lastIndexOf('-'),
key = cls.substring(index + 1),
$drag = jq(this.edrag);
if (!$drag.hasClass(cls + '-over') && !zk.mobile) //no mouse over for mobile
return;// disable
$drag.addClass(cls + '-clk');
var opts = this.opts;
switch (key) {
case 'home':
if (opts.startStep > 0) {
opts.startStep = 0;
if (this._isVer) {
const moving = opts.startPosition + 'px';
this.epos.style.top = moving;
$drag.animate({top: moving}, 500);
if (typeof this.opts.onScrollY == 'function')
this.opts.onScrollY.call(this.widget, opts.startStep + opts.offset);
} else {
const moving = opts.startPosition + 'px';
this.epos.style.left = moving;
$drag.animate({left: moving}, 500);
if (typeof this.opts.onScrollX == 'function')
this.opts.onScrollX.call(this.widget, opts.startStep + opts.offset);
}
$drag.removeClass(cls + '-over');
}
break;
case 'up':
if (opts.startStep > 0) {
opts.startStep -= 1;
var move = opts.startPosition + (opts.startStep * this._scale);
if (this._isVer) {
var end;
if (zk(this.eend).isVisible()) {
end = this.eend.offsetTop;
} else {
end = opts.viewportSize + opts.startPosition;
}
end -= this.edrag.offsetHeight - this._gap;
this.epos.style.top = move + 'px';
if (end < move) {
this.edrag.style.top = end + 'px';
} else {
this.edrag.style.top = move + 'px';
}
if (typeof this.opts.onScrollY == 'function')
this.opts.onScrollY.call(this.widget, opts.startStep + opts.offset);
} else {
var end;
if (zk(this.eend).isVisible()) {
end = this.eend.offsetLeft;
} else {
end = opts.viewportSize + opts.startPosition;
}
end -= this.edrag.offsetWidth - this._gap;
this.epos.style.left = move + 'px';
if (end < move) {
this.edrag.style.left = end + 'px';
} else {
this.edrag.style.left = move + 'px';
}
if (typeof this.opts.onScrollX == 'function')
this.opts.onScrollX.call(this.widget, opts.startStep + opts.offset);
}
if (opts.startStep == 0)
$drag.removeClass(cls + '-over');
}
break;
case 'down':
if (opts.startStep < opts.endStep - opts.viewport) {
opts.startStep += 1;
var move = opts.startPosition + (opts.startStep * this._scale);
if (this._isVer) {
var end;
if (zk(this.eend).isVisible()) {
end = this.eend.offsetTop;
} else {
end = opts.viewportSize + opts.startPosition;
}
end -= this.edrag.offsetHeight - this._gap;
this.epos.style.top = move + 'px';
if (end < move) {
this.edrag.style.top = end + 'px';
} else {
this.edrag.style.top = move + 'px';
}
if (typeof this.opts.onScrollY == 'function')
this.opts.onScrollY.call(this.widget, opts.startStep + opts.offset);
} else {
var end;
if (zk(this.eend).isVisible()) {
end = this.eend.offsetLeft;
} else {
end = opts.viewportSize + opts.startPosition;
}
end -= this.edrag.offsetWidth - this._gap;
this.epos.style.left = move + 'px';
if (end < move) {
this.edrag.style.left = end + 'px';
} else {
this.edrag.style.left = move + 'px';
}
if (typeof this.opts.onScrollX == 'function')
this.opts.onScrollX.call(this.widget, opts.startStep + opts.offset);
}
if (opts.startStep == opts.endStep - opts.viewport)
$drag.removeClass(cls + '-over');
}
break;
case 'end':
if (opts.startStep < opts.endStep - opts.viewport) {
opts.startStep = opts.endStep - opts.viewport;
if (this._isVer) {
let moving: number;
if (zk(this.eend).isVisible()) {
moving = this.eend.offsetTop - (this.edrag.offsetHeight - this._gap);
} else {
moving = opts.startPosition + opts.viewportSize - (this.edrag.offsetHeight - this._gap);
}
this.epos.style.top = moving as unknown as string;
$drag.animate({top: moving}, 500);
if (typeof this.opts.onScrollY == 'function')
this.opts.onScrollY.call(this.widget, opts.startStep + opts.offset);
} else {
let moving: number;
if (zk(this.eend).isVisible()) {
moving = this.eend.offsetLeft - (this.edrag.offsetWidth - this._gap);
} else {
moving = opts.startPosition + opts.viewportSize - (this.edrag.offsetWidth - this._gap);
}
this.epos.style.left = moving as unknown as string;
$drag.animate({left: moving}, 500);
if (typeof this.opts.onScrollX == 'function')
this.opts.onScrollX.call(this.widget, opts.startStep + opts.offset);
}
$drag.removeClass(cls + '-over');
}
break;
}
this._syncButtonStatus();
}
/** @internal */
_mousewheelY(evt: zk.Event, delta: number, deltaX: number, deltaY: number): void {
if (deltaY) {
evt.stop();
var opts = this.opts,
steps = opts.startStep,
endStep = opts.endStep - opts.viewport,
scale = this._scale,
wgt = this.widget;
if (deltaY > 0) { // up
opts.startStep -= Math.max(Math.round(wgt._cols! / 5), 1);
if (opts.startStep < 0)
opts.startStep = 0;
} else { // down
opts.startStep += Math.max(Math.round(wgt._cols! / 5), 1);
if (opts.startStep > endStep)
opts.startStep = endStep;
}
if (steps == opts.startStep)
return;// nothing changed
var moving = opts.startPosition + (opts.startStep * scale),
end = zk(this.eend).isVisible() ? this.eend.offsetTop - (this.edrag.offsetHeight - this._gap)
: opts.startPosition + opts.viewportSize - (this.edrag.offsetHeight - this._gap);
this.epos.style.top = moving + 'px';
if (moving > end)
moving = end;
this.edrag.style.top = moving + 'px';
if (typeof this.opts.onScrollY == 'function')
this.opts.onScrollY.call(this.widget, opts.startStep + opts.offset);
this._syncButtonStatus();
}
}
/** @internal */
_mousewheelX(evt: zk.Event, delta: number, deltaX: number, deltaY: number): void {
if (deltaX) {
evt.stop();
var opts = this.opts,
steps = opts.startStep,
endStep = opts.endStep - opts.viewport,
scale = this._scale,
wgt = this.widget;
if (deltaX < 0) { // up
opts.startStep -= Math.max(Math.round(wgt._cols! / 5), 1);
if (opts.startStep < 0)
opts.startStep = 0;
} else { // down
opts.startStep += Math.max(Math.round(wgt._cols! / 5), 1);
if (opts.startStep > endStep)
opts.startStep = endStep;
}
if (steps == opts.startStep)
return;// nothing changed
var moving = opts.startPosition + (opts.startStep * scale),
end = zk(this.eend).isVisible() ? this.eend.offsetLeft - (this.edrag.offsetWidth - this._gap)
: opts.startPosition + opts.viewportSize - (this.edrag.offsetWidth - this._gap);
this.epos.style.left = moving + 'px';
if (moving > end)
moving = end;
this.edrag.style.left = moving + 'px';
if (typeof this.opts.onScrollX == 'function')
this.opts.onScrollX.call(this.widget, opts.startStep + opts.offset);
this._syncButtonStatus();
}
}
/** @internal */
_initDragdrop(): void {
var orient = this._isVer ? 'v' : 'h',
uuid = this.uid + '-' + orient + 'bar';
this.node = jq(uuid, zk)[0];
this.edrag = this.node.firstChild as HTMLElement;
this.edragBody = this.edrag.childNodes[2];
this.epos = this.edrag.nextSibling as HTMLElement;
this.eend = this.node.lastChild as HTMLElement;
// sync the gap between edrag and epos
var s = this.epos.style,
old = s.display;
s.display = 'block';
this._gap = this._isVer ? this.edrag.offsetHeight - this.epos.offsetHeight
: this.edrag.offsetWidth - this.epos.offsetWidth;
s.display = old;
this.drag = new zk.Draggable(this, this.edrag, {
constraint: this._isVer ? 'vertical' : 'horizontal',
snap: snap,
starteffect: starteffect,
zIndex: '12000',
ignoredrag: ignoredrag,
endeffect: endeffect
});
jq(this.epos).hide();
}
destroy(): void {
this.drag.destroy();
this._unlistenMouseEvent();
jq(this.node).remove();
// @ts-expect-error: these variables should only become undefined after destroy
this.node = this.edrag = this.epos = this.drag = undefined;
}
redraw(p: HTMLElement): void {
var orient = this._isVer ? 'v' : 'h',
ocls = this._isVer ? 'vertical' : 'horizontal',
uuid = this.uid + '-' + orient + 'bar',
zcls = this.widget.$s('wscroll');
jq(p).append(/*safe*/ '<div id="' + uuid + '" class="' + zcls + '-' + ocls + '">'
+ '<div class="' + zcls + '-drag">'
+ '<div class="' + zcls + '-home" title="' + /*safe*/ msgzul.WS_HOME + '"></div>'
+ '<div class="' + zcls + '-up" title="' + /*safe*/ msgzul.WS_PREV + '"></div>'
+ '<div class="' + zcls + '-body"></div>'
+ '<div class="' + zcls + '-down" title="' + /*safe*/ msgzul.WS_NEXT + '"></div>'
+ '<div class="' + zcls + '-end" title="' + /*safe*/ msgzul.WS_END + '"></div>'
+ '</div>'
+ '<div class="' + zcls + '-pos"></div>'
+ '<div class="' + zcls + '-endbar"></div>'
+ '</div>');
}
/** @internal */
_syncButtonStatus(): void {
var zcls = this.zcls + '-wscroll',
$drag = jq(this.edrag),
opts = this.opts;
$drag.toggleClass(zcls + '-head', opts.startStep == 0);
$drag.toggleClass(zcls + '-tail', opts.startStep == opts.endStep - opts.viewport);
}
/**
* Sets the easing animation function for the scrolling effects.
* For more details, please refer to jquery's easing plugin.
* http://gsgd.co.uk/sandbox/jquery/easing/
*/
static easing = easing;
}