PageRenderTime 57ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/jstween/jstween-1.1.js

https://bitbucket.org/puffstream/menu-slider
JavaScript | 1420 lines | 1006 code | 370 blank | 44 comment | 233 complexity | 66227f5e16ecc701bd2f7ddcf3417048 MD5 | raw file
Possible License(s): CC-BY-SA-3.0
  1. /*!
  2. * JSTween JavaScript Library v1.1
  3. * http://www.jstween.org/
  4. *
  5. * Copyright 2011, Marco Wolfsheimer
  6. * JSTween by Marco Wolfsheimer is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
  7. *
  8. * Date: Sun Mar 13 12:46:40 2011 -0000
  9. */
  10. /*
  11. TERMS OF USE - EASING EQUATIONS
  12. Open source under the BSD License.
  13. Copyright © 2001 Robert Penner
  14. All rights reserved.
  15. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
  16. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  17. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  18. Neither the name of the author nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission.
  19. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  20. */
  21. var JSTween = ( function ( that ) {
  22. var __prop = /[\-]{0,1}[0-9\.]{1,}|#[0-9\.abcdef]{3,6}/gi,
  23. __unit = /[pxemtcin%]{1,2}|deg/gi,
  24. __value = /[0-9\.\-]{1,}/gi,
  25. __hexValue = /[0-9a-f]{3,6}/gi,
  26. __hasHash = /^#/,
  27. __singleValue = /^[0-9\.\-]{1,}([pxemtcin%]{1,2}|deg)$/,
  28. __letters = /[a-z]{1,}/,
  29. __hasRGB = /^rgb\(/,
  30. __hasScroll = /^scroll/,
  31. __cssHyphen = /-([a-z])/ig,
  32. __cssMSHyphen = /^-ms/ig,
  33. __cssSuportLookup = {
  34. opacity:[ 'opacity', '-moz-opacity', 'filter' ],
  35. shadow: [ 'box-shadow', '-moz-box-shadow', '-o-box-shadow', '-ms-box-shadow', '-webkit-box-shadow' ],
  36. transform: [ '-moz-transform', 'transform', '-o-transform', '-ms-transform','-webkit-transform' ],
  37. transformOrigin: [ '-moz-transform-origin', 'transform-origin', '-o-transform-origin', '-ms-transform-origin', '-webkit-transform-origin' ],
  38. borderRadius:[ '-moz-border-radius', 'border-radius', '-webkit-border-radius' ],
  39. borderRadiusTopLeft:[ '-moz-border-radius-topleft', 'border-top-left-radius', '-webkit-border-top-left-radius' ],
  40. borderRadiusTopRight:[ '-moz-border-radius-topright', 'border-top-right-radius', '-webkit-border-top-right-radius' ],
  41. borderRadiusBottomLeft:[ '-moz-border-radius-bottomleft', 'border-bottom-left-radius', '-webkit-border-bottom-left-radius' ],
  42. borderRadiusBottomRight:[ '-moz-border-radius-bottomright', 'border-bottom-right-radius', '-webkit-border-bottom-right-radius' ],
  43. backgroundSize: [ 'background-size', '-moz-background-size', '-o-background-size', '-webkit-background-size' ]
  44. },
  45. __timeline = {},
  46. __elements = [],
  47. __frame = 0,
  48. __runTime = 0,
  49. __playing = false,
  50. __playCallback = false,
  51. __frameTime = false,
  52. __playTime = 0,
  53. __config = {},
  54. __mobile = ( /iPad/i.test( navigator.userAgent ) || /iPhone OS/i.test( navigator.userAgent ) );
  55. var init = function () {
  56. framerate( __mobile ? 30 : 45 );
  57. cssSupport();
  58. // Fail nicely if jQuery does not exist
  59. try{ attach(); } catch(e){ return; }
  60. };
  61. var attach = function() {
  62. var fn = jQuery.fn;
  63. jQuery.JSTween = that;
  64. fn.tween = function (options) {
  65. var i, length = this.length;
  66. for( i = 0; i < length; i++ ) {
  67. tween(this[i], options);
  68. }
  69. return this;
  70. };
  71. jQuery.framerate = function (options) {
  72. framerate(options);
  73. };
  74. jQuery.play = function ( callback ) {
  75. play( callback );
  76. };
  77. jQuery.clear = function ( elem, prop ) {
  78. clear( elem, prop );
  79. };
  80. fn.play = function ( callback ) {
  81. play( callback );
  82. return this;
  83. };
  84. fn.clear = function (prop) {
  85. var i, length = this.length;
  86. for( i = 0; i < length; i++ ) {
  87. clear(this[i], prop);
  88. }
  89. return this;
  90. };
  91. fn.property = function (name) {
  92. var prop = [], i, length = this.length;
  93. for( i = 0; i < length; i++ ) {
  94. prop.push( getProperty( this[i], name) );
  95. }
  96. return prop.length === 1 ? prop[0] : prop;
  97. };
  98. fn.opacity = function (value) {
  99. var i, length = this.length;
  100. for( i = 0; i < length; i++ ) {
  101. opacity(this[i], value);
  102. }
  103. return this;
  104. };
  105. fn.alpha = fn.opacity;
  106. fn.transparency = fn.opacity;
  107. fn.rotate = function (value) {
  108. var i, length = this.length;
  109. for( i = 0; i < length; i++ ) {
  110. rotate(this[i], value);
  111. }
  112. return this;
  113. };
  114. fn.action = function (type, value, units, callback) {
  115. var elementID,
  116. prop,
  117. i,
  118. length = this.length,
  119. parsedType = {};
  120. if( typeof type === 'object' ) {
  121. for( prop in type ) {
  122. if( type.hasOwnProperty( prop ) && typeof type[prop] === 'string' ) {
  123. if( __singleValue.test( type[prop] ) ) {
  124. parsedType[prop] = { value: parseFloat( type[prop].match( __value )[0], 10 ), units: type[prop].match( __unit )[0] };
  125. } else {
  126. parsedType[prop] = { value: type[prop], units: undefined };
  127. }
  128. }
  129. }
  130. for( i = 0; i < length; i++ ) {
  131. elementID = jQuery.JSTween.register(this[i]);
  132. for( prop in parsedType ) {
  133. if( parsedType.hasOwnProperty( prop ) ) {
  134. jQuery.JSTween.action( elementID, prop, parsedType[prop].value, parsedType[prop].units, undefined, true);
  135. }
  136. }
  137. }
  138. } else {
  139. for( i = 0; i < length; i++ ) {
  140. action( register(this[i]), type, value, units, callback, true);
  141. }
  142. }
  143. return this;
  144. };
  145. fn.state = function (type) {
  146. if (this.length > 0 ) {
  147. if( this[0].__animate !== undefined ) {
  148. if( type !== undefined && this[0].__animate.state[type] !== undefined ) {
  149. return this[0].__animate.state[type];
  150. } else if( type === undefined ) {
  151. return this[0].__animate.state;
  152. }
  153. }
  154. }
  155. };
  156. fn.transform = function (value) {
  157. var i, length = this.length;
  158. for( i = 0; i < length; i++ ) {
  159. transform(this[i], value);
  160. }
  161. return this;
  162. };
  163. fn.transformOrigin = function (value) {
  164. var i, length = this.length;
  165. for( i = 0; i < length; i++ ) {
  166. transformOrigin(this[i], value);
  167. }
  168. return this;
  169. };
  170. fn.backgroundSize = function (value) {
  171. var i, length = this.length;
  172. for( i = 0; i < length; i++ ) {
  173. backgroundSize(this[i], value);
  174. }
  175. return this;
  176. };
  177. fn.shadow = function (value) {
  178. var i, length = this.length;
  179. for( i = 0; i < length; i++ ) {
  180. shadow(this[i], value);
  181. }
  182. return this;
  183. };
  184. fn.borderRadius = function (value, units) {
  185. var i, length = this.length;
  186. for( i = 0; i < length; i++ ) {
  187. borderRadius(this[i], value, units );
  188. }
  189. return this;
  190. };
  191. fn.borderRadiusTopRight = function (value, units) {
  192. var i, length = this.length;
  193. for( i = 0; i < length; i++ ) {
  194. borderRadiusCorner(this[i], 'top', 'right', value, units);
  195. }
  196. return this;
  197. };
  198. fn.borderRadiusTopLeft = function (value, units) {
  199. var i, length = this.length;
  200. for( i = 0; i < length; i++ ) {
  201. borderRadiusCorner(this[i], 'top', 'left', value, units);
  202. }
  203. return this;
  204. };
  205. fn.borderRadiusBottomRight = function (value, units) {
  206. var i, length = this.length;
  207. for( i = 0; i < length; i++ ) {
  208. borderRadiusCorner(this[i], 'bottom', 'right', value, units);
  209. }
  210. return this;
  211. };
  212. fn.borderRadiusBottomLeft = function (value, units) {
  213. var i, length = this.length;
  214. for( i = 0; i < length; i++ ) {
  215. borderRadiusCorner(this[i], 'bottom', 'left', value, units);
  216. }
  217. return this;
  218. };
  219. fn.borderRadiusCorner = function (top, right, value, units) {
  220. var i, length = this.length;
  221. for( i = 0; i < this.length; i++ ) {
  222. borderRadiusCorner(this[i], top, right, value, units);
  223. }
  224. return this;
  225. };
  226. };
  227. var upperCase = function( all, letter ) {
  228. return letter.toUpperCase();
  229. };
  230. var camelCase = function( string ){
  231. return string.replace( __cssMSHyphen, 'ms' ).replace( __cssHyphen, upperCase );
  232. };
  233. var framerate = function (fps) {
  234. if( !fps ) { return __config.frameRate; }
  235. __config.frameRate = fps || 45;
  236. __config.frameDelay = Math.round(1000 / __config.frameRate);
  237. __config.frameLength = (1 / __config.frameRate);
  238. return __config.frameRate;
  239. };
  240. var cssSupport = function(){
  241. var htmlTag = document.getElementsByTagName('html'),
  242. htmlStyle, propType;
  243. if( htmlTag[0] !== undefined ) {
  244. htmlStyle = htmlTag[0].style;
  245. for( propType in __cssSuportLookup ) {
  246. if ( __cssSuportLookup.hasOwnProperty( propType ) ) {
  247. for( i = 0; i < __cssSuportLookup[ propType ].length; i++ ) {
  248. if( htmlStyle[ __cssSuportLookup[propType][i] ] !== undefined ) {
  249. __cssSuportLookup[propType] = __cssSuportLookup[propType][i];
  250. break;
  251. } else if ( htmlStyle[ camelCase( __cssSuportLookup[propType][i] ) ] !== undefined ) {
  252. __cssSuportLookup[propType] = camelCase( __cssSuportLookup[propType][i] );
  253. break;
  254. }
  255. }
  256. }
  257. }
  258. }
  259. };
  260. var getProperty = function (element, name) {
  261. if( element.__animate !== undefined ) {
  262. if( name === undefined ) {
  263. return element.__animate.state;
  264. } else if ( element.__animate.state[name] ) {
  265. return element.__animate.state[name];
  266. } else {
  267. return false;
  268. }
  269. } else {
  270. return false;
  271. }
  272. };
  273. var getScroll = function (element, property, stop) {
  274. if (element.tagName === undefined && ( element.scroll !== undefined || element.scrollTo !== undefined ) ) {
  275. return $( element ).scrollLeft() + 'px ' + $( element ).scrollTop() + 'px';
  276. } else {
  277. return element.scrollLeft + 'px ' + element.scrollTop + 'px';
  278. }
  279. };
  280. var getComputedStyle = function (element, property, stop) {
  281. var foundValue = getProperty(element, property),
  282. computedStyle, value, units, scroll;
  283. // First, see if we have an animation state value for this property already.. much quicker than attacking the DOM
  284. if (foundValue !== false && !__hasScroll.test(property)) {
  285. return {
  286. value: foundValue.value,
  287. units: element.__animate.state[property].units
  288. };
  289. } else {
  290. // Yes I know.. switch isn't wonderfull.. but a necessary way to keep the coad bloat down and performance up in JSTween.
  291. switch (property) {
  292. case 'transform':
  293. case 'transformOrigin':
  294. case 'shadow':
  295. case 'boxShadow':
  296. case 'backgroundSize':
  297. value = stop;
  298. break;
  299. case 'opacity':
  300. case 'transparency':
  301. case 'alpha':
  302. value = 100;
  303. break;
  304. case 'scrollLeft':
  305. case 'scrollTop':
  306. case 'scroll':
  307. case 'scrollTo':
  308. value = getScroll(element, property, stop);
  309. break;
  310. default:
  311. if (window.getComputedStyle !== undefined) {
  312. computedStyle = window.getComputedStyle(element, null)[property];
  313. } else if (element.currentStyle !== undefined) {
  314. computedStyle = element.currentStyle[property];
  315. }
  316. if (computedStyle === 'auto' || computedStyle === undefined || computedStyle === '') {
  317. value = 0;
  318. units = 'px';
  319. } else if( __hasRGB.test( computedStyle ) ) {
  320. value = convertRGBToHex( computedStyle );
  321. } else {
  322. value = parseFloat(computedStyle.match( __value ), 10);
  323. units = computedStyle.match( __unit );
  324. }
  325. break;
  326. }
  327. return {
  328. value: value,
  329. units: units
  330. };
  331. }
  332. };
  333. var parseOptions = function (element, options) {
  334. var newOptions = {},
  335. property, computedStyle;
  336. for (property in options) {
  337. if (options.hasOwnProperty(property) && property !== 'onStart' && property !== 'onStop' && property !== 'onFrame') {
  338. newOptions[property] = {};
  339. // Find missing start values if needed
  340. if (options[property].start === undefined) {
  341. computedStyle = getComputedStyle(element, property, options[property].stop);
  342. newOptions[property].start = computedStyle.value;
  343. } else {
  344. newOptions[property].start = parseProperty(options[property].start);
  345. }
  346. newOptions[property].stop = parseProperty(options[property].stop, 1);
  347. newOptions[property].duration = parseProperty(options[property].duration || newOptions[property].dur, 1);
  348. newOptions[property].time = parseProperty(options[property].time, 0);
  349. newOptions[property].merge = parseProperty(options[property].merge, false);
  350. newOptions[property].effect = parseProperty(options[property].effect, 'linear');
  351. newOptions[property].framerate = parseProperty(options[property].framerate, __config.frameRate);
  352. newOptions[property].units = parseProperty(options[property].units, computedStyle ? computedStyle.units : 'px');
  353. newOptions[property].end = parseProperty(options[property].end, (newOptions[property].time + newOptions[property].duration));
  354. // Clean up scolling metrics and turn them into paired strings
  355. if ( __hasScroll.test( property ) ) {
  356. if (typeof newOptions[property].start === 'number') {
  357. newOptions[property].start = newOptions[property].start + 'px ' + newOptions[property].start + 'px';
  358. }
  359. if (typeof newOptions[property].stop === 'number') {
  360. newOptions[property].stop = newOptions[property].stop + 'px ' + newOptions[property].stop + 'px';
  361. }
  362. }
  363. newOptions[property].callback = {
  364. onStart: options[property].onStart,
  365. onFrame: options[property].onFrame,
  366. onStop: options[property].onStop
  367. };
  368. }
  369. }
  370. return newOptions;
  371. };
  372. var parseProperty = function (property, defaultValue) {
  373. if (typeof property === 'function') {
  374. return property();
  375. } else if (property !== undefined) {
  376. return property;
  377. } else {
  378. return defaultValue;
  379. }
  380. };
  381. var convertRGBToHex = function (color) {
  382. var colours = color.match(__value), red, green, blue;
  383. red = parseInt( colours[0], 10 ).toString(16);
  384. if (red.length === 1) { red = "0" + red; }
  385. green = parseInt( colours[1], 10 ).toString(16);
  386. if (green.length === 1) { green = "0" + green; }
  387. blue = parseInt( colours[2], 10 ).toString(16);
  388. if (blue.length === 1) { blue = "0" + blue; }
  389. return '#' + red + green + blue;
  390. };
  391. var parseColor = function (color) {
  392. if (color.length === 3) {
  393. return [parseInt(color.substr(0, 1), 16) * 16, parseInt(color.substr(1, 1), 16) * 16, parseInt(color.substr(2, 1), 16) * 16];
  394. } else {
  395. return [parseInt(color.substr(0, 2), 16), parseInt(color.substr(2, 2), 16), parseInt(color.substr(4, 2), 16)];
  396. }
  397. };
  398. var parseCSSProperty = function (str) {
  399. var values = str.match( __prop ),
  400. delimiters = str.split( __prop ),
  401. units = [],
  402. id,
  403. length = values.length;
  404. for (id = 0; id < length; id++) {
  405. if (__hasHash.test( values[id])) {
  406. values[id] = parseColor(values[id].match( __hexValue )[0]);
  407. } else {
  408. values[id] = parseFloat(values[id].match(__value)[0], 10);
  409. }
  410. }
  411. return {
  412. value: values,
  413. delimiter: delimiters
  414. };
  415. };
  416. var mergeStringProperty = function (start, stop, property, options, time, end) {
  417. var frameProperty = "",
  418. color = "",
  419. i, n,
  420. length = start.value.length,
  421. startLength = 0;
  422. for (i = 0; i < length; i++) {
  423. if (typeof start.value[i] === 'object' && start.value[i].length !== undefined) {
  424. frameProperty += start.delimiter[i] + "#";
  425. startLength = start.value[i].length;
  426. for (n = 0; n < startLength; n++) {
  427. color = Math.round(effects[options.effect]((time - options.time), start.value[i][n], (stop.value[i][n] - start.value[i][n]), (end - options.time)), 10).toString(16);
  428. if (color.length === 1) {
  429. color = "0" + color;
  430. }
  431. frameProperty += color;
  432. }
  433. } else {
  434. frameProperty += start.delimiter[i] + effects[options.effect]((time - options.time), start.value[i], (stop.value[i] - start.value[i]), (end - options.time));
  435. }
  436. }
  437. return frameProperty + start.delimiter[start.delimiter.length - 1];
  438. };
  439. var loopStringFrames = function( elementID, property, propertyOptions ){
  440. var frameCounter,
  441. startParsed = parseCSSProperty(propertyOptions.start),
  442. stopParsed = parseCSSProperty(propertyOptions.stop),
  443. time,
  444. offset,
  445. frameValue,
  446. frameSkip,
  447. frameLength = __config.frameLength,
  448. end = propertyOptions.end;
  449. frameCounter = frameSkip = Math.round(__config.frameRate / propertyOptions.framerate - 1);
  450. for ( time = propertyOptions.time; time < end; time += frameLength) {
  451. offset = __frame + Math.round(time * __config.frameRate);
  452. if (frameCounter === 0) {
  453. frameValue = mergeStringProperty(startParsed, stopParsed, property, propertyOptions, time, propertyOptions.end);
  454. makeFrame(offset, elementID, property, frameValue, propertyOptions.units, false, false);
  455. frameCounter = frameSkip;
  456. } else {
  457. makeFrame(offset, elementID, property);
  458. frameCounter--;
  459. }
  460. }
  461. // Final frame, make sure the element lands in the correct place
  462. offset = __frame + (Math.round(propertyOptions.end * __config.frameRate));
  463. makeFrame(offset, elementID, property, propertyOptions.stop, propertyOptions.units, false, true);
  464. };
  465. var loopFrames = function( elementID, property, propertyOptions ){
  466. var frameCounter,
  467. time,
  468. offset,
  469. frameValue,
  470. frameSkip,
  471. frameLength = __config.frameLength,
  472. end = propertyOptions.end;
  473. frameCounter = frameSkip = Math.round(__config.frameRate / propertyOptions.framerate - 1);
  474. for ( time = propertyOptions.time; time < end; time += frameLength) {
  475. offset = __frame + (Math.round(time * __config.frameRate));
  476. if (frameCounter === 0) {
  477. frameValue = effects[propertyOptions.effect]((time - propertyOptions.time), propertyOptions.start, (propertyOptions.stop - propertyOptions.start), (propertyOptions.end - propertyOptions.time));
  478. makeFrame(offset, elementID, property, frameValue, propertyOptions.units, false, false);
  479. frameCounter = frameSkip;
  480. } else {
  481. makeFrame(offset, elementID, property);
  482. frameCounter--;
  483. }
  484. }
  485. // Final frame, make sure the element lands in the correct place
  486. offset = __frame + (Math.round(propertyOptions.end * __config.frameRate));
  487. makeFrame(offset, elementID, property, propertyOptions.stop, propertyOptions.units, false, true);
  488. };
  489. var getTimeBounds = function( options ) {
  490. var bounds = { start:0, stop:0 }, property;
  491. for (property in options) {
  492. if (options.hasOwnProperty(property)) {
  493. if( options[property].end > bounds.stop ) { bounds.stop = options[property].end; }
  494. }
  495. }
  496. bounds.start = bounds.stop;
  497. for (property in options) {
  498. if (options.hasOwnProperty(property)) {
  499. if( options[property].time < bounds.start ) { bounds.start = options[property].time; }
  500. }
  501. }
  502. return bounds;
  503. };
  504. var tween = function (element, config) {
  505. var elementID = register(element),
  506. offset = 0,
  507. time = 0,
  508. startParsed, stopParsed, frameSkip = 0,
  509. options = parseOptions(element, config),
  510. property,
  511. bounds = getTimeBounds( options );
  512. for (property in options) {
  513. if (options.hasOwnProperty(property)) {
  514. // Make property frames
  515. if (typeof options[property].start === 'string') {
  516. loopStringFrames( elementID, property, options[property] );
  517. } else {
  518. loopFrames( elementID, property, options[property] );
  519. }
  520. // PROPERTY CALLBACKS
  521. // onStart
  522. if (typeof options[property].callback.onStart === 'function') {
  523. addCallback(__frame + (Math.round(options[property].time * __config.frameRate)), elementID, property, options[property].callback.onStart);
  524. }
  525. // onFrame
  526. if (typeof options[property].callback.onFrame === 'function') {
  527. for (time = options[property].time; time < options[property].end; time += __config.frameLength) {
  528. offset = __frame + (Math.round(time * __config.frameRate));
  529. addCallback(offset, elementID, property, options[property].callback.onFrame);
  530. }
  531. }
  532. // onStop
  533. if (typeof options[property].callback.onStop === 'function') {
  534. addCallback(__frame + (Math.round(options[property].end * __config.frameRate)), elementID, property, options[property].callback.onStop);
  535. }
  536. // CLEANUP
  537. // Get the offset and increase the current runtime if needed
  538. offset = __frame + (Math.round(options[property].end * __config.frameRate));
  539. // Clean up
  540. if (offset > __runTime) {
  541. __runTime = offset;
  542. }
  543. }
  544. }
  545. if (typeof config.onStart === 'function') {
  546. addCallback(__frame + (Math.round(bounds.start * __config.frameRate)), elementID, 'callback', config.onStart);
  547. }
  548. if (typeof config.onFrame === 'function') {
  549. for (frame = __frame + Math.round(bounds.start * __config.frameRate); frame <= __frame + Math.round(bounds.stop * __config.frameRate); frame++) {
  550. addCallback(frame, elementID, 'callback', config.onFrame);
  551. }
  552. }
  553. if (typeof config.onStop === 'function') {
  554. addCallback(__frame + (Math.round(bounds.stop * __config.frameRate)), elementID, 'callback', config.onStop);
  555. }
  556. };
  557. var makeFrame = function (offset, elementID, type, value, units, callback, skip) {
  558. /// Wow this is long winded.. but we need to check for existing frames, properties and elements. IT COULD be abstracted out into smaller methods but that would have a performance hit
  559. if (elementID !== undefined) {
  560. if (__timeline[offset] === undefined) {
  561. __timeline[offset] = {};
  562. __timeline[offset][elementID] = {};
  563. __timeline[offset][elementID][type] = {
  564. value: value,
  565. units: units,
  566. callback: [],
  567. skip: skip
  568. };
  569. } else if (__timeline[offset][elementID] === undefined) {
  570. __timeline[offset][elementID] = {};
  571. __timeline[offset][elementID][type] = {
  572. value: value,
  573. units: units,
  574. callback: [],
  575. skip: skip
  576. };
  577. } else if (__timeline[offset][elementID][type] === undefined) {
  578. __timeline[offset][elementID][type] = {
  579. value: value,
  580. units: units,
  581. callback: [],
  582. skip: skip
  583. };
  584. } else {
  585. if (value !== false) {
  586. __timeline[offset][elementID][type].value = value;
  587. }
  588. if (units !== false) {
  589. __timeline[offset][elementID][type].units = units;
  590. }
  591. __timeline[offset][elementID][type].skip = skip;
  592. }
  593. if (typeof callback === 'function') {
  594. __timeline[offset][elementID][type].callback.push(callback);
  595. }
  596. } else if (__timeline[offset] === undefined) {
  597. __timeline[offset] = {};
  598. }
  599. };
  600. var addCallback = function (offset, elementID, type, callback) {
  601. makeFrame(offset, elementID, type, false, false, callback, true);
  602. };
  603. var play = function ( callback ) {
  604. if (__playing === false) {
  605. __frameTime = false;
  606. __playing = true;
  607. __playTime = timestamp();
  608. __playCallback = callback;
  609. playHead();
  610. }
  611. };
  612. var clear = function (element, property) {
  613. var time;
  614. if( element !== undefined && property !== undefined && element.__animate !== undefined ) {
  615. for (time in __timeline) {
  616. if (__timeline.hasOwnProperty(time) && __timeline[time][element.__animate.id] !== undefined && __timeline[time][element.__animate.id][property] !== undefined) {
  617. delete __timeline[time][element.__animate.id][property];
  618. }
  619. }
  620. } else if( element !== undefined && element.__animate !== undefined ) {
  621. for (time in __timeline) {
  622. if (__timeline.hasOwnProperty(time) && __timeline[time][element.__animate.id] !== undefined) {
  623. delete __timeline[time][element.__animate.id];
  624. }
  625. }
  626. } else {
  627. for (time in __timeline) {
  628. if (__timeline.hasOwnProperty(time)) {
  629. delete __timeline[time];
  630. }
  631. }
  632. }
  633. };
  634. var timestamp = function () {
  635. var now = new Date();
  636. return now.getTime();
  637. };
  638. var playHead = function () {
  639. var current, elementID, type, delay;
  640. if (__frame <= __runTime ) {
  641. delay = (__config.frameDelay - ((timestamp() - __playTime) - ( __frame * __config.frameDelay)));
  642. if (delay < 0) {
  643. delay = 0;
  644. } else if (delay > __config.frameDelay) {
  645. delay = __config.frameDelay;
  646. }
  647. setTimeout(function () {
  648. playHead(delay ? true : false);
  649. }, delay);
  650. for (elementID in __timeline[__frame]) {
  651. if (__timeline[__frame].hasOwnProperty(elementID)) {
  652. current = __timeline[__frame][elementID];
  653. for (type in current) {
  654. if (current.hasOwnProperty(type)) {
  655. action(elementID, type, current[type].value, current[type].units, current[type].callback, ( current[type].skip === true ? true : ( delay ? true : false ) ) );
  656. }
  657. }
  658. }
  659. }
  660. delete __timeline[__frame];
  661. __frame++;
  662. __frameTime = timestamp();
  663. } else {
  664. __frameTime = __playing = false;
  665. __frame = 0;
  666. if( typeof __playCallback === 'function' ) {
  667. __playCallback();
  668. __playCallback = false;
  669. }
  670. }
  671. };
  672. var action = function (elementID, type, value, units, callback, updateDOM ) {
  673. // Always render the last frame / property for this element
  674. var prop = __elements[elementID].__animate.state[type];
  675. if (updateDOM === true && value !== false && ( prop === undefined || ( prop.value != value ||prop.units != units ) ) ) {
  676. // Again.. the switch of the century.. I don't like this pattern but the altenative is even nastier
  677. switch (type) {
  678. case "zIndex":
  679. __elements[elementID].style.zIndex = value;
  680. break;
  681. case "alpha":
  682. case "transparency":
  683. case "opacity":
  684. opacity(__elements[elementID], value);
  685. break;
  686. case "scroll":
  687. case "scrollTop":
  688. case "scrollLeft":
  689. case 'scrollTo':
  690. scroll(__elements[elementID], type, value);
  691. break;
  692. case 'shadow':
  693. case 'boxShadow':
  694. shadow(__elements[elementID], value);
  695. break;
  696. case 'rotate':
  697. rotate(__elements[elementID], value);
  698. break;
  699. case 'transformOrigin':
  700. transformOrigin(__elements[elementID], value);
  701. break;
  702. case 'transform':
  703. transform(__elements[elementID], value);
  704. break;
  705. case 'backgroundSize':
  706. backgroundSize(__elements[elementID], value);
  707. break;
  708. case 'borderRadius':
  709. borderRadius(__elements[elementID], value, units);
  710. break;
  711. case 'borderRadiusTopRight':
  712. borderRadiusCorner(__elements[elementID], 'top', 'right', value, units);
  713. break;
  714. case 'borderRadiusTopLeft':
  715. borderRadiusCorner(__elements[elementID], 'top', 'left', value, units);
  716. break;
  717. case 'borderRadiusBottomRight':
  718. borderRadiusCorner(__elements[elementID], 'bottom', 'right', value, units);
  719. break;
  720. case 'borderRadiusBottomLeft':
  721. borderRadiusCorner(__elements[elementID], 'bottom', 'left', value, units);
  722. break;
  723. default:
  724. if (typeof value === 'string') {
  725. __elements[elementID].style[type] = value;
  726. } else {
  727. __elements[elementID].style[type] = value + units;
  728. }
  729. break;
  730. }
  731. }
  732. __elements[elementID].__animate.state[type] = {
  733. value: value,
  734. units: units
  735. };
  736. if (callback !== undefined && callback.length > 0) {
  737. for (i = 0; i < callback.length; i++) {
  738. if (typeof callback[i] === 'function') {
  739. callback[i](__elements[elementID], {
  740. type: type,
  741. value: value,
  742. units: units,
  743. id: elementID
  744. });
  745. }
  746. }
  747. }
  748. };
  749. var scroll = function (element, property, value) {
  750. var parsedValue;
  751. if (element.tagName === undefined && ( typeof element.scroll === 'function' || typeof element.scrollTo === 'function' ) && typeof value === 'string') {
  752. parsedValue = value.match(__value);
  753. if (parsedValue) {
  754. if (self.pageYOffset) {
  755. window.scroll(parseInt(parsedValue[0], 10), parseInt(parsedValue[1], 10));
  756. } else if (document.documentElement && document.documentElement.scrollTop) {
  757. window.scrollTo(parseInt(parsedValue[0], 10), parseInt(parsedValue[1], 10));
  758. } else if (document.body) {
  759. window.scrollTo(parseInt(parsedValue[0], 10), parseInt(parsedValue[1], 10));
  760. }
  761. }
  762. } else {
  763. if (typeof value === 'string') {
  764. parsedValue = value.match(__value);
  765. } else {
  766. parsedValue = [value, value];
  767. }
  768. if (property === 'scrollTop') {
  769. element.scrollTop = parseInt(parsedValue[1], 10);
  770. } else if (property === 'scrollLeft') {
  771. element.scrollLeft = parseInt(parsedValue[0], 10);
  772. } else {
  773. element.scrollLeft = parseInt(parsedValue[0], 10);
  774. element.scrollTop = parseInt(parsedValue[1], 10);
  775. }
  776. }
  777. };
  778. var setProperty = function( element, prop, value, units ) {
  779. element.style[prop] = value + ( units ? units : '');
  780. };
  781. var opacity = function (element, value) {
  782. if( __cssSuportLookup.opacity === 'filter' ) {
  783. setProperty(element, 'filter', 'alpha(opacity=' + value + ')');
  784. } else {
  785. setProperty(element, __cssSuportLookup.opacity, (value / 100) );
  786. }
  787. };
  788. var shadow = function (element, value) {
  789. setProperty(element, __cssSuportLookup.shadow, value);
  790. };
  791. var rotate = function (element, value) {
  792. setProperty(element, __cssSuportLookup.transform, 'rotate(' + value + 'deg)');
  793. };
  794. var transform = function (element, value) {
  795. setProperty(element, __cssSuportLookup.transform, value);
  796. };
  797. var backgroundSize = function (element, value) {
  798. setProperty(element, __cssSuportLookup.backgroundSize, value);
  799. };
  800. var transformOrigin = function (element, value) {
  801. setProperty(element, __cssSuportLookup.transformOrigin, value);
  802. };
  803. var borderRadius = function (element, value, units) {
  804. setProperty(element, __cssSuportLookup.borderRadius, value, units);
  805. };
  806. var borderRadiusCorner = function (element, upDown, leftRight, value, units) {
  807. if( upDown === 'top' ) {
  808. if( leftRight === 'left' ) {
  809. setProperty(element, __cssSuportLookup.borderRadiusTopLeft, value, units);
  810. } else {
  811. setProperty(element, __cssSuportLookup.borderRadiusTopRight, value, units);
  812. }
  813. } else {
  814. if( leftRight === 'left' ) {
  815. setProperty(element, __cssSuportLookup.borderRadiusBottomLeft, value, units);
  816. } else {
  817. setProperty(element, __cssSuportLookup.borderRadiusBottomRight, value, units);
  818. }
  819. }
  820. };
  821. var register = function (element) {
  822. if (element.__animate === undefined) {
  823. var elementID = __elements.length;
  824. element.__animate = {
  825. id: elementID,
  826. state: {},
  827. callback: {},
  828. dragging: false
  829. };
  830. __elements.push(element);
  831. return elementID;
  832. } else {
  833. return element.__animate.id;
  834. }
  835. };
  836. var effects = {
  837. linear: function (t, b, c, d) {
  838. return c * t / d + b;
  839. },
  840. quadIn: function (t, b, c, d) {
  841. return c * (t /= d) * t + b;
  842. },
  843. quadOut: function (t, b, c, d) {
  844. return -c * (t /= d) * (t - 2) + b;
  845. },
  846. quadInOut: function (t, b, c, d) {
  847. if ((t /= d / 2) < 1) {
  848. return c / 2 * t * t + b;
  849. }
  850. return -c / 2 * ((--t) * (t - 2) - 1) + b;
  851. },
  852. cubicIn: function (t, b, c, d) {
  853. return c * (t /= d) * t * t + b;
  854. },
  855. cubicOut: function (t, b, c, d) {
  856. return c * ((t = t / d - 1) * t * t + 1) + b;
  857. },
  858. cubicInOut: function (t, b, c, d) {
  859. if ((t /= d / 2) < 1) {
  860. return c / 2 * t * t * t + b;
  861. }
  862. return c / 2 * ((t -= 2) * t * t + 2) + b;
  863. },
  864. // Copy of cubic
  865. easeIn: function (t, b, c, d) {
  866. return c * (t /= d) * t * t + b;
  867. },
  868. easeOut: function (t, b, c, d) {
  869. return c * ((t = t / d - 1) * t * t + 1) + b;
  870. },
  871. easeInOut: function (t, b, c, d) {
  872. if ((t /= d / 2) < 1) {
  873. return c / 2 * t * t * t + b;
  874. }
  875. return c / 2 * ((t -= 2) * t * t + 2) + b;
  876. },
  877. // End copy
  878. quartIn: function (t, b, c, d) {
  879. return c * (t /= d) * t * t * t + b;
  880. },
  881. quartOut: function (t, b, c, d) {
  882. return -c * ((t = t / d - 1) * t * t * t - 1) + b;
  883. },
  884. quartInOut: function (t, b, c, d) {
  885. if ((t /= d / 2) < 1) {
  886. return c / 2 * t * t * t * t + b;
  887. }
  888. return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
  889. },
  890. quintIn: function (t, b, c, d) {
  891. return c * (t /= d) * t * t * t * t + b;
  892. },
  893. quintOut: function (t, b, c, d) {
  894. return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
  895. },
  896. quintInOut: function (t, b, c, d) {
  897. if ((t /= d / 2) < 1) {
  898. return c / 2 * t * t * t * t * t + b;
  899. }
  900. return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
  901. },
  902. sineIn: function (t, b, c, d) {
  903. return -c * Math.cos(t / d * (Math.PI / 2)) + c + b;
  904. },
  905. sineOut: function (t, b, c, d) {
  906. return c * Math.sin(t / d * (Math.PI / 2)) + b;
  907. },
  908. sineInOut: function (t, b, c, d) {
  909. return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b;
  910. },
  911. expoIn: function (t, b, c, d) {
  912. return (t === 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b;
  913. },
  914. expoOut: function (t, b, c, d) {
  915. return (t === d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;
  916. },
  917. expoInOut: function (t, b, c, d) {
  918. if (t === 0) { return b; }
  919. if (t === d) { return b + c; }
  920. if ((t /= d / 2) < 1) {
  921. return c / 2 * Math.pow(2, 10 * (t - 1)) + b;
  922. }
  923. return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b;
  924. },
  925. circIn: function (t, b, c, d) {
  926. return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b;
  927. },
  928. circOut: function (t, b, c, d) {
  929. return c * Math.sqrt(1 - (t = t / d - 1) * t) + b;
  930. },
  931. circInOut: function (t, b, c, d) {
  932. if ((t /= d / 2) < 1) {
  933. return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b;
  934. }
  935. return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b;
  936. },
  937. bounceIn: function (t, b, c, d) {
  938. return c - effects.bounceOut(d - t, 0, c, d) + b;
  939. },
  940. bounceOut: function (t, b, c, d) {
  941. if ((t /= d) < (1 / 2.75)) {
  942. return c * (7.5625 * t * t) + b;
  943. } else
  944. if (t < (2 / 2.75)) {
  945. return c * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75) + b;
  946. } else
  947. if (t < (2.5 / 2.75)) {
  948. return c * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375) + b;
  949. } else {
  950. return c * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375) + b;
  951. }
  952. },
  953. bounceInOut: function (t, b, c, d) {
  954. if (t < d / 2) {
  955. return effects.bounceIn(t * 2, 0, c, d) * 0.5 + b;
  956. }
  957. return effects.bounceOut(t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b;
  958. },
  959. elasticIn: function (t, b, c, d, a, p) {
  960. if (t === 0) { return b; }
  961. if ((t /= d) === 1) {
  962. return b + c;
  963. }
  964. if (!p) {
  965. p = d * 0.3;
  966. }
  967. if (!a) {
  968. a = 1;
  969. }
  970. var s = 0;
  971. if (a < Math.abs(c)) {
  972. a = c;
  973. s = p / 4;
  974. } else {
  975. s = p / (2 * Math.PI) * Math.asin(c / a);
  976. }
  977. return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
  978. },
  979. elasticOut: function (t, b, c, d, a, p) {
  980. if (t === 0) {
  981. return b;
  982. }
  983. if ((t /= d) === 1) {
  984. return b + c;
  985. }
  986. if (!p) {
  987. p = d * 0.3;
  988. }
  989. if (!a) {
  990. a = 1;
  991. }
  992. var s = 0;
  993. if (a < Math.abs(c)) {
  994. a = c;
  995. s = p / 4;
  996. } else {
  997. s = p / (2 * Math.PI) * Math.asin(c / a);
  998. }
  999. return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
  1000. },
  1001. elasticInOut: function (t, b, c, d, a, p) {
  1002. if (t === 0) {
  1003. return b;
  1004. }
  1005. if ((t /= d / 2) === 2) {
  1006. return b + c;
  1007. }
  1008. if (!p) {
  1009. p = d * (0.3 * 1.5);
  1010. }
  1011. if (!a) {
  1012. a = 1;
  1013. }
  1014. var s = 0;
  1015. if (a < Math.abs(c)) {
  1016. a = c;
  1017. s = p / 4;
  1018. } else {
  1019. s = p / (2 * Math.PI) * Math.asin(c / a);
  1020. }
  1021. if (t < 1) {
  1022. return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
  1023. }
  1024. return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p) * 0.5 + c + b;
  1025. }
  1026. };
  1027. that.tween = tween;
  1028. that.action = action;
  1029. that.register = register;
  1030. that.shadow = shadow;
  1031. that.opacity = opacity;
  1032. that.borderRadius = borderRadius;
  1033. that.borderRadiusCorner = borderRadiusCorner;
  1034. that.backgroundSize = backgroundSize;
  1035. that.transformOrigin = transformOrigin;
  1036. that.rotate = rotate;
  1037. that.transform = transform;
  1038. that.clear = clear;
  1039. that.play = play;
  1040. that.property = getProperty;
  1041. that.getScroll = getScroll;
  1042. that.scroll = scroll;
  1043. that.framerate = framerate;
  1044. init();
  1045. return that;
  1046. }( JSTween || {} ) );