1 /**
  2  * jx.nfx.js
  3  * do animation of htmlElements
  4  * CSS3 animation & normal are compatible
  5  * 
  6  */
  7 
  8 /* FX */
  9 Jx().$package(function(J){
 10     J.fx = J.fx || {};
 11     
 12     !function(context, doc, win) {
 13 
 14         var html = doc.documentElement,
 15         rgbOhex = /^rgb\(|#/,
 16         relVal = /^([+\-])=([\d\.]+)/,
 17         numUnit = /^(?:[\+\-]=)?\d+(?:\.\d+)?(%|in|cm|mm|em|ex|pt|pc|px)$/,
 18         rotate = /rotate\(((?:[+\-]=)?([\-\d\.]+))deg\)/,
 19         scale = /scale\(((?:[+\-]=)?([\d\.]+))\)/,
 20         skew = /skew\(((?:[+\-]=)?([\-\d\.]+))deg, ?((?:[+\-]=)?([\-\d\.]+))deg\)/,
 21         translate = /translate\(((?:[+\-]=)?([\-\d\.]+))px, ?((?:[+\-]=)?([\-\d\.]+))px\)/,
 22         // these elements do not require 'px'
 23         unitless = {
 24             lineHeight: 1,
 25             zoom: 1,
 26             zIndex: 1,
 27             opacity: 1,
 28             transform: 1
 29         },
 30         // which property name does this browser use for transform
 31         /**
 32          * @ignore
 33          */
 34         transform = function() {
 35             var styles = doc.createElement('a').style,
 36             props = ['webkitTransform', 'MozTransform', 'OTransform', 'msTransform', 'Transform'],
 37             i
 38             for (i = 0; i < props.length; i++) {
 39                 if (props[i] in styles) return props[i]
 40             }
 41         } (),
 42 
 43         // does this browser support the opacity property?
 44         /**
 45          * @ignore
 46          */
 47         opasity = function() {
 48             return typeof doc.createElement('a').style.opacity !== 'undefined'
 49         } (),
 50 
 51         // initial style is determined by the elements themselves
 52         getStyle = doc.defaultView && doc.defaultView.getComputedStyle ?
 53         function(el, property) {
 54             property = property == 'transform' ? transform: property
 55             var value = null,
 56             computed = doc.defaultView.getComputedStyle(el, '')
 57             computed && (value = computed[camelize(property)])
 58             return el.style[property] || value
 59         }: html.currentStyle ?
 60 
 61         function(el, property) {
 62             property = camelize(property)
 63 
 64             if (property == 'opacity') {
 65                 var val = 100
 66                 try {
 67                     val = el.filters['DXImageTransform.Microsoft.Alpha'].opacity
 68                 } catch(e1) {
 69                     try {
 70                         val = el.filters('alpha').opacity
 71                     } catch(e2) {}
 72                 }
 73                 return val / 100
 74             }
 75             var value = el.currentStyle ? el.currentStyle[property] : null
 76             return el.style[property] || value
 77         }: function(el, property) {
 78             return el.style[camelize(property)]
 79         },
 80         /**
 81          * @ignore
 82          */
 83         frame = function() {
 84             // native animation frames
 85             // http://webstuff.nfshost.com/anim-timing/Overview.html
 86             // http://dev.chromium.org/developers/design-documents/requestanimationframe-implementation
 87             return win.requestAnimationFrame || win.webkitRequestAnimationFrame || win.mozRequestAnimationFrame || win.oRequestAnimationFrame || win.msRequestAnimationFrame ||
 88             function(callback) {
 89                 win.setTimeout(function() {
 90                     callback( + new Date())
 91                 },
 92                 10)
 93             }
 94         } ()
 95 
 96         function parseTransform(style, base) {
 97             var values = {}, m;
 98             if (m = style.match(rotate)) values.rotate = by(m[1], base ? base.rotate: null); 
 99             if (m = style.match(scale)) values.scale = by(m[1], base ? base.scale: null); 
100             if (m = style.match(skew)) {
101                 values.skewx = by(m[1], base ? base.skewx: null);
102                 values.skewy = by(m[3], base ? base.skewy: null);
103             }
104             if (m = style.match(translate)) {
105                 values.translatex = by(m[1], base ? base.translatex: null);
106                 values.translatey = by(m[3], base ? base.translatey: null);
107             }
108             return values;
109         }
110 
111         function formatTransform(v) {
112             var s = ''
113             if ('rotate' in v) s += 'rotate(' + v.rotate + 'deg) '
114             if ('scale' in v) s += 'scale(' + v.scale + ') '
115             if ('translatex' in v) s += 'translate(' + v.translatex + 'px,' + v.translatey + 'px) '
116             if ('skewx' in v) s += 'skew(' + v.skewx + 'deg,' + v.skewy + 'deg)'
117             return s
118         }
119 
120         function rgb(r, g, b) {
121             return '#' + (1 << 24 | r << 16 | g << 8 | b).toString(16).slice(1)
122         }
123 
124         // convert rgb and short hex to long hex
125         function toHex(c) {
126             var m = /rgba?\((\d+),\s*(\d+),\s*(\d+)/.exec(c)
127             return (m ? rgb(m[1], m[2], m[3]) : c).replace(/#(\w)(\w)(\w)$/, '#$1$1$2$2$3$3') // short to long
128         }
129 
130         // change font-size => fontSize etc.
131         function camelize(s) {
132             return s.replace(/-(.)/g,
133             function(m, m1) {
134                 return m1.toUpperCase()
135             })
136         }
137 
138         function fun(f) {
139             return typeof f == 'function'
140         }
141 
142         /**
143         * Core tween method that requests each frame
144         * @param duration: time in milliseconds. defaults to 1000
145         * @param fn: tween frame callback function receiving 'position'
146         * @param done {optional}: complete callback function
147         * @param ease {optional}: easing method. defaults to easeOut
148         * @param from {optional}: integer to start from
149         * @param to {optional}: integer to end at
150         * @returns method to stop the animation
151         */
152         function tween(duration, fn, done, ease, from, to) {
153             ease = ease ||
154             function(t) {
155                 // default to a pleasant-to-the-eye easeOut (like native animations)
156                 return Math.sin(t * Math.PI / 2)
157             }
158             var time = duration || 1000,
159             diff = to - from,
160             start = +new Date(),
161             stop = 0,
162             end = 0
163             frame(run)
164 
165             function run(t) {
166                 var delta = t - start
167                 if (delta > time || stop) {
168                     to = isFinite(to) ? to: 1
169                     stop ? end && fn(to) : fn(to)
170                     return done && done()
171                 }
172                 // if you don't specify a 'to' you can use tween as a generic delta tweener
173                 // cool, eh?
174                 isFinite(to) ? fn((diff * ease(delta / time)) + from) : fn(ease(delta / time))
175                 frame(run)
176             }
177             return {
178                 stop: function(jump) {
179                     stop = 1
180                     end = jump // jump to end of animation?
181                 }
182             }
183         }
184 
185         /**
186         * generic bezier method for animating x|y coordinates
187         * minimum of 2 points required (start and end).
188         * first point start, last point end
189         * additional control points are optional (but why else would you use this anyway ;)
190         * @param points: array containing control points
191            [[0, 0], [100, 200], [200, 100]]
192         * @param pos: current be(tween) position represented as float  0 - 1
193         * @return [x, y]
194         */
195         function bezier(points, pos) {
196             var n = points.length,
197             r = [],
198             i,
199             j
200             for (i = 0; i < n; ++i) {
201                 r[i] = [points[i][0], points[i][1]]
202             }
203             for (j = 1; j < n; ++j) {
204                 for (i = 0; i < n - j; ++i) {
205                     r[i][0] = (1 - pos) * r[i][0] + pos * r[parseInt(i + 1, 10)][0]
206                     r[i][1] = (1 - pos) * r[i][1] + pos * r[parseInt(i + 1, 10)][1]
207                 }
208             }
209             return [r[0][0], r[0][1]]
210         }
211 
212         // this gets you the next hex in line according to a 'position'
213         function nextColor(pos, start, finish) {
214             var r = [],
215             i,
216             e,
217             from,
218             to;
219             for (i = 0; i < 6; i++) {
220                 from = Math.min(15, parseInt(start.charAt(i), 16))
221                 to = Math.min(15, parseInt(finish.charAt(i), 16))
222                 e = Math.floor((to - from) * pos + from)
223                 e = e > 15 ? 15 : e < 0 ? 0 : e
224                 r[i] = e.toString(16)
225             }
226             return '#' + r.join('')
227         }
228 
229         // this retreives the frame value within a sequence
230         function getTweenVal(pos, units, begin, end, k, i, v) {
231             if (k == 'transform') {
232                 v = {}
233                 for (var t in begin[i][k]) {
234                     v[t] = (t in end[i][k]) ? Math.round(((end[i][k][t] - begin[i][k][t]) * pos + begin[i][k][t]) * 1000) / 1000 : begin[i][k][t]
235                 }
236                 return v
237             } else if (typeof begin[i][k] == 'string') {
238                 return nextColor(pos, begin[i][k], end[i][k])
239             } else {
240                 // round so we don't get crazy long floats
241                 v = Math.round(((end[i][k] - begin[i][k]) * pos + begin[i][k]) * 1000) / 1000
242                 // some css properties don't require a unit (like zIndex, lineHeight, opacity)
243                 if (! (k in unitless)) v += units[i][k] || 'px'
244                 return v
245             }
246         }
247 
248         // support for relative movement via '+=n' or '-=n'
249         function by(val, start, m, r, i) {
250             return (m = relVal.exec(val)) ? (i = parseFloat(m[2])) && (start + (m[1] == '+' ? 1 : -1) * i) : parseFloat(val)
251         }
252 
253         /**
254         * fx:
255         * @param element(s): HTMLElement(s)
256         * @param options: mixed bag between CSS Style properties & animation options
257         *  - {n} CSS properties|values
258         *     - value can be strings, integers,
259         *     - or callback function that receives element to be animated. method must return value to be tweened
260         *     - relative animations start with += or -= followed by integer
261         *  - duration: time in ms - defaults to 1000(ms)
262         *  - easing: a transition method - defaults to an 'easeOut' algorithm
263         *  - complete: a callback method for when all elements have finished
264         *  - bezier: array of arrays containing x|y coordinates that define the bezier points. defaults to none
265         *     - this may also be a function that receives element to be animated. it must return a value
266         */
267         function animate(elements, options) {
268             var els = elements ? (els = isFinite(elements.length) ? elements: [elements]) : [],
269             i,
270             complete = options.complete,
271             duration = options.duration,
272             ease = options.easing,
273             points = options.bezier,
274             begin = [],
275             end = [],
276             units = [],
277             bez = [],
278             originalLeft,
279             originalTop
280 
281             delete options.complete;
282             delete options.duration;
283             delete options.easing;
284             delete options.bezier;
285 
286             if (points) {
287                 // remember the original values for top|left
288                 originalLeft = options.left;
289                 originalTop = options.top;
290                 delete options.right;
291                 delete options.bottom;
292                 delete options.left;
293                 delete options.top;
294             }
295 
296             for (i = els.length; i--;) {
297 
298                 // record beginning and end states to calculate positions
299                 begin[i] = {}
300                 end[i] = {}
301                 units[i] = {}
302 
303                 // are we 'moving'?
304                 if (points) {
305 
306                     var left = getStyle(els[i], 'left'),
307                     top = getStyle(els[i], 'top'),
308                     xy = [by(fun(originalLeft) ? originalLeft(els[i]) : originalLeft || 0, parseFloat(left)), by(fun(originalTop) ? originalTop(els[i]) : originalTop || 0, parseFloat(top))]
309 
310                     bez[i] = fun(points) ? points(els[i], xy) : points
311                     bez[i].push(xy)
312                     bez[i].unshift([
313                     parseInt(left, 10), parseInt(top, 10)])
314                 }
315 
316                 for (var k in options) {
317                     var v = getStyle(els[i], k),
318                     unit,
319                     tmp = fun(options[k]) ? options[k](els[i]) : options[k]
320                     if (typeof tmp == 'string' && rgbOhex.test(tmp) && !rgbOhex.test(v)) {
321                         delete options[k]; // remove key :(
322                         continue; // cannot animate colors like 'orange' or 'transparent'
323                         // only #xxx, #xxxxxx, rgb(n,n,n)
324                     }
325 
326                     begin[i][k] = k == 'transform' ? parseTransform(v) : typeof tmp == 'string' && rgbOhex.test(tmp) ? toHex(v).slice(1) : parseFloat(v)
327                     end[i][k] = k == 'transform' ? parseTransform(tmp, begin[i][k]) : typeof tmp == 'string' && tmp.charAt(0) == '#' ? toHex(tmp).slice(1) : by(tmp, parseFloat(v));
328                     // record original unit
329                     (typeof tmp == 'string') && (unit = tmp.match(numUnit)) && (units[i][k] = unit[1])
330                 }
331             }
332             // ONE TWEEN TO RULE THEM ALL
333             return tween(duration,
334             function(pos, v, xy) {
335                 // normally not a fan of optimizing for() loops, but we want something
336                 // fast for animating
337                 for (i = els.length; i--;) {
338                     if (points) {
339                         xy = bezier(bez[i], pos)
340                         els[i].style.left = xy[0] + 'px'
341                         els[i].style.top = xy[1] + 'px'
342                     }
343                     for (var k in options) {
344                         v = getTweenVal(pos, units, begin, end, k, i)
345                         k == 'transform' ? els[i].style[transform] = formatTransform(v) : k == 'opacity' && !opasity ? (els[i].style.filter = 'alpha(opacity=' + (v * 100) + ')') : (els[i].style[camelize(k)] = v)
346                     }
347                 }
348             },
349             complete, ease)
350         }
351 
352         // expose static methods
353         animate.tween = tween;
354         animate.getStyle = getStyle;
355         animate.bezier = bezier;
356         animate.transform = transform;
357         animate.parseTransform = parseTransform;
358         animate.formatTransform = formatTransform;
359 
360         if (typeof module !== 'undefined') module.exports = animate;
361         else context['animate'] = animate;
362 
363     } (J.fx || this, document, window)  
364     
365 });
366 /* easings */
367 Jx().$package(function(J){
368     J.fx.easings = J.fx.easings || {};
369     
370     var easings = {
371       easeOut: function (t) {
372         return Math.sin(t * Math.PI / 2);
373       }
374 
375       , easeOutStrong: function (t) {
376         return (t == 1) ? 1 : 1 - Math.pow(2, -10 * t);
377       }
378 
379       , easeIn: function (t) {
380         return t * t;
381       }
382 
383       , easeInStrong: function (t) {
384         return (t == 0) ? 0 : Math.pow(2, 10 * (t - 1));
385       }
386 
387       , easeOutBounce: function(pos) {
388         if ((pos) < (1/2.75)) {
389           return (7.5625*pos*pos);
390         } else if (pos < (2/2.75)) {
391           return (7.5625*(pos-=(1.5/2.75))*pos + .75);
392         } else if (pos < (2.5/2.75)) {
393           return (7.5625*(pos-=(2.25/2.75))*pos + .9375);
394         } else {
395           return (7.5625*(pos-=(2.625/2.75))*pos + .984375);
396         }
397       }
398 
399       , easeInBack: function(pos){
400         var s = 1.70158;
401         return (pos)*pos*((s+1)*pos - s);
402       }
403 
404       , easeOutBack: function(pos){
405         var s = 1.70158;
406         return (pos=pos-1)*pos*((s+1)*pos + s) + 1;
407       }
408 
409       , bounce: function (t) {
410         if (t < (1 / 2.75)) {
411           return 7.5625 * t * t;
412         }
413         if (t < (2 / 2.75)) {
414           return 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75;
415         }
416         if (t < (2.5 / 2.75)) {
417           return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375;
418         }
419         return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375;
420       }
421 
422       , bouncePast: function (pos) {
423         if (pos < (1 / 2.75)) {
424           return (7.5625 * pos * pos);
425         } else if (pos < (2 / 2.75)) {
426           return 2 - (7.5625 * (pos -= (1.5 / 2.75)) * pos + .75);
427         } else if (pos < (2.5 / 2.75)) {
428           return 2 - (7.5625 * (pos -= (2.25 / 2.75)) * pos + .9375);
429         } else {
430           return 2 - (7.5625 * (pos -= (2.625 / 2.75)) * pos + .984375);
431         }
432       }
433 
434       , swingTo: function(pos) {
435         var s = 1.70158;
436         return (pos -= 1) * pos * ((s + 1) * pos + s) + 1;
437       }
438 
439       , swingFrom: function (pos) {
440         var s = 1.70158;
441         return pos * pos * ((s + 1) * pos - s);
442       }
443 
444       , elastic: function(pos) {
445         return -1 * Math.pow(4, -8 * pos) * Math.sin((pos * 6 - 1) * (2 * Math.PI) / 2) + 1;
446       }
447 
448       , spring: function(pos) {
449         return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
450       }
451 
452       , blink: function(pos, blinks) {
453         return Math.round(pos*(blinks||5)) % 2;
454       }
455 
456       , pulse: function(pos, pulses) {
457         return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5;
458       }
459 
460       , wobble: function(pos) {
461         return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
462       }
463 
464       , sinusoidal: function(pos) {
465         return (-Math.cos(pos*Math.PI)/2) + 0.5;
466       }
467 
468       , flicker: function(pos) {
469         var pos = pos + (Math.random()-0.5)/5;
470         return easings.sinusoidal(pos < 0 ? 0 : pos > 1 ? 1 : pos);
471       }
472 
473       , mirror: function(pos) {
474         if (pos < 0.5)
475         return easings.sinusoidal(pos*2);
476         else
477         return easings.sinusoidal(1-(pos-0.5)*2);
478       }
479 
480     };
481         
482     //extend
483     var extend = J.extend || function (target, source) {
484         for (var k in source) {
485             target[k] = source[k];
486         }
487         return target;
488     }
489     extend(J.fx.easings, easings)
490     
491 });
492 
493