initial commit
[map.git] / honeymap-master / static / extern / jquery-transit.js
1 /*!
2  * jQuery Transit - CSS3 transitions and transformations
3  * Copyright(c) 2011 Rico Sta. Cruz <rico@ricostacruz.com>
4  * MIT Licensed.
5  *
6  * http://ricostacruz.com/jquery.transit
7  * http://github.com/rstacruz/jquery.transit
8  */
9
10 (function($) {
11   "use strict";
12
13   $.transit = {
14     version: "0.1.3",
15
16     // Map of $.css() keys to values for 'transitionProperty'.
17     // See https://developer.mozilla.org/en/CSS/CSS_transitions#Properties_that_can_be_animated
18     propertyMap: {
19       marginLeft    : 'margin',
20       marginRight   : 'margin',
21       marginBottom  : 'margin',
22       marginTop     : 'margin',
23       paddingLeft   : 'padding',
24       paddingRight  : 'padding',
25       paddingBottom : 'padding',
26       paddingTop    : 'padding'
27     },
28
29     // Will simply transition "instantly" if false
30     enabled: true,
31
32     // Set this to false if you don't want to use the transition end property.
33     useTransitionEnd: false
34   };
35
36   var div = document.createElement('div');
37   var support = {};
38
39   // Helper function to get the proper vendor property name.
40   // (`transition` => `WebkitTransition`)
41   function getVendorPropertyName(prop) {
42     var prefixes = ['Moz', 'Webkit', 'O', 'ms'];
43     var prop_ = prop.charAt(0).toUpperCase() + prop.substr(1);
44
45     if (prop in div.style) { return prop; }
46
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
53   /*
54   * matchMedia() polyfill - test whether a CSS media type or media query applies
55   * primary author: Scott Jehl
56   * Copyright (c) 2010 Filament Group, Inc
57   * MIT license
58   * adapted by Paul Irish to use the matchMedia API
59   * http://dev.w3.org/csswg/cssom-view/#dom-window-matchmedia
60   * which webkit now supports: http://trac.webkit.org/changeset/72552
61   *
62   * Doesn't implement media.type as there's no way for crossbrowser property
63   * getters. instead of media.type == 'tv' just use media.matchMedium('tv')
64   */
65   if ( !(window.matchMedia) ){
66     window.matchMedia = (function(doc,undefined){
67
68       var cache = {},
69           docElem = doc.documentElement,
70           fakeBody = doc.createElement('body'),
71           testDiv = doc.createElement('div');
72
73       testDiv.setAttribute('id','ejs-qtest');
74       fakeBody.appendChild(testDiv);
75
76       return function(q){
77         if (cache[q] === undefined) {
78           var styleBlock = doc.createElement('style'),
79             cssrule = '@media '+q+' { #ejs-qtest { position: absolute; } }';
80           //must set type for IE!
81           styleBlock.type = "text/css";
82           if (styleBlock.styleSheet){
83             styleBlock.styleSheet.cssText = cssrule;
84           }
85           else {
86             styleBlock.appendChild(doc.createTextNode(cssrule));
87           }
88           docElem.insertBefore(fakeBody, docElem.firstChild);
89           docElem.insertBefore(styleBlock, docElem.firstChild);
90           cache[q] = ((window.getComputedStyle ? window.getComputedStyle(testDiv,null) : testDiv.currentStyle)['position'] == 'absolute');
91           docElem.removeChild(fakeBody);
92           docElem.removeChild(styleBlock);
93         }
94         return cache[q];
95       };
96
97     })(document);
98   }
99
100   // Helper function to check if transform3D is supported.
101   // Should return true for Webkits and Firefox 10+.
102   function checkTransform3dSupport() {
103     // Borrowed Modernizr implementation which detects support for 3D transforms on Android < 4
104     var ret = !!getVendorPropertyName('perspective');
105     if (ret && 'WebkitPerspective' in div.style) {
106       return matchMedia("(-webkit-transform-3d)").matches;
107     }
108     return ret;
109   }
110
111   var isChrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
112
113   // Check for the browser's transitions support.
114   // You can access this in jQuery's `$.support.transition`.
115   // As per [jQuery's cssHooks documentation](http://api.jquery.com/jQuery.cssHooks/),
116   // we set $.support.transition to a string of the actual property name used.
117   support.transition      = getVendorPropertyName('transition');
118   support.transitionDelay = getVendorPropertyName('transitionDelay');
119   support.transform       = getVendorPropertyName('transform');
120   support.transformOrigin = getVendorPropertyName('transformOrigin');
121   support.transform3d     = checkTransform3dSupport();
122
123   $.extend($.support, support);
124
125   var eventNames = {
126     'MozTransition':    'transitionend',
127     'OTransition':      'oTransitionEnd',
128     'WebkitTransition': 'webkitTransitionEnd',
129     'msTransition':     'MSTransitionEnd'
130   };
131
132   // Detect the 'transitionend' event needed.
133   var transitionEnd = support.transitionEnd = eventNames[support.transition] || null;
134
135   // Avoid memory leak in IE.
136   div = null;
137
138   // ## $.cssEase
139   // List of easing aliases that you can use with `$.fn.transition`.
140   $.cssEase = {
141     '_default': 'ease',
142     'in':       'ease-in',
143     'out':      'ease-out',
144     'in-out':   'ease-in-out',
145     'snap':     'cubic-bezier(0,1,.5,1)'
146   };
147
148   // ## 'transform' CSS hook
149   // Allows you to use the `transform` property in CSS.
150   //
151   //     $("#hello").css({ transform: "rotate(90deg)" });
152   //
153   //     $("#hello").css('transform');
154   //     //=> { rotate: '90deg' }
155   //
156   $.cssHooks.transform = {
157     // The getter returns a `Transform` object.
158     get: function(elem) {
159       return $(elem).data('transform') || new Transform();
160     },
161
162     // The setter accepts a `Transform` object or a string.
163     set: function(elem, v) {
164       var value = v;
165
166       if (!(value instanceof Transform)) {
167         value = new Transform(value);
168       }
169
170       // We've seen the 3D version of Scale() not work in Chrome when the
171       // element being scaled extends outside of the viewport.  Thus, we're
172       // forcing Chrome to not use the 3d transforms as well.  Not sure if
173       // translate is affectede, but not risking it.  Detection code from
174       // http://davidwalsh.name/detecting-google-chrome-javascript
175       //
176       // Also eliminate WebKit-browsers withouth 3d transforms support
177       // such as Android < 4 default browser. Fixes #34
178       if (support.transform3d && !isChrome) {
179         elem.style[support.transform] = value.toString(true);
180       } else {
181         elem.style[support.transform] = value.toString();
182       }
183
184       $(elem).data('transform', value);
185     }
186   };
187
188   // ## 'transformOrigin' CSS hook
189   // Allows the use for `transformOrigin` to define where scaling and rotation
190   // is pivoted.
191   //
192   //     $("#hello").css({ transformOrigin: '0 0' });
193   //
194   $.cssHooks.transformOrigin = {
195     get: function(elem) {
196       return elem.style[support.transformOrigin];
197     },
198     set: function(elem, value) {
199       elem.style[support.transformOrigin] = value;
200     }
201   };
202
203   // ## Other CSS hooks
204   // Allows you to rotate, scale and translate.
205   registerCssHook('scale');
206   registerCssHook('translate');
207   registerCssHook('rotate');
208   registerCssHook('rotateX');
209   registerCssHook('rotateY');
210   registerCssHook('rotate3d');
211   registerCssHook('perspective');
212   registerCssHook('skewX');
213   registerCssHook('skewY');
214   registerCssHook('x', true);
215   registerCssHook('y', true);
216
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
245   Transform.prototype = {
246     // ### setFromString()
247     // Sets a property from a string.
248     //
249     //     t.setFromString('scale', '2,4');
250     //     // Same as set('scale', '2', '4');
251     //
252     setFromString: function(prop, val) {
253       var args =
254         (typeof val === 'string')  ? val.split(',') :
255         (val.constructor === Array) ? val :
256         [ val ];
257
258       args.unshift(prop);
259
260       Transform.prototype.set.apply(this, args);
261     },
262
263     // ### set()
264     // Sets a property.
265     //
266     //     t.set('scale', 2, 4);
267     //
268     set: function(prop) {
269       var args = Array.prototype.slice.apply(arguments, [1]);
270       if (this.setter[prop]) {
271         this.setter[prop].apply(this, args);
272       } else {
273         this[prop] = args.join(',');
274       }
275     },
276
277     get: function(prop) {
278       if (this.getter[prop]) {
279         return this.getter[prop].apply(this);
280       } else {
281         return this[prop] || 0;
282       }
283     },
284
285     setter: {
286       // ### rotate
287       //
288       //     .css({ rotate: 30 })
289       //     .css({ rotate: "30" })
290       //     .css({ rotate: "30deg" })
291       //     .css({ rotate: "30deg" })
292       //
293       rotate: function(theta) {
294         this.rotate = unit(theta, 'deg');
295       },
296
297       rotateX: function(theta) {
298         this.rotateX = unit(theta, 'deg');
299       },
300
301       rotateY: function(theta) {
302         this.rotateY = unit(theta, 'deg');
303       },
304
305       // ### scale
306       //
307       //     .css({ scale: 9 })      //=> "scale(9,9)"
308       //     .css({ scale: '3,2' })  //=> "scale(3,2)"
309       //
310       scale: function(x, y) {
311         if (y === undefined) { y = x; }
312         this.scale = x + "," + y;
313       },
314
315       // ### skewX + skewY
316       skewX: function(x) {
317         this.skewX = unit(x, 'deg');
318       },
319
320       skewY: function(y) {
321         this.skewY = unit(y, 'deg');
322       },
323
324       // ### perspectvie
325       perspective: function(dist) {
326         this.perspective = unit(dist, 'px');
327       },
328
329       // ### x / y
330       // Translations. Notice how this keeps the other value.
331       //
332       //     .css({ x: 4 })       //=> "translate(4px, 0)"
333       //     .css({ y: 10 })      //=> "translate(4px, 10px)"
334       //
335       x: function(x) {
336         this.set('translate', x, null);
337       },
338
339       y: function(y) {
340         this.set('translate', null, y);
341       },
342
343       // ### translate
344       // Notice how this keeps the other value.
345       //
346       //     .css({ translate: '2, 5' })    //=> "translate(2px, 5px)"
347       //
348       translate: function(x, y) {
349         if (this._translateX === undefined) { this._translateX = 0; }
350         if (this._translateY === undefined) { this._translateY = 0; }
351
352         if (x !== null) { this._translateX = unit(x, 'px'); }
353         if (y !== null) { this._translateY = unit(y, 'px'); }
354
355         this.translate = this._translateX + "," + this._translateY;
356       }
357     },
358
359     getter: {
360       x: function() {
361         return this._translateX || 0;
362       },
363
364       y: function() {
365         return this._translateY || 0;
366       },
367
368       scale: function() {
369         var s = (this.scale || "1,1").split(',');
370         if (s[0]) { s[0] = parseFloat(s[0]); }
371         if (s[1]) { s[1] = parseFloat(s[1]); }
372
373         // "2.5,2.5" => 2.5
374         // "2.5,1" => [2.5,1]
375         return (s[0] === s[1]) ? s[0] : s;
376       },
377
378       rotate3d: function() {
379         var s = (this.rotate3d || "0,0,0,0deg").split(',');
380         for (var i=0; i<=3; ++i) {
381           if (s[i]) { s[i] = parseFloat(s[i]); }
382         }
383         if (s[3]) { s[3] = unit(s[3], 'deg'); }
384
385         return s;
386       }
387     },
388
389     // ### parse()
390     // Parses from a string. Called on constructor.
391     parse: function(str) {
392       var self = this;
393       str.replace(/([a-zA-Z0-9]+)\((.*?)\)/g, function(x, prop, val) {
394         self.setFromString(prop, val);
395       });
396     },
397
398     // ### toString()
399     // Converts to a `transition` CSS property string. If `use3d` is given,
400     // it converts to a `-webkit-transition` CSS property string instead.
401     toString: function(use3d) {
402       var re = [];
403
404       for (var i in this) {
405         if (this.hasOwnProperty(i)) {
406           // Don't use 3D transformations if the browser can't support it.
407           if ((!support.transform3d) && (
408             (i === 'rotateX') ||
409             (i === 'rotateY') ||
410             (i === 'perspective') ||
411             (i === 'transformOrigin'))) { continue; }
412
413           if (i[0] !== '_') {
414             if (use3d && (i === 'scale')) {
415               re.push(i + "3d(" + this[i] + ",1)");
416             } else if (use3d && (i === 'translate')) {
417               re.push(i + "3d(" + this[i] + ",0)");
418             } else {
419               re.push(i + "(" + this[i] + ")");
420             }
421           }
422         }
423       }
424
425       return re.join(" ");
426     }
427   };
428
429   function callOrQueue(self, queue, fn) {
430     if (queue === true) {
431       self.queue(fn);
432     } else if (queue) {
433       self.queue(queue, fn);
434     } else {
435       fn();
436     }
437   }
438
439   // ### getProperties(dict)
440   // Returns properties (for `transition-property`) for dictionary `props`. The
441   // value of `props` is what you would expect in `$.css(...)`.
442   function getProperties(props) {
443     var re = [];
444
445     $.each(props, function(key) {
446       key = $.camelCase(key); // Convert "text-align" => "textAlign"
447       key = $.transit.propertyMap[key] || key;
448       key = uncamel(key); // Convert back to dasherized
449
450       if ($.inArray(key, re) === -1) { re.push(key); }
451     });
452
453     return re;
454   }
455
456   // ### getTransition()
457   // Returns the transition string to be used for the `transition` CSS property.
458   //
459   // Example:
460   //
461   //     getTransition({ opacity: 1, rotate: 30 }, 500, 'ease');
462   //     //=> 'opacity 500ms ease, -webkit-transform 500ms ease'
463   //
464   function getTransition(properties, duration, easing, delay) {
465     // Get the CSS properties needed.
466     var props = getProperties(properties);
467
468     // Account for aliases (`in` => `ease-in`).
469     if ($.cssEase[easing]) { easing = $.cssEase[easing]; }
470
471     // Build the duration/easing/delay attributes for it.
472     var attribs = '' + toMS(duration) + ' ' + easing;
473     if (parseInt(delay, 10) > 0) { attribs += ' ' + toMS(delay); }
474
475     // For more properties, add them this way:
476     // "margin 200ms ease, padding 200ms ease, ..."
477     var transitions = [];
478     $.each(props, function(i, name) {
479       transitions.push(name + ' ' + attribs);
480     });
481
482     return transitions.join(', ');
483   }
484
485   // ## $.fn.transition
486   // Works like $.fn.animate(), but uses CSS transitions.
487   //
488   //     $("...").transition({ opacity: 0.1, scale: 0.3 });
489   //
490   //     // Specific duration
491   //     $("...").transition({ opacity: 0.1, scale: 0.3 }, 500);
492   //
493   //     // With duration and easing
494   //     $("...").transition({ opacity: 0.1, scale: 0.3 }, 500, 'in');
495   //
496   //     // With callback
497   //     $("...").transition({ opacity: 0.1, scale: 0.3 }, function() { ... });
498   //
499   //     // With everything
500   //     $("...").transition({ opacity: 0.1, scale: 0.3 }, 500, 'in', function() { ... });
501   //
502   //     // Alternate syntax
503   //     $("...").transition({
504   //       opacity: 0.1,
505   //       duration: 200,
506   //       delay: 40,
507   //       easing: 'in',
508   //       complete: function() { /* ... */ }
509   //      });
510   //
511   $.fn.transition = $.fn.transit = function(properties, duration, easing, callback) {
512     var self  = this;
513     var delay = 0;
514     var queue = true;
515
516     // Account for `.transition(properties, callback)`.
517     if (typeof duration === 'function') {
518       callback = duration;
519       duration = undefined;
520     }
521
522     // Account for `.transition(properties, duration, callback)`.
523     if (typeof easing === 'function') {
524       callback = easing;
525       easing = undefined;
526     }
527
528     // Alternate syntax.
529     if (typeof properties.easing !== 'undefined') {
530       easing = properties.easing;
531       delete properties.easing;
532     }
533
534     if (typeof properties.duration !== 'undefined') {
535       duration = properties.duration;
536       delete properties.duration;
537     }
538
539     if (typeof properties.complete !== 'undefined') {
540       callback = properties.complete;
541       delete properties.complete;
542     }
543
544     if (typeof properties.queue !== 'undefined') {
545       queue = properties.queue;
546       delete properties.queue;
547     }
548
549     if (typeof properties.delay !== 'undefined') {
550       delay = properties.delay;
551       delete properties.delay;
552     }
553
554     // Set defaults. (`400` duration, `ease` easing)
555     if (typeof duration === 'undefined') { duration = $.fx.speeds._default; }
556     if (typeof easing === 'undefined')   { easing = $.cssEase._default; }
557
558     duration = $.fx.off ? 0 : toMS(duration);
559
560     // Build the `transition` property.
561     var transitionValue = getTransition(properties, duration, easing, delay);
562
563     // Compute delay until callback.
564     // If this becomes 0, don't bother setting the transition property.
565     var work = $.transit.enabled && support.transition;
566     var i = work ? (parseInt(duration, 10) + parseInt(delay, 10)) : 0;
567
568     // If there's nothing to do...
569     if (i === 0) {
570       var fn = function(next) {
571         self.css(properties);
572         if (callback) { callback(); }
573         next();
574       };
575
576       callOrQueue(self, queue, fn);
577       return self;
578     }
579
580     // Save the old transitions of each element so we can restore it later.
581     var oldTransitions = {};
582
583     var run = function(nextCall) {
584       var bound = false;
585
586       // Prepare the callback.
587       var cb = function() {
588         if (bound) { self.unbind(transitionEnd, cb); }
589
590         if (i > 0) {
591           self.each(function() {
592             this.style[support.transition] = (oldTransitions[this] || null);
593           });
594         }
595
596         if (typeof callback === 'function') { callback.apply(self); }
597         if (typeof nextCall === 'function') { nextCall(); }
598       };
599
600       if ((i > 0) && (transitionEnd) && ($.transit.useTransitionEnd)) {
601         // Use the 'transitionend' event if it's available.
602         bound = true;
603         self.bind(transitionEnd, cb);
604       } else {
605         // Fallback to timers if the 'transitionend' event isn't supported.
606         window.setTimeout(cb, i);
607       }
608
609       // Apply transitions.
610       self.each(function() {
611         if (i > 0) {
612           this.style[support.transition] = transitionValue;
613         }
614         $(this).css(properties);
615       });
616     };
617
618     // Defer running. This allows the browser to paint any pending CSS it hasn't
619     // painted yet before doing the transitions.
620     var deferredRun = function(next) {
621       var i = 0;
622
623       // Durations that are too slow will get transitions mixed up.
624       // (Tested on Mac/FF 7.0.1)
625       if ((support.transition === 'MozTransition') && (i < 25)) { i = 25; }
626
627       window.setTimeout(function() { run(next); }, i);
628     };
629
630     // Use jQuery's fx queue.
631     callOrQueue(self, queue, deferredRun);
632
633     // Chainability.
634     return this;
635   };
636
637   function registerCssHook(prop, isPixels) {
638     // For certain properties, the 'px' should not be implied.
639     if (!isPixels) { $.cssNumber[prop] = true; }
640
641     $.transit.propertyMap[prop] = support.transform;
642
643     $.cssHooks[prop] = {
644       get: function(elem) {
645         var t = $(elem).css('transform');
646         return t.get(prop);
647       },
648
649       set: function(elem, value) {
650         var t = $(elem).css('transform');
651         t.setFromString(prop, value);
652
653         $(elem).css({ transform: t });
654       }
655     };
656   }
657
658   // ### uncamel(str)
659   // Converts a camelcase string to a dasherized string.
660   // (`marginLeft` => `margin-left`)
661   function uncamel(str) {
662     return str.replace(/([A-Z])/g, function(letter) { return '-' + letter.toLowerCase(); });
663   }
664
665   // ### unit(number, unit)
666   // Ensures that number `number` has a unit. If no unit is found, assume the
667   // default is `unit`.
668   //
669   //     unit(2, 'px')          //=> "2px"
670   //     unit("30deg", 'rad')   //=> "30deg"
671   //
672   function unit(i, units) {
673     if ((typeof i === "string") && (!i.match(/^[\-0-9\.]+$/))) {
674       return i;
675     } else {
676       return "" + i + units;
677     }
678   }
679
680   // ### toMS(duration)
681   // Converts given `duration` to a millisecond string.
682   //
683   //     toMS('fast')   //=> '400ms'
684   //     toMS(10)       //=> '10ms'
685   //
686   function toMS(duration) {
687     var i = duration;
688
689     // Allow for string durations like 'fast'.
690     if ($.fx.speeds[i]) { i = $.fx.speeds[i]; }
691
692     return unit(i, 'ms');
693   }
694
695   // Export some functions for testable-ness.
696   $.transit.getTransitionValue = getTransition;
697 })(jQuery);