angular-material-npfixed
Version:
The Angular Material project is an implementation of Material Design in Angular.js. This project provides a set of reusable, well-tested, and accessible Material Design UI components. Angular Material is supported internally at Google by the Angular.js, M
261 lines (217 loc) • 8.51 kB
JavaScript
// Polyfill angular < 1.4 (provide $animateCss)
angular
.module('material.core')
.factory('$$mdAnimate', function($q, $timeout, $mdConstant, $animateCss){
// Since $$mdAnimate is injected into $mdUtil... use a wrapper function
// to subsequently inject $mdUtil as an argument to the AnimateDomUtils
return function($mdUtil) {
return AnimateDomUtils( $mdUtil, $q, $timeout, $mdConstant, $animateCss);
};
});
/**
* Factory function that requires special injections
*/
function AnimateDomUtils($mdUtil, $q, $timeout, $mdConstant, $animateCss) {
var self;
return self = {
/**
*
*/
translate3d : function( target, from, to, options ) {
return $animateCss(target, {
from: from,
to: to,
addClass: options.transitionInClass,
removeClass: options.transitionOutClass,
duration: options.duration
})
.start()
.then(function(){
// Resolve with reverser function...
return reverseTranslate;
});
/**
* Specific reversal of the request translate animation above...
*/
function reverseTranslate (newFrom) {
return $animateCss(target, {
to: newFrom || from,
addClass: options.transitionOutClass,
removeClass: options.transitionInClass,
duration: options.duration
}).start();
}
},
/**
* Listen for transitionEnd event (with optional timeout)
* Announce completion or failure via promise handlers
*/
waitTransitionEnd: function (element, opts) {
var TIMEOUT = 3000; // fallback is 3 secs
return $q(function(resolve, reject){
opts = opts || { };
// If there is no transition is found, resolve immediately
//
// NOTE: using $mdUtil.nextTick() causes delays/issues
if (noTransitionFound(opts.cachedTransitionStyles)) {
TIMEOUT = 0;
}
var timer = $timeout(finished, opts.timeout || TIMEOUT);
element.on($mdConstant.CSS.TRANSITIONEND, finished);
/**
* Upon timeout or transitionEnd, reject or resolve (respectively) this promise.
* NOTE: Make sure this transitionEnd didn't bubble up from a child
*/
function finished(ev) {
if ( ev && ev.target !== element[0]) return;
if ( ev ) $timeout.cancel(timer);
element.off($mdConstant.CSS.TRANSITIONEND, finished);
// Never reject since ngAnimate may cause timeouts due missed transitionEnd events
resolve();
}
/**
* Checks whether or not there is a transition.
*
* @param styles The cached styles to use for the calculation. If null, getComputedStyle()
* will be used.
*
* @returns {boolean} True if there is no transition/duration; false otherwise.
*/
function noTransitionFound(styles) {
styles = styles || window.getComputedStyle(element[0]);
return styles.transitionDuration == '0s' || (!styles.transition && !styles.transitionProperty);
}
});
},
calculateTransformValues: function (element, originator) {
var origin = originator.element;
var bounds = originator.bounds;
if (origin || bounds) {
var originBnds = origin ? self.clientRect(origin) || currentBounds() : self.copyRect(bounds);
var dialogRect = self.copyRect(element[0].getBoundingClientRect());
var dialogCenterPt = self.centerPointFor(dialogRect);
var originCenterPt = self.centerPointFor(originBnds);
return {
centerX: originCenterPt.x - dialogCenterPt.x,
centerY: originCenterPt.y - dialogCenterPt.y,
scaleX: Math.round(100 * Math.min(0.5, originBnds.width / dialogRect.width)) / 100,
scaleY: Math.round(100 * Math.min(0.5, originBnds.height / dialogRect.height)) / 100
};
}
return {centerX: 0, centerY: 0, scaleX: 0.5, scaleY: 0.5};
/**
* This is a fallback if the origin information is no longer valid, then the
* origin bounds simply becomes the current bounds for the dialogContainer's parent
*/
function currentBounds() {
var cntr = element ? element.parent() : null;
var parent = cntr ? cntr.parent() : null;
return parent ? self.clientRect(parent) : null;
}
},
/**
* Calculate the zoom transform from dialog to origin.
*
* We use this to set the dialog position immediately;
* then the md-transition-in actually translates back to
* `translate3d(0,0,0) scale(1.0)`...
*
* NOTE: all values are rounded to the nearest integer
*/
calculateZoomToOrigin: function (element, originator) {
var zoomTemplate = "translate3d( {centerX}px, {centerY}px, 0 ) scale( {scaleX}, {scaleY} )";
var buildZoom = angular.bind(null, $mdUtil.supplant, zoomTemplate);
return buildZoom(self.calculateTransformValues(element, originator));
},
/**
* Calculate the slide transform from panel to origin.
* NOTE: all values are rounded to the nearest integer
*/
calculateSlideToOrigin: function (element, originator) {
var slideTemplate = "translate3d( {centerX}px, {centerY}px, 0 )";
var buildSlide = angular.bind(null, $mdUtil.supplant, slideTemplate);
return buildSlide(self.calculateTransformValues(element, originator));
},
/**
* Enhance raw values to represent valid css stylings...
*/
toCss : function( raw ) {
var css = { };
var lookups = 'left top right bottom width height x y min-width min-height max-width max-height';
angular.forEach(raw, function(value,key) {
if ( angular.isUndefined(value) ) return;
if ( lookups.indexOf(key) >= 0 ) {
css[key] = value + 'px';
} else {
switch (key) {
case 'transition':
convertToVendor(key, $mdConstant.CSS.TRANSITION, value);
break;
case 'transform':
convertToVendor(key, $mdConstant.CSS.TRANSFORM, value);
break;
case 'transformOrigin':
convertToVendor(key, $mdConstant.CSS.TRANSFORM_ORIGIN, value);
break;
case 'font-size':
css['font-size'] = value; // font sizes aren't always in px
break;
}
}
});
return css;
function convertToVendor(key, vendor, value) {
angular.forEach(vendor.split(' '), function (key) {
css[key] = value;
});
}
},
/**
* Convert the translate CSS value to key/value pair(s).
*/
toTransformCss: function (transform, addTransition, transition) {
var css = {};
angular.forEach($mdConstant.CSS.TRANSFORM.split(' '), function (key) {
css[key] = transform;
});
if (addTransition) {
transition = transition || "all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1) !important";
css.transition = transition;
}
return css;
},
/**
* Clone the Rect and calculate the height/width if needed
*/
copyRect: function (source, destination) {
if (!source) return null;
destination = destination || {};
angular.forEach('left top right bottom width height'.split(' '), function (key) {
destination[key] = Math.round(source[key]);
});
destination.width = destination.width || (destination.right - destination.left);
destination.height = destination.height || (destination.bottom - destination.top);
return destination;
},
/**
* Calculate ClientRect of element; return null if hidden or zero size
*/
clientRect: function (element) {
var bounds = angular.element(element)[0].getBoundingClientRect();
var isPositiveSizeClientRect = function (rect) {
return rect && (rect.width > 0) && (rect.height > 0);
};
// If the event origin element has zero size, it has probably been hidden.
return isPositiveSizeClientRect(bounds) ? self.copyRect(bounds) : null;
},
/**
* Calculate 'rounded' center point of Rect
*/
centerPointFor: function (targetRect) {
return targetRect ? {
x: Math.round(targetRect.left + (targetRect.width / 2)),
y: Math.round(targetRect.top + (targetRect.height / 2))
} : { x : 0, y : 0 };
}
};
}