jquery.transit.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745
  1. /*!
  2. * jQuery Transit - CSS3 transitions and transformations
  3. * (c) 2011-2014 Rico Sta. Cruz
  4. * MIT Licensed.
  5. *
  6. * http://ricostacruz.com/jquery.transit
  7. * http://github.com/rstacruz/jquery.transit
  8. */
  9. /* jshint expr: true */
  10. ;(function (root, factory) {
  11. if (typeof define === 'function' && define.amd) {
  12. define(['jquery'], factory);
  13. } else if (typeof exports === 'object') {
  14. module.exports = factory(require('jquery'));
  15. } else {
  16. factory(root.jQuery);
  17. }
  18. }(this, function($) {
  19. $.transit = {
  20. version: "0.9.12",
  21. // Map of $.css() keys to values for 'transitionProperty'.
  22. // See https://developer.mozilla.org/en/CSS/CSS_transitions#Properties_that_can_be_animated
  23. propertyMap: {
  24. marginLeft : 'margin',
  25. marginRight : 'margin',
  26. marginBottom : 'margin',
  27. marginTop : 'margin',
  28. paddingLeft : 'padding',
  29. paddingRight : 'padding',
  30. paddingBottom : 'padding',
  31. paddingTop : 'padding'
  32. },
  33. // Will simply transition "instantly" if false
  34. enabled: true,
  35. // Set this to false if you don't want to use the transition end property.
  36. useTransitionEnd: false
  37. };
  38. var div = document.createElement('div');
  39. var support = {};
  40. // Helper function to get the proper vendor property name.
  41. // (`transition` => `WebkitTransition`)
  42. function getVendorPropertyName(prop) {
  43. // Handle unprefixed versions (FF16+, for example)
  44. if (prop in div.style) return prop;
  45. var prefixes = ['Moz', 'Webkit', 'O', 'ms'];
  46. var prop_ = prop.charAt(0).toUpperCase() + prop.substr(1);
  47. for (var i=0; i<prefixes.length; ++i) {
  48. var vendorProp = prefixes[i] + prop_;
  49. if (vendorProp in div.style) { return vendorProp; }
  50. }
  51. }
  52. // Helper function to check if transform3D is supported.
  53. // Should return true for Webkits and Firefox 10+.
  54. function checkTransform3dSupport() {
  55. div.style[support.transform] = '';
  56. div.style[support.transform] = 'rotateY(90deg)';
  57. return div.style[support.transform] !== '';
  58. }
  59. var isChrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
  60. // Check for the browser's transitions support.
  61. support.transition = getVendorPropertyName('transition');
  62. support.transitionDelay = getVendorPropertyName('transitionDelay');
  63. support.transform = getVendorPropertyName('transform');
  64. support.transformOrigin = getVendorPropertyName('transformOrigin');
  65. support.filter = getVendorPropertyName('Filter');
  66. support.transform3d = checkTransform3dSupport();
  67. var eventNames = {
  68. 'transition': 'transitionend',
  69. 'MozTransition': 'transitionend',
  70. 'OTransition': 'oTransitionEnd',
  71. 'WebkitTransition': 'webkitTransitionEnd',
  72. 'msTransition': 'MSTransitionEnd'
  73. };
  74. // Detect the 'transitionend' event needed.
  75. var transitionEnd = support.transitionEnd = eventNames[support.transition] || null;
  76. // Populate jQuery's `$.support` with the vendor prefixes we know.
  77. // As per [jQuery's cssHooks documentation](http://api.jquery.com/jQuery.cssHooks/),
  78. // we set $.support.transition to a string of the actual property name used.
  79. for (var key in support) {
  80. if (support.hasOwnProperty(key) && typeof $.support[key] === 'undefined') {
  81. $.support[key] = support[key];
  82. }
  83. }
  84. // Avoid memory leak in IE.
  85. div = null;
  86. // ## $.cssEase
  87. // List of easing aliases that you can use with `$.fn.transition`.
  88. $.cssEase = {
  89. '_default': 'ease',
  90. 'in': 'ease-in',
  91. 'out': 'ease-out',
  92. 'in-out': 'ease-in-out',
  93. 'snap': 'cubic-bezier(0,1,.5,1)',
  94. // Penner equations
  95. 'easeInCubic': 'cubic-bezier(.550,.055,.675,.190)',
  96. 'easeOutCubic': 'cubic-bezier(.215,.61,.355,1)',
  97. 'easeInOutCubic': 'cubic-bezier(.645,.045,.355,1)',
  98. 'easeInCirc': 'cubic-bezier(.6,.04,.98,.335)',
  99. 'easeOutCirc': 'cubic-bezier(.075,.82,.165,1)',
  100. 'easeInOutCirc': 'cubic-bezier(.785,.135,.15,.86)',
  101. 'easeInExpo': 'cubic-bezier(.95,.05,.795,.035)',
  102. 'easeOutExpo': 'cubic-bezier(.19,1,.22,1)',
  103. 'easeInOutExpo': 'cubic-bezier(1,0,0,1)',
  104. 'easeInQuad': 'cubic-bezier(.55,.085,.68,.53)',
  105. 'easeOutQuad': 'cubic-bezier(.25,.46,.45,.94)',
  106. 'easeInOutQuad': 'cubic-bezier(.455,.03,.515,.955)',
  107. 'easeInQuart': 'cubic-bezier(.895,.03,.685,.22)',
  108. 'easeOutQuart': 'cubic-bezier(.165,.84,.44,1)',
  109. 'easeInOutQuart': 'cubic-bezier(.77,0,.175,1)',
  110. 'easeInQuint': 'cubic-bezier(.755,.05,.855,.06)',
  111. 'easeOutQuint': 'cubic-bezier(.23,1,.32,1)',
  112. 'easeInOutQuint': 'cubic-bezier(.86,0,.07,1)',
  113. 'easeInSine': 'cubic-bezier(.47,0,.745,.715)',
  114. 'easeOutSine': 'cubic-bezier(.39,.575,.565,1)',
  115. 'easeInOutSine': 'cubic-bezier(.445,.05,.55,.95)',
  116. 'easeInBack': 'cubic-bezier(.6,-.28,.735,.045)',
  117. 'easeOutBack': 'cubic-bezier(.175, .885,.32,1.275)',
  118. 'easeInOutBack': 'cubic-bezier(.68,-.55,.265,1.55)'
  119. };
  120. // ## 'transform' CSS hook
  121. // Allows you to use the `transform` property in CSS.
  122. //
  123. // $("#hello").css({ transform: "rotate(90deg)" });
  124. //
  125. // $("#hello").css('transform');
  126. // //=> { rotate: '90deg' }
  127. //
  128. $.cssHooks['transit:transform'] = {
  129. // The getter returns a `Transform` object.
  130. get: function(elem) {
  131. return $(elem).data('transform') || new Transform();
  132. },
  133. // The setter accepts a `Transform` object or a string.
  134. set: function(elem, v) {
  135. var value = v;
  136. if (!(value instanceof Transform)) {
  137. value = new Transform(value);
  138. }
  139. // We've seen the 3D version of Scale() not work in Chrome when the
  140. // element being scaled extends outside of the viewport. Thus, we're
  141. // forcing Chrome to not use the 3d transforms as well. Not sure if
  142. // translate is affectede, but not risking it. Detection code from
  143. // http://davidwalsh.name/detecting-google-chrome-javascript
  144. if (support.transform === 'WebkitTransform' && !isChrome) {
  145. elem.style[support.transform] = value.toString(true);
  146. } else {
  147. elem.style[support.transform] = value.toString();
  148. }
  149. $(elem).data('transform', value);
  150. }
  151. };
  152. // Add a CSS hook for `.css({ transform: '...' })`.
  153. // In jQuery 1.8+, this will intentionally override the default `transform`
  154. // CSS hook so it'll play well with Transit. (see issue #62)
  155. $.cssHooks.transform = {
  156. set: $.cssHooks['transit:transform'].set
  157. };
  158. // ## 'filter' CSS hook
  159. // Allows you to use the `filter` property in CSS.
  160. //
  161. // $("#hello").css({ filter: 'blur(10px)' });
  162. //
  163. $.cssHooks.filter = {
  164. get: function(elem) {
  165. return elem.style[support.filter];
  166. },
  167. set: function(elem, value) {
  168. elem.style[support.filter] = value;
  169. }
  170. };
  171. // jQuery 1.8+ supports prefix-free transitions, so these polyfills will not
  172. // be necessary.
  173. if ($.fn.jquery < "1.8") {
  174. // ## 'transformOrigin' CSS hook
  175. // Allows the use for `transformOrigin` to define where scaling and rotation
  176. // is pivoted.
  177. //
  178. // $("#hello").css({ transformOrigin: '0 0' });
  179. //
  180. $.cssHooks.transformOrigin = {
  181. get: function(elem) {
  182. return elem.style[support.transformOrigin];
  183. },
  184. set: function(elem, value) {
  185. elem.style[support.transformOrigin] = value;
  186. }
  187. };
  188. // ## 'transition' CSS hook
  189. // Allows you to use the `transition` property in CSS.
  190. //
  191. // $("#hello").css({ transition: 'all 0 ease 0' });
  192. //
  193. $.cssHooks.transition = {
  194. get: function(elem) {
  195. return elem.style[support.transition];
  196. },
  197. set: function(elem, value) {
  198. elem.style[support.transition] = value;
  199. }
  200. };
  201. }
  202. // ## Other CSS hooks
  203. // Allows you to rotate, scale and translate.
  204. registerCssHook('scale');
  205. registerCssHook('scaleX');
  206. registerCssHook('scaleY');
  207. registerCssHook('translate');
  208. registerCssHook('rotate');
  209. registerCssHook('rotateX');
  210. registerCssHook('rotateY');
  211. registerCssHook('rotate3d');
  212. registerCssHook('perspective');
  213. registerCssHook('skewX');
  214. registerCssHook('skewY');
  215. registerCssHook('x', true);
  216. registerCssHook('y', true);
  217. // ## Transform class
  218. // This is the main class of a transformation property that powers
  219. // `$.fn.css({ transform: '...' })`.
  220. //
  221. // This is, in essence, a dictionary object with key/values as `-transform`
  222. // properties.
  223. //
  224. // var t = new Transform("rotate(90) scale(4)");
  225. //
  226. // t.rotate //=> "90deg"
  227. // t.scale //=> "4,4"
  228. //
  229. // Setters are accounted for.
  230. //
  231. // t.set('rotate', 4)
  232. // t.rotate //=> "4deg"
  233. //
  234. // Convert it to a CSS string using the `toString()` and `toString(true)` (for WebKit)
  235. // functions.
  236. //
  237. // t.toString() //=> "rotate(90deg) scale(4,4)"
  238. // t.toString(true) //=> "rotate(90deg) scale3d(4,4,0)" (WebKit version)
  239. //
  240. function Transform(str) {
  241. if (typeof str === 'string') { this.parse(str); }
  242. return this;
  243. }
  244. Transform.prototype = {
  245. // ### setFromString()
  246. // Sets a property from a string.
  247. //
  248. // t.setFromString('scale', '2,4');
  249. // // Same as set('scale', '2', '4');
  250. //
  251. setFromString: function(prop, val) {
  252. var args =
  253. (typeof val === 'string') ? val.split(',') :
  254. (val.constructor === Array) ? val :
  255. [ val ];
  256. args.unshift(prop);
  257. Transform.prototype.set.apply(this, args);
  258. },
  259. // ### set()
  260. // Sets a property.
  261. //
  262. // t.set('scale', 2, 4);
  263. //
  264. set: function(prop) {
  265. var args = Array.prototype.slice.apply(arguments, [1]);
  266. if (this.setter[prop]) {
  267. this.setter[prop].apply(this, args);
  268. } else {
  269. this[prop] = args.join(',');
  270. }
  271. },
  272. get: function(prop) {
  273. if (this.getter[prop]) {
  274. return this.getter[prop].apply(this);
  275. } else {
  276. return this[prop] || 0;
  277. }
  278. },
  279. setter: {
  280. // ### rotate
  281. //
  282. // .css({ rotate: 30 })
  283. // .css({ rotate: "30" })
  284. // .css({ rotate: "30deg" })
  285. // .css({ rotate: "30deg" })
  286. //
  287. rotate: function(theta) {
  288. this.rotate = unit(theta, 'deg');
  289. },
  290. rotateX: function(theta) {
  291. this.rotateX = unit(theta, 'deg');
  292. },
  293. rotateY: function(theta) {
  294. this.rotateY = unit(theta, 'deg');
  295. },
  296. // ### scale
  297. //
  298. // .css({ scale: 9 }) //=> "scale(9,9)"
  299. // .css({ scale: '3,2' }) //=> "scale(3,2)"
  300. //
  301. scale: function(x, y) {
  302. if (y === undefined) { y = x; }
  303. this.scale = x + "," + y;
  304. },
  305. // ### skewX + skewY
  306. skewX: function(x) {
  307. this.skewX = unit(x, 'deg');
  308. },
  309. skewY: function(y) {
  310. this.skewY = unit(y, 'deg');
  311. },
  312. // ### perspectvie
  313. perspective: function(dist) {
  314. this.perspective = unit(dist, 'px');
  315. },
  316. // ### x / y
  317. // Translations. Notice how this keeps the other value.
  318. //
  319. // .css({ x: 4 }) //=> "translate(4px, 0)"
  320. // .css({ y: 10 }) //=> "translate(4px, 10px)"
  321. //
  322. x: function(x) {
  323. this.set('translate', x, null);
  324. },
  325. y: function(y) {
  326. this.set('translate', null, y);
  327. },
  328. // ### translate
  329. // Notice how this keeps the other value.
  330. //
  331. // .css({ translate: '2, 5' }) //=> "translate(2px, 5px)"
  332. //
  333. translate: function(x, y) {
  334. if (this._translateX === undefined) { this._translateX = 0; }
  335. if (this._translateY === undefined) { this._translateY = 0; }
  336. if (x !== null && x !== undefined) { this._translateX = unit(x, 'px'); }
  337. if (y !== null && y !== undefined) { this._translateY = unit(y, 'px'); }
  338. this.translate = this._translateX + "," + this._translateY;
  339. }
  340. },
  341. getter: {
  342. x: function() {
  343. return this._translateX || 0;
  344. },
  345. y: function() {
  346. return this._translateY || 0;
  347. },
  348. scale: function() {
  349. var s = (this.scale || "1,1").split(',');
  350. if (s[0]) { s[0] = parseFloat(s[0]); }
  351. if (s[1]) { s[1] = parseFloat(s[1]); }
  352. // "2.5,2.5" => 2.5
  353. // "2.5,1" => [2.5,1]
  354. return (s[0] === s[1]) ? s[0] : s;
  355. },
  356. rotate3d: function() {
  357. var s = (this.rotate3d || "0,0,0,0deg").split(',');
  358. for (var i=0; i<=3; ++i) {
  359. if (s[i]) { s[i] = parseFloat(s[i]); }
  360. }
  361. if (s[3]) { s[3] = unit(s[3], 'deg'); }
  362. return s;
  363. }
  364. },
  365. // ### parse()
  366. // Parses from a string. Called on constructor.
  367. parse: function(str) {
  368. var self = this;
  369. str.replace(/([a-zA-Z0-9]+)\((.*?)\)/g, function(x, prop, val) {
  370. self.setFromString(prop, val);
  371. });
  372. },
  373. // ### toString()
  374. // Converts to a `transition` CSS property string. If `use3d` is given,
  375. // it converts to a `-webkit-transition` CSS property string instead.
  376. toString: function(use3d) {
  377. var re = [];
  378. for (var i in this) {
  379. if (this.hasOwnProperty(i)) {
  380. // Don't use 3D transformations if the browser can't support it.
  381. if ((!support.transform3d) && (
  382. (i === 'rotateX') ||
  383. (i === 'rotateY') ||
  384. (i === 'perspective') ||
  385. (i === 'transformOrigin'))) { continue; }
  386. if (i[0] !== '_') {
  387. if (use3d && (i === 'scale')) {
  388. re.push(i + "3d(" + this[i] + ",1)");
  389. } else if (use3d && (i === 'translate')) {
  390. re.push(i + "3d(" + this[i] + ",0)");
  391. } else {
  392. re.push(i + "(" + this[i] + ")");
  393. }
  394. }
  395. }
  396. }
  397. return re.join(" ");
  398. }
  399. };
  400. function callOrQueue(self, queue, fn) {
  401. if (queue === true) {
  402. self.queue(fn);
  403. } else if (queue) {
  404. self.queue(queue, fn);
  405. } else {
  406. self.each(function () {
  407. fn.call(this);
  408. });
  409. }
  410. }
  411. // ### getProperties(dict)
  412. // Returns properties (for `transition-property`) for dictionary `props`. The
  413. // value of `props` is what you would expect in `$.css(...)`.
  414. function getProperties(props) {
  415. var re = [];
  416. $.each(props, function(key) {
  417. key = $.camelCase(key); // Convert "text-align" => "textAlign"
  418. key = $.transit.propertyMap[key] || $.cssProps[key] || key;
  419. key = uncamel(key); // Convert back to dasherized
  420. // Get vendor specify propertie
  421. if (support[key])
  422. key = uncamel(support[key]);
  423. if ($.inArray(key, re) === -1) { re.push(key); }
  424. });
  425. return re;
  426. }
  427. // ### getTransition()
  428. // Returns the transition string to be used for the `transition` CSS property.
  429. //
  430. // Example:
  431. //
  432. // getTransition({ opacity: 1, rotate: 30 }, 500, 'ease');
  433. // //=> 'opacity 500ms ease, -webkit-transform 500ms ease'
  434. //
  435. function getTransition(properties, duration, easing, delay) {
  436. // Get the CSS properties needed.
  437. var props = getProperties(properties);
  438. // Account for aliases (`in` => `ease-in`).
  439. if ($.cssEase[easing]) { easing = $.cssEase[easing]; }
  440. // Build the duration/easing/delay attributes for it.
  441. var attribs = '' + toMS(duration) + ' ' + easing;
  442. if (parseInt(delay, 10) > 0) { attribs += ' ' + toMS(delay); }
  443. // For more properties, add them this way:
  444. // "margin 200ms ease, padding 200ms ease, ..."
  445. var transitions = [];
  446. $.each(props, function(i, name) {
  447. transitions.push(name + ' ' + attribs);
  448. });
  449. return transitions.join(', ');
  450. }
  451. // ## $.fn.transition
  452. // Works like $.fn.animate(), but uses CSS transitions.
  453. //
  454. // $("...").transition({ opacity: 0.1, scale: 0.3 });
  455. //
  456. // // Specific duration
  457. // $("...").transition({ opacity: 0.1, scale: 0.3 }, 500);
  458. //
  459. // // With duration and easing
  460. // $("...").transition({ opacity: 0.1, scale: 0.3 }, 500, 'in');
  461. //
  462. // // With callback
  463. // $("...").transition({ opacity: 0.1, scale: 0.3 }, function() { ... });
  464. //
  465. // // With everything
  466. // $("...").transition({ opacity: 0.1, scale: 0.3 }, 500, 'in', function() { ... });
  467. //
  468. // // Alternate syntax
  469. // $("...").transition({
  470. // opacity: 0.1,
  471. // duration: 200,
  472. // delay: 40,
  473. // easing: 'in',
  474. // complete: function() { /* ... */ }
  475. // });
  476. //
  477. $.fn.transition = $.fn.transit = function(properties, duration, easing, callback) {
  478. var self = this;
  479. var delay = 0;
  480. var queue = true;
  481. var theseProperties = $.extend(true, {}, properties);
  482. // Account for `.transition(properties, callback)`.
  483. if (typeof duration === 'function') {
  484. callback = duration;
  485. duration = undefined;
  486. }
  487. // Account for `.transition(properties, options)`.
  488. if (typeof duration === 'object') {
  489. easing = duration.easing;
  490. delay = duration.delay || 0;
  491. queue = typeof duration.queue === "undefined" ? true : duration.queue;
  492. callback = duration.complete;
  493. duration = duration.duration;
  494. }
  495. // Account for `.transition(properties, duration, callback)`.
  496. if (typeof easing === 'function') {
  497. callback = easing;
  498. easing = undefined;
  499. }
  500. // Alternate syntax.
  501. if (typeof theseProperties.easing !== 'undefined') {
  502. easing = theseProperties.easing;
  503. delete theseProperties.easing;
  504. }
  505. if (typeof theseProperties.duration !== 'undefined') {
  506. duration = theseProperties.duration;
  507. delete theseProperties.duration;
  508. }
  509. if (typeof theseProperties.complete !== 'undefined') {
  510. callback = theseProperties.complete;
  511. delete theseProperties.complete;
  512. }
  513. if (typeof theseProperties.queue !== 'undefined') {
  514. queue = theseProperties.queue;
  515. delete theseProperties.queue;
  516. }
  517. if (typeof theseProperties.delay !== 'undefined') {
  518. delay = theseProperties.delay;
  519. delete theseProperties.delay;
  520. }
  521. // Set defaults. (`400` duration, `ease` easing)
  522. if (typeof duration === 'undefined') { duration = $.fx.speeds._default; }
  523. if (typeof easing === 'undefined') { easing = $.cssEase._default; }
  524. duration = toMS(duration);
  525. // Build the `transition` property.
  526. var transitionValue = getTransition(theseProperties, duration, easing, delay);
  527. // Compute delay until callback.
  528. // If this becomes 0, don't bother setting the transition property.
  529. var work = $.transit.enabled && support.transition;
  530. var i = work ? (parseInt(duration, 10) + parseInt(delay, 10)) : 0;
  531. // If there's nothing to do...
  532. if (i === 0) {
  533. var fn = function(next) {
  534. self.css(theseProperties);
  535. if (callback) { callback.apply(self); }
  536. if (next) { next(); }
  537. };
  538. callOrQueue(self, queue, fn);
  539. return self;
  540. }
  541. // Save the old transitions of each element so we can restore it later.
  542. var oldTransitions = {};
  543. var run = function(nextCall) {
  544. var bound = false;
  545. // Prepare the callback.
  546. var cb = function() {
  547. if (bound) { self.unbind(transitionEnd, cb); }
  548. if (i > 0) {
  549. self.each(function() {
  550. this.style[support.transition] = (oldTransitions[this] || null);
  551. });
  552. }
  553. if (typeof callback === 'function') { callback.apply(self); }
  554. if (typeof nextCall === 'function') { nextCall(); }
  555. };
  556. if ((i > 0) && (transitionEnd) && ($.transit.useTransitionEnd)) {
  557. // Use the 'transitionend' event if it's available.
  558. bound = true;
  559. self.bind(transitionEnd, cb);
  560. } else {
  561. // Fallback to timers if the 'transitionend' event isn't supported.
  562. window.setTimeout(cb, i);
  563. }
  564. // Apply transitions.
  565. self.each(function() {
  566. if (i > 0) {
  567. this.style[support.transition] = transitionValue;
  568. }
  569. $(this).css(theseProperties);
  570. });
  571. };
  572. // Defer running. This allows the browser to paint any pending CSS it hasn't
  573. // painted yet before doing the transitions.
  574. var deferredRun = function(next) {
  575. this.offsetWidth; // force a repaint
  576. run(next);
  577. };
  578. // Use jQuery's fx queue.
  579. callOrQueue(self, queue, deferredRun);
  580. // Chainability.
  581. return this;
  582. };
  583. function registerCssHook(prop, isPixels) {
  584. // For certain properties, the 'px' should not be implied.
  585. if (!isPixels) { $.cssNumber[prop] = true; }
  586. $.transit.propertyMap[prop] = support.transform;
  587. $.cssHooks[prop] = {
  588. get: function(elem) {
  589. var t = $(elem).css('transit:transform');
  590. return t.get(prop);
  591. },
  592. set: function(elem, value) {
  593. var t = $(elem).css('transit:transform');
  594. t.setFromString(prop, value);
  595. $(elem).css({ 'transit:transform': t });
  596. }
  597. };
  598. }
  599. // ### uncamel(str)
  600. // Converts a camelcase string to a dasherized string.
  601. // (`marginLeft` => `margin-left`)
  602. function uncamel(str) {
  603. return str.replace(/([A-Z])/g, function(letter) { return '-' + letter.toLowerCase(); });
  604. }
  605. // ### unit(number, unit)
  606. // Ensures that number `number` has a unit. If no unit is found, assume the
  607. // default is `unit`.
  608. //
  609. // unit(2, 'px') //=> "2px"
  610. // unit("30deg", 'rad') //=> "30deg"
  611. //
  612. function unit(i, units) {
  613. if ((typeof i === "string") && (!i.match(/^[\-0-9\.]+$/))) {
  614. return i;
  615. } else {
  616. return "" + i + units;
  617. }
  618. }
  619. // ### toMS(duration)
  620. // Converts given `duration` to a millisecond string.
  621. //
  622. // toMS('fast') => $.fx.speeds[i] => "200ms"
  623. // toMS('normal') //=> $.fx.speeds._default => "400ms"
  624. // toMS(10) //=> '10ms'
  625. // toMS('100ms') //=> '100ms'
  626. //
  627. function toMS(duration) {
  628. var i = duration;
  629. // Allow string durations like 'fast' and 'slow', without overriding numeric values.
  630. if (typeof i === 'string' && (!i.match(/^[\-0-9\.]+/))) { i = $.fx.speeds[i] || $.fx.speeds._default; }
  631. return unit(i, 'ms');
  632. }
  633. // Export some functions for testable-ness.
  634. $.transit.getTransitionValue = getTransition;
  635. return $;
  636. }));