' +
'
' +
@@ -104,38 +90,38 @@ function SliderDirective($mdTheming) {
* We use a controller for all the logic so that we can expose a few
* things to unit tests
*/
-function SliderController(scope, element, attr, $$rAF, $window, $mdEffects, $mdAria, $mdUtil, $mdConstant) {
+function SliderController($scope, $element, $attrs, $$rAF, $window, $mdAria, $mdUtil, $mdConstant) {
this.init = function init(ngModelCtrl) {
- var thumb = angular.element(element[0].querySelector('.md-thumb'));
+ var thumb = angular.element($element[0].querySelector('.md-thumb'));
var thumbContainer = thumb.parent();
- var trackContainer = angular.element(element[0].querySelector('.md-track-container'));
- var activeTrack = angular.element(element[0].querySelector('.md-track-fill'));
- var tickContainer = angular.element(element[0].querySelector('.md-track-ticks'));
+ var trackContainer = angular.element($element[0].querySelector('.md-track-container'));
+ var activeTrack = angular.element($element[0].querySelector('.md-track-fill'));
+ var tickContainer = angular.element($element[0].querySelector('.md-track-ticks'));
var throttledRefreshDimensions = $mdUtil.throttle(refreshSliderDimensions, 5000);
- // Default values, overridable by attrs
- attr.min ? attr.$observe('min', updateMin) : updateMin(0);
- attr.max ? attr.$observe('max', updateMax) : updateMax(100);
- attr.step ? attr.$observe('step', updateStep) : updateStep(1);
+ // Default values, overridable by $attrss
+ $attrs.min ? $attrs.$observe('min', updateMin) : updateMin(0);
+ $attrs.max ? $attrs.$observe('max', updateMax) : updateMax(100);
+ $attrs.step ? $attrs.$observe('step', updateStep) : updateStep(1);
// We have to manually stop the $watch on ngDisabled because it exists
- // on the parent scope, and won't be automatically destroyed when
+ // on the parent $scope, and won't be automatically destroyed when
// the component is destroyed.
var stopDisabledWatch = angular.noop;
- if (attr.ngDisabled) {
- stopDisabledWatch = scope.$parent.$watch(attr.ngDisabled, updateAriaDisabled);
+ if ($attrs.ngDisabled) {
+ stopDisabledWatch = $scope.$parent.$watch($attrs.ngDisabled, updateAriaDisabled);
} else {
- updateAriaDisabled(!!attr.disabled);
+ updateAriaDisabled(!!$attrs.disabled);
}
- $mdAria.expect(element, 'aria-label');
+ $mdAria.expect($element, 'aria-label');
- element.attr('tabIndex', 0);
- element.attr('role', 'slider');
- element.on('keydown', keydownListener);
+ $element.attr('tabIndex', 0);
+ $element.attr('role', 'slider');
+ $element.on('keydown', keydownListener);
- var hammertime = new Hammer(element[0], {
+ var hammertime = new Hammer($element[0], {
recognizers: [
[Hammer.Pan, { direction: Hammer.DIRECTION_HORIZONTAL }]
]
@@ -156,7 +142,7 @@ function SliderController(scope, element, attr, $$rAF, $window, $mdEffects, $mdA
var debouncedUpdateAll = $$rAF.debounce(updateAll);
angular.element($window).on('resize', debouncedUpdateAll);
- scope.$on('$destroy', function() {
+ $scope.$on('$destroy', function() {
angular.element($window).off('resize', debouncedUpdateAll);
hammertime.destroy();
stopDisabledWatch();
@@ -175,26 +161,26 @@ function SliderController(scope, element, attr, $$rAF, $window, $mdEffects, $mdA
var step;
function updateMin(value) {
min = parseFloat(value);
- element.attr('aria-valuemin', value);
+ $element.attr('aria-valuemin', value);
}
function updateMax(value) {
max = parseFloat(value);
- element.attr('aria-valuemax', value);
+ $element.attr('aria-valuemax', value);
}
function updateStep(value) {
step = parseFloat(value);
redrawTicks();
}
function updateAriaDisabled(isDisabled) {
- element.attr('aria-disabled', !!isDisabled);
+ $element.attr('aria-disabled', !!isDisabled);
}
// Draw the ticks with canvas.
- // The alternative to drawing ticks with canvas is to draw one element for each tick,
+ // The alternative to drawing ticks with canvas is to draw one $element for each tick,
// which could quickly become a performance bottleneck.
var tickCanvas, tickCtx;
function redrawTicks() {
- if (!angular.isDefined(attr.discrete)) return;
+ if (!angular.isDefined($attrs.discrete)) return;
var numSteps = Math.floor( (max - min) / step );
if (!tickCanvas) {
@@ -232,7 +218,7 @@ function SliderController(scope, element, attr, $$rAF, $window, $mdEffects, $mdA
* left/right arrow listener
*/
function keydownListener(ev) {
- if(element[0].hasAttribute('disabled')) {
+ if($element[0].hasAttribute('disabled')) {
return;
}
@@ -248,7 +234,7 @@ function SliderController(scope, element, attr, $$rAF, $window, $mdEffects, $mdA
}
ev.preventDefault();
ev.stopPropagation();
- scope.$evalAsync(function() {
+ $scope.$evalAsync(function() {
setModelValue(ngModelCtrl.$viewValue + changeAmount);
});
}
@@ -267,8 +253,8 @@ function SliderController(scope, element, attr, $$rAF, $window, $mdEffects, $mdA
}
var percent = (ngModelCtrl.$viewValue - min) / (max - min);
- scope.modelValue = ngModelCtrl.$viewValue;
- element.attr('aria-valuenow', ngModelCtrl.$viewValue);
+ $scope.modelValue = ngModelCtrl.$viewValue;
+ $element.attr('aria-valuenow', ngModelCtrl.$viewValue);
setSliderPercent(percent);
}
@@ -289,10 +275,10 @@ function SliderController(scope, element, attr, $$rAF, $window, $mdEffects, $mdA
function setSliderPercent(percent) {
activeTrack.css('width', (percent * 100) + '%');
thumbContainer.css(
- $mdEffects.TRANSFORM,
+ $mdConstant.CSS.TRANSFORM,
'translate3d(' + getSliderDimensions().width * percent + 'px,0,0)'
);
- element.toggleClass('md-min', percent === 0);
+ $element.toggleClass('md-min', percent === 0);
}
@@ -300,16 +286,16 @@ function SliderController(scope, element, attr, $$rAF, $window, $mdEffects, $mdA
* Slide listeners
*/
var isSliding = false;
- var isDiscrete = angular.isDefined(attr.discrete);
+ var isDiscrete = angular.isDefined($attrs.discrete);
function onInput(ev) {
if (!isSliding && ev.eventType === Hammer.INPUT_START &&
- !element[0].hasAttribute('disabled')) {
+ !$element[0].hasAttribute('disabled')) {
isSliding = true;
- element.addClass('active');
- element[0].focus();
+ $element.addClass('active');
+ $element[0].focus();
refreshSliderDimensions();
onPan(ev);
@@ -321,12 +307,12 @@ function SliderController(scope, element, attr, $$rAF, $window, $mdEffects, $mdA
if ( isSliding && isDiscrete ) onPanEnd(ev);
isSliding = false;
- element.removeClass('panning active');
+ $element.removeClass('panning active');
}
}
function onPanStart() {
if (!isSliding) return;
- element.addClass('panning');
+ $element.addClass('panning');
}
function onPan(ev) {
if (!isSliding) return;
@@ -342,7 +328,7 @@ function SliderController(scope, element, attr, $$rAF, $window, $mdEffects, $mdA
}
function onPanEnd(ev) {
- if ( isDiscrete && !element[0].hasAttribute('disabled') ) {
+ if ( isDiscrete && !$element[0].hasAttribute('disabled') ) {
// Convert exact to closest discrete value.
// Slide animate the thumb... and then update the model value.
@@ -371,7 +357,7 @@ function SliderController(scope, element, attr, $$rAF, $window, $mdEffects, $mdA
* @param x
*/
function doSlide( x ) {
- scope.$evalAsync( function() {
+ $scope.$evalAsync( function() {
setModelValue( percentToValue( positionToPercent(x) ));
});
}
@@ -408,3 +394,4 @@ function SliderController(scope, element, attr, $$rAF, $window, $mdEffects, $mdA
};
}
+})();
diff --git a/src/components/sticky/sticky.js b/src/components/sticky/sticky.js
index 06f6d32a2a2..62aeaa7895a 100644
--- a/src/components/sticky/sticky.js
+++ b/src/components/sticky/sticky.js
@@ -1,3 +1,6 @@
+(function() {
+'use strict';
+
/*
* @ngdoc module
* @name material.components.sticky
@@ -8,17 +11,9 @@
angular.module('material.components.sticky', [
'material.core',
- 'material.components.content',
- 'material.animations'
+ 'material.components.content'
])
-.factory('$mdSticky', [
- '$document',
- '$mdEffects',
- '$compile',
- '$$rAF',
- '$mdUtil',
- MdSticky
-]);
+ .factory('$mdSticky', MdSticky);
/*
* @ngdoc service
@@ -36,7 +31,7 @@ angular.module('material.components.sticky', [
* If not provided, it will use the result of `element.clone()`.
*/
-function MdSticky($document, $mdEffects, $compile, $$rAF, $mdUtil) {
+function MdSticky($document, $mdConstant, $compile, $$rAF, $mdUtil) {
var browserStickySupport = checkStickySupport();
@@ -246,12 +241,12 @@ function MdSticky($document, $mdEffects, $compile, $$rAF, $mdUtil) {
if (amount === null || amount === undefined) {
if (item.translateY) {
item.translateY = null;
- item.clone.css($mdEffects.TRANSFORM, '');
+ item.clone.css($mdConstant.CSS.TRANSFORM, '');
}
} else {
item.translateY = amount;
item.clone.css(
- $mdEffects.TRANSFORM,
+ $mdConstant.CSS.TRANSFORM,
'translate3d(' + item.left + 'px,' + amount + 'px,0)'
);
}
@@ -307,3 +302,4 @@ function MdSticky($document, $mdEffects, $compile, $$rAF, $mdUtil) {
}
}
+})();
diff --git a/src/components/subheader/subheader.js b/src/components/subheader/subheader.js
index 4d2d5be0016..64c800cbfcd 100644
--- a/src/components/subheader/subheader.js
+++ b/src/components/subheader/subheader.js
@@ -1,3 +1,6 @@
+(function() {
+'use strict';
+
/**
* @ngdoc module
* @name material.components.subheader
@@ -5,15 +8,10 @@
* SubHeader module
*/
angular.module('material.components.subheader', [
- 'material.components.sticky',
- 'material.services.theming'
+ 'material.core',
+ 'material.components.sticky'
])
-.directive('mdSubheader', [
- '$mdSticky',
- '$compile',
- '$mdTheming',
- MdSubheaderDirective
-]);
+ .directive('mdSubheader', MdSubheaderDirective);
/**
* @ngdoc directive
@@ -66,3 +64,4 @@ function MdSubheaderDirective($mdSticky, $compile, $mdTheming) {
}
};
}
+})();
diff --git a/src/components/swipe/swipe.js b/src/components/swipe/swipe.js
index 8f39ab24f63..2407c4456e8 100644
--- a/src/components/swipe/swipe.js
+++ b/src/components/swipe/swipe.js
@@ -1,206 +1,205 @@
(function() {
+'use strict';
+
+
+/**
+ * @ngdoc module
+ * @name material.components.swipe
+ * @description Swipe module!
+ */
+angular.module('material.components.swipe',[])
+ .factory('$mdSwipe', MdSwipeFactory)
+ .directive('mdSwipeLeft', MdSwipeLeftDirective)
+ .directive('mdSwipeRight', MdSwipeRightDirective);
+
+/*
+ * @ngdoc service
+ * @module material.components.swipe
+ * @name $mdSwipe
+ * @description
+ * This service allows directives to easily attach swipe and pan listeners to
+ * the specified element.
+ */
+
+function MdSwipeFactory() {
+ // match expected API functionality
+ var attachNoop = function(){ return angular.noop; };
/**
- * @ngdoc module
- * @name material.components.swipe
- * @description Swipe module!
+ * SwipeService constructor pre-captures scope and customized event types
+ *
+ * @param scope
+ * @param eventTypes
+ * @returns {*}
+ * @constructor
*/
- angular.module('material.components.swipe',['ng'])
+ return function SwipeService(scope, eventTypes) {
+ if ( !eventTypes ) eventTypes = "swipeleft swiperight";
- /*
- * @ngdoc service
- * @module material.components.swipe
- * @name $mdSwipe
- * @description
- * This service allows directives to easily attach swipe and pan listeners to
- * the specified element.
- */
- .factory("$mdSwipe", function() {
+ // publish configureFor() method for specific element instance
+ return function configureFor(element, onSwipeCallback, attachLater ) {
+ var hammertime = new Hammer(element[0], {
+ recognizers : addRecognizers([], eventTypes )
+ });
- // match expected API functionality
- var attachNoop = function(){ return angular.noop; };
+ // Attach swipe listeners now
+ if ( !attachLater ) attachSwipe();
+
+ // auto-disconnect during destroy
+ scope.$on('$destroy', function() {
+ hammertime.destroy();
+ });
+
+ return attachSwipe;
+
+ // **********************
+ // Internal methods
+ // **********************
/**
- * SwipeService constructor pre-captures scope and customized event types
+ * Delegate swipe event to callback function
+ * and ensure $digest is triggered.
*
- * @param scope
- * @param eventTypes
- * @returns {*}
- * @constructor
+ * @param ev HammerEvent
*/
- return function SwipeService(scope, eventTypes) {
- if ( !eventTypes ) eventTypes = "swipeleft swiperight";
-
- // publish configureFor() method for specific element instance
- return function configureFor(element, onSwipeCallback, attachLater ) {
- var hammertime = new Hammer(element[0], {
- recognizers : addRecognizers([], eventTypes )
- });
+ function swipeHandler(ev) {
- // Attach swipe listeners now
- if ( !attachLater ) attachSwipe();
+ // Prevent triggering parent hammer listeners
+ ev.srcEvent.stopPropagation();
- // auto-disconnect during destroy
- scope.$on('$destroy', function() {
- hammertime.destroy();
+ if ( angular.isFunction(onSwipeCallback) ) {
+ scope.$apply(function() {
+ onSwipeCallback(ev);
});
+ }
+ }
- return attachSwipe;
-
- // **********************
- // Internal methods
- // **********************
-
- /**
- * Delegate swipe event to callback function
- * and ensure $digest is triggered.
- *
- * @param ev HammerEvent
- */
- function swipeHandler(ev) {
-
- // Prevent triggering parent hammer listeners
- ev.srcEvent.stopPropagation();
-
- if ( angular.isFunction(onSwipeCallback) ) {
- scope.$apply(function() {
- onSwipeCallback(ev);
- });
- }
- }
-
- /**
- * Enable listeners and return detach() fn
- */
- function attachSwipe() {
- hammertime.on(eventTypes, swipeHandler );
-
- return function detachSwipe() {
- hammertime.off( eventTypes );
- };
- }
-
- /**
- * Add optional recognizers such as panleft, panright
- */
- function addRecognizers(list, events) {
- var hasPanning = (events.indexOf("pan") > -1);
- var hasSwipe = (events.indexOf("swipe") > -1);
-
- if (hasPanning) {
- list.push([ Hammer.Pan, { direction: Hammer.DIRECTION_HORIZONTAL } ]);
- }
- if (hasSwipe) {
- list.push([ Hammer.Swipe, { direction: Hammer.DIRECTION_HORIZONTAL } ]);
- }
-
- return list;
- }
+ /**
+ * Enable listeners and return detach() fn
+ */
+ function attachSwipe() {
+ hammertime.on(eventTypes, swipeHandler );
- };
- };
- })
-
- /**
- * @ngdoc directive
- * @module material.components.swipe
- * @name mdSwipeLeft
- *
- * @restrict A
- *
- * @description
- * The `
` directive identifies an element on which
- * HammerJS horizontal swipe left and pan left support will be active. The swipe/pan action
- * can result in custom activity trigger by evaluating `expression`.
- *
- * @param {boolean=} noPan Use of attribute indicates flag to disable detection of `panleft` activity
- *
- * @usage
- *
- *
- *
- *
- *
- *
- *
- */
- .directive("mdSwipeLeft", ['$parse', '$mdSwipe',
- function MdSwipeLeft($parse, $mdSwipe) {
- return {
- restrict: 'A',
- link : swipePostLink( $parse, $mdSwipe, "SwipeLeft" )
- };
- }])
-
- /**
- * @ngdoc directive
- * @module material.components.swipe
- * @name mdSwipeRight
- *
- * @restrict A
- *
- * @description
- * The `
` directive identifies functionality
- * that attaches HammerJS horizontal swipe right and pan right support to an element. The swipe/pan action
- * can result in activity trigger by evaluating `expression`
- *
- * @param {boolean=} noPan Use of attribute indicates flag to disable detection of `panright` activity
- *
- * @usage
- *
- *
- *
- *
- *
- *
- *
- */
- .directive( "mdSwipeRight", ['$parse', '$mdSwipe',
- function MdSwipeRight($parse, $mdSwipe) {
- return {
- restrict: 'A',
- link: swipePostLink( $parse, $mdSwipe, "SwipeRight" )
+ return function detachSwipe() {
+ hammertime.off( eventTypes );
};
}
- ]);
-
- /**
- * Factory to build PostLink function specific to Swipe or Pan direction
- *
- * @param $parse
- * @param $mdSwipe
- * @param name
- * @returns {Function}
- */
- function swipePostLink($parse, $mdSwipe, name ) {
-
- return function(scope, element, attrs) {
- var direction = name.toLowerCase();
- var directiveName= "md" + name;
-
- var parentGetter = $parse(attrs[directiveName]) || angular.noop;
- var configureSwipe = $mdSwipe(scope, direction);
- var requestSwipe = function(locals) {
- // build function to request scope-specific swipe response
- parentGetter(scope, locals);
- };
-
- configureSwipe( element, function onHandleSwipe(ev) {
- if ( ev.type == direction ) {
- requestSwipe();
- }
- });
+ /**
+ * Add optional recognizers such as panleft, panright
+ */
+ function addRecognizers(list, events) {
+ var hasPanning = (events.indexOf("pan") > -1);
+ var hasSwipe = (events.indexOf("swipe") > -1);
+
+ if (hasPanning) {
+ list.push([ Hammer.Pan, { direction: Hammer.DIRECTION_HORIZONTAL } ]);
+ }
+ if (hasSwipe) {
+ list.push([ Hammer.Swipe, { direction: Hammer.DIRECTION_HORIZONTAL } ]);
+ }
+
+ return list;
}
- }
-
-})();
+ };
+ };
+}
+
+/**
+ * @ngdoc directive
+ * @module material.components.swipe
+ * @name mdSwipeLeft
+ *
+ * @restrict A
+ *
+ * @description
+ * The `
` directive identifies an element on which
+ * HammerJS horizontal swipe left and pan left support will be active. The swipe/pan action
+ * can result in custom activity trigger by evaluating `expression`.
+ *
+ * @param {boolean=} noPan Use of attribute indicates flag to disable detection of `panleft` activity
+ *
+ * @usage
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ */
+function MdSwipeLeftDirective($parse, $mdSwipe) {
+ return {
+ restrict: 'A',
+ link : swipePostLink( $parse, $mdSwipe, "SwipeLeft" )
+ };
+}
+
+/**
+ * @ngdoc directive
+ * @module material.components.swipe
+ * @name mdSwipeRight
+ *
+ * @restrict A
+ *
+ * @description
+ * The `
` directive identifies functionality
+ * that attaches HammerJS horizontal swipe right and pan right support to an element. The swipe/pan action
+ * can result in activity trigger by evaluating `expression`
+ *
+ * @param {boolean=} noPan Use of attribute indicates flag to disable detection of `panright` activity
+ *
+ * @usage
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ */
+function MdSwipeRightDirective($parse, $mdSwipe) {
+ return {
+ restrict: 'A',
+ link : swipePostLink( $parse, $mdSwipe, "SwipeRight" )
+ };
+}
+
+/**
+ * Factory to build PostLink function specific to Swipe or Pan direction
+ *
+ * @param $parse
+ * @param $mdSwipe
+ * @param name
+ * @returns {Function}
+ */
+function swipePostLink($parse, $mdSwipe, name ) {
+
+ return function(scope, element, attrs) {
+ var direction = name.toLowerCase();
+ var directiveName= "md" + name;
+
+ var parentGetter = $parse(attrs[directiveName]) || angular.noop;
+ var configureSwipe = $mdSwipe(scope, direction);
+ var requestSwipe = function(locals) {
+ // build function to request scope-specific swipe response
+ parentGetter(scope, locals);
+ };
+
+ configureSwipe( element, function onHandleSwipe(ev) {
+ if ( ev.type == direction ) {
+ requestSwipe();
+ }
+ });
+ };
+}
+})();
diff --git a/src/components/switch/switch.js b/src/components/switch/switch.js
index fb62d7a89b2..43f7353b0d3 100644
--- a/src/components/switch/switch.js
+++ b/src/components/switch/switch.js
@@ -1,3 +1,6 @@
+(function() {
+'use strict';
+
/**
* @private
* @ngdoc module
@@ -5,17 +8,11 @@
*/
angular.module('material.components.switch', [
+ 'material.core',
'material.components.checkbox',
- 'material.components.radioButton',
- 'material.services.theming'
+ 'material.components.radioButton'
])
-
-.directive('mdSwitch', [
- 'mdCheckboxDirective',
- 'mdRadioButtonDirective',
- '$mdTheming',
- MdSwitch
-]);
+ .directive('mdSwitch', MdSwitch);
/**
* @private
@@ -51,9 +48,9 @@ angular.module('material.components.switch', [
*
*
*/
-function MdSwitch(checkboxDirectives, radioButtonDirectives, $mdTheming) {
- var checkboxDirective = checkboxDirectives[0];
- var radioButtonDirective = radioButtonDirectives[0];
+function MdSwitch(mdCheckboxDirective, mdRadioButtonDirective, $mdTheming) {
+ var checkboxDirective = mdCheckboxDirective[0];
+ var radioButtonDirective = mdRadioButtonDirective[0];
return {
restrict: 'E',
@@ -83,3 +80,4 @@ function MdSwitch(checkboxDirectives, radioButtonDirectives, $mdTheming) {
};
}
}
+})();
diff --git a/src/components/tabs/js/inkBarDirective.js b/src/components/tabs/js/inkBarDirective.js
index c25d85395a3..a44cb8d9c2a 100644
--- a/src/components/tabs/js/inkBarDirective.js
+++ b/src/components/tabs/js/inkBarDirective.js
@@ -1,19 +1,15 @@
+(function() {
+'use strict';
+
/**
* Conditionally configure ink bar animations when the
* tab selection changes. If `nobar` then do not show the
* bar nor animate.
*/
angular.module('material.components.tabs')
+ .directive('mdTabsInkBar', MdTabInkDirective);
-.directive('mdTabsInkBar', [
- '$mdEffects',
- '$window',
- '$$rAF',
- '$timeout',
- MdTabInkDirective
-]);
-
-function MdTabInkDirective($mdEffects, $window, $$rAF, $timeout) {
+function MdTabInkDirective($mdConstant, $window, $$rAF, $timeout) {
return {
restrict: 'E',
@@ -59,10 +55,11 @@ function MdTabInkDirective($mdEffects, $window, $$rAF, $timeout) {
display : width > 0 ? 'block' : 'none',
width: width + 'px'
});
- element.css($mdEffects.TRANSFORM, 'translate3d(' + left + 'px,0,0)');
+ element.css($mdConstant.CSS.TRANSFORM, 'translate3d(' + left + 'px,0,0)');
}
}
}
}
+})();
diff --git a/src/components/tabs/js/paginationDirective.js b/src/components/tabs/js/paginationDirective.js
index daf4e526fd4..89de64b5cef 100644
--- a/src/components/tabs/js/paginationDirective.js
+++ b/src/components/tabs/js/paginationDirective.js
@@ -1,16 +1,11 @@
+(function() {
+'use strict';
-angular.module('material.components.tabs')
-.directive('mdTabsPagination', [
- '$mdEffects',
- '$window',
- '$$rAF',
- '$$q',
- '$timeout',
- TabPaginationDirective
-]);
+angular.module('material.components.tabs')
+ .directive('mdTabsPagination', TabPaginationDirective);
-function TabPaginationDirective($mdEffects, $window, $$rAF, $$q, $timeout) {
+function TabPaginationDirective($mdConstant, $window, $$rAF, $$q, $timeout) {
// TODO allow configuration of TAB_MIN_WIDTH
// Must match tab min-width rule in _tabs.scss
@@ -151,15 +146,15 @@ function TabPaginationDirective($mdEffects, $window, $$rAF, $$q, $timeout) {
var deferred = $$q.defer();
tabsCtrl.$$pagingOffset = x;
- tabsParent.css($mdEffects.TRANSFORM, 'translate3d(' + x + 'px,0,0)');
- tabsParent.on($mdEffects.TRANSITIONEND_EVENT, onTabsParentTransitionEnd);
+ tabsParent.css($mdConstant.CSS.TRANSFORM, 'translate3d(' + x + 'px,0,0)');
+ tabsParent.on($mdConstant.CSS.TRANSITIONEND, onTabsParentTransitionEnd);
return deferred.promise;
function onTabsParentTransitionEnd(ev) {
// Make sure this event didn't bubble up from an animation in a child element.
if (ev.target === tabsParent[0]) {
- tabsParent.off($mdEffects.TRANSITIONEND_EVENT, onTabsParentTransitionEnd);
+ tabsParent.off($mdConstant.CSS.TRANSITIONEND, onTabsParentTransitionEnd);
deferred.resolve();
}
}
@@ -194,3 +189,4 @@ function TabPaginationDirective($mdEffects, $window, $$rAF, $$q, $timeout) {
}
}
+})();
diff --git a/src/components/tabs/js/tabItemController.js b/src/components/tabs/js/tabItemController.js
index 44858fa6a0a..14f0a6ea35a 100644
--- a/src/components/tabs/js/tabItemController.js
+++ b/src/components/tabs/js/tabItemController.js
@@ -1,22 +1,17 @@
+(function() {
+'use strict';
-angular.module('material.components.tabs')
-.controller('$mdTab', [
- '$scope',
- '$element',
- '$compile',
- '$animate',
- '$mdUtil',
- TabItemController
-]);
+angular.module('material.components.tabs')
+ .controller('$mdTab', TabItemController);
-function TabItemController(scope, element, $compile, $animate, $mdUtil) {
+function TabItemController($scope, $element, $compile, $animate, $mdUtil) {
var self = this;
// Properties
self.contentContainer = angular.element('
');
- self.hammertime = Hammer(self.contentContainer[0]);
- self.element = element;
+ self.hammertime = new Hammer(self.contentContainer[0]);
+ self.element = $element;
// Methods
self.isDisabled = isDisabled;
@@ -26,7 +21,7 @@ function TabItemController(scope, element, $compile, $animate, $mdUtil) {
self.onDeselect = onDeselect;
function isDisabled() {
- return element[0].hasAttribute('disabled');
+ return $element[0].hasAttribute('disabled');
}
/**
@@ -36,7 +31,7 @@ function TabItemController(scope, element, $compile, $animate, $mdUtil) {
function onAdd(contentArea) {
if (self.content.length) {
self.contentContainer.append(self.content);
- self.contentScope = scope.$parent.$new();
+ self.contentScope = $scope.$parent.$new();
contentArea.append(self.contentContainer);
$compile(self.contentContainer)(self.contentScope);
@@ -55,29 +50,30 @@ function TabItemController(scope, element, $compile, $animate, $mdUtil) {
function onSelect() {
// Resume watchers and events firing when tab is selected
$mdUtil.reconnectScope(self.contentScope);
- self.hammertime.on('swipeleft swiperight', scope.onSwipe);
+ self.hammertime.on('swipeleft swiperight', $scope.onSwipe);
- element.addClass('active');
- element.attr('aria-selected', true);
- element.attr('tabIndex', 0);
+ $element.addClass('active');
+ $element.attr('aria-selected', true);
+ $element.attr('tabIndex', 0);
$animate.removeClass(self.contentContainer, 'ng-hide');
- scope.onSelect();
+ $scope.onSelect();
}
function onDeselect() {
// Stop watchers & events from firing while tab is deselected
$mdUtil.disconnectScope(self.contentScope);
- self.hammertime.off('swipeleft swiperight', scope.onSwipe);
+ self.hammertime.off('swipeleft swiperight', $scope.onSwipe);
- element.removeClass('active');
- element.attr('aria-selected', false);
+ $element.removeClass('active');
+ $element.attr('aria-selected', false);
// Only allow tabbing to the active tab
- element.attr('tabIndex', -1);
+ $element.attr('tabIndex', -1);
$animate.addClass(self.contentContainer, 'ng-hide');
- scope.onDeselect();
+ $scope.onDeselect();
}
}
+})();
diff --git a/src/components/tabs/js/tabItemDirective.js b/src/components/tabs/js/tabItemDirective.js
index 280454355c9..3966a2c31c1 100644
--- a/src/components/tabs/js/tabItemDirective.js
+++ b/src/components/tabs/js/tabItemDirective.js
@@ -1,13 +1,8 @@
-angular.module('material.components.tabs')
+(function() {
+'use strict';
-.directive('mdTab', [
- '$mdInkRipple',
- '$compile',
- '$mdAria',
- '$mdUtil',
- '$mdConstant',
- MdTabDirective
-]);
+angular.module('material.components.tabs')
+ .directive('mdTab', MdTabDirective);
/**
* @ngdoc directive
@@ -233,3 +228,4 @@ function MdTabDirective($mdInkRipple, $compile, $mdAria, $mdUtil, $mdConstant) {
}
+})();
diff --git a/src/components/tabs/js/tabsController.js b/src/components/tabs/js/tabsController.js
index 2bd4c984275..6c8b0385cea 100644
--- a/src/components/tabs/js/tabsController.js
+++ b/src/components/tabs/js/tabsController.js
@@ -1,21 +1,18 @@
-angular.module('material.components.tabs')
+(function() {
+'use strict';
-.controller('$mdTabs', [
- '$scope',
- '$element',
- '$mdUtil',
- MdTabsController
-]);
+angular.module('material.components.tabs')
+ .controller('$mdTabs', MdTabsController);
-function MdTabsController(scope, element, $mdUtil) {
+function MdTabsController($scope, $element, $mdUtil) {
var tabsList = $mdUtil.iterator([], false);
var self = this;
// Properties
- self.element = element;
- // The section containing the tab content elements
- self.contentArea = angular.element(element[0].querySelector('.md-tabs-content'));
+ self.$element = $element;
+ // The section containing the tab content $elements
+ self.contentArea = angular.element($element[0].querySelector('.md-tabs-content'));
// Methods from iterator
self.inRange = tabsList.inRange;
@@ -33,7 +30,7 @@ function MdTabsController(scope, element, $mdUtil) {
self.next = next;
self.previous = previous;
- scope.$on('$destroy', function() {
+ $scope.$on('$destroy', function() {
self.deselect(self.selected());
for (var i = tabsList.count() - 1; i >= 0; i--) {
self.remove(tabsList[i], true);
@@ -42,7 +39,7 @@ function MdTabsController(scope, element, $mdUtil) {
// Get the selected tab
function selected() {
- return self.itemAt(scope.selectedIndex);
+ return self.itemAt($scope.selectedIndex);
}
// Add a new tab.
@@ -54,11 +51,11 @@ function MdTabsController(scope, element, $mdUtil) {
// Select the new tab if we don't have a selectedIndex, or if the
// selectedIndex we've been waiting for is this tab
- if (scope.selectedIndex === -1 || !angular.isNumber(scope.selectedIndex) ||
- scope.selectedIndex === self.indexOf(tab)) {
+ if ($scope.selectedIndex === -1 || !angular.isNumber($scope.selectedIndex) ||
+ $scope.selectedIndex === self.indexOf(tab)) {
self.select(tab);
}
- scope.$broadcast('$mdTabsChanged');
+ $scope.$broadcast('$mdTabsChanged');
}
function remove(tab, noReselect) {
@@ -77,7 +74,7 @@ function MdTabsController(scope, element, $mdUtil) {
tabsList.remove(tab);
tab.onRemove();
- scope.$broadcast('$mdTabsChanged');
+ $scope.$broadcast('$mdTabsChanged');
}
// Move a tab (used when ng-repeat order changes)
@@ -88,7 +85,7 @@ function MdTabsController(scope, element, $mdUtil) {
tabsList.add(tab, toIndex);
if (isSelected) self.select(tab);
- scope.$broadcast('$mdTabsChanged');
+ $scope.$broadcast('$mdTabsChanged');
}
function select(tab) {
@@ -97,7 +94,7 @@ function MdTabsController(scope, element, $mdUtil) {
self.deselect(self.selected());
- scope.selectedIndex = self.indexOf(tab);
+ $scope.selectedIndex = self.indexOf(tab);
tab.isSelected = true;
tab.onSelect();
}
@@ -106,7 +103,7 @@ function MdTabsController(scope, element, $mdUtil) {
if (!tab || !tab.isSelected) return;
if (!tabsList.contains(tab)) return;
- scope.selectedIndex = -1;
+ $scope.selectedIndex = -1;
tab.isSelected = false;
tab.onDeselect();
}
@@ -123,3 +120,4 @@ function MdTabsController(scope, element, $mdUtil) {
}
}
+})();
diff --git a/src/components/tabs/js/tabsDirective.js b/src/components/tabs/js/tabsDirective.js
index c03b55d512f..34baf7fddae 100644
--- a/src/components/tabs/js/tabsDirective.js
+++ b/src/components/tabs/js/tabsDirective.js
@@ -1,4 +1,8 @@
+(function() {
+'use strict';
+
angular.module('material.components.tabs')
+ .directive('mdTabs', TabsDirective);
/**
* @ngdoc directive
@@ -72,12 +76,6 @@ angular.module('material.components.tabs')
*
*
*/
-.directive('mdTabs', [
- '$parse',
- '$mdTheming',
- TabsDirective
-]);
-
function TabsDirective($parse, $mdTheming) {
return {
restrict: 'E',
@@ -149,3 +147,4 @@ function TabsDirective($parse, $mdTheming) {
}
}
+})();
diff --git a/src/components/tabs/tabs.js b/src/components/tabs/tabs.js
index 4044083e126..18b8ed71275 100644
--- a/src/components/tabs/tabs.js
+++ b/src/components/tabs/tabs.js
@@ -1,3 +1,6 @@
+(function() {
+'use strict';
+
/**
* @ngdoc module
* @name material.components.tabs
@@ -5,9 +8,10 @@
*
* Tabs
*/
+/*
+ * @see js folder for tabs implementation
+ */
angular.module('material.components.tabs', [
- 'material.core',
- 'material.animations',
- 'material.services.theming',
- 'material.services.aria'
+ 'material.core'
]);
+})();
diff --git a/src/components/textField/README.md b/src/components/textField/README.md
deleted file mode 100644
index 74650736160..00000000000
--- a/src/components/textField/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-Text fields allow the user to input text. They can be single line, with or without scrolling, or multi-line, and can have an icon. Touching a text field places the cursor and automatically displays the keyboard. In addition to typing, text fields allow for a variety of other tasks, such as text selection (cut, copy, paste) and data lookup via auto-completion. Text fields can have different types.
-
-[Floating Label](https://www.google.com/design/spec/components/text-fields.html#text-fields-floating-labels) Text fields are show below:
-
diff --git a/src/components/textField/module.json b/src/components/textField/module.json
deleted file mode 100644
index 8cfd9dafef3..00000000000
--- a/src/components/textField/module.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "module": "material.components.textField",
- "name": "Text field",
- "demos": {
- "demo1": {
- "name": "Using Floating-Label Text fields",
- "files": ["demo1/*"]
- }
- }
-}
diff --git a/src/components/textField/textField.js b/src/components/textField/textField.js
index 51abfeae97b..540186ea1c9 100644
--- a/src/components/textField/textField.js
+++ b/src/components/textField/textField.js
@@ -1,13 +1,18 @@
+(function() {
+'use strict';
+
/**
* @ngdoc module
* @name material.components.textField
* @description
* Form
*/
-angular.module('material.components.textField', ['material.core', 'material.services.theming'])
- .directive('mdInputGroup', [ mdInputGroupDirective ])
- .directive('mdInput', ['$mdUtil', mdInputDirective ])
- .directive('mdTextFloat', [ '$mdTheming', '$mdUtil', mdTextFloatDirective ]);
+angular.module('material.components.textField', [
+ 'material.core'
+])
+ .directive('mdInputGroup', mdInputGroupDirective)
+ .directive('mdInput', mdInputDirective)
+ .directive('mdTextFloat', mdTextFloatDirective);
@@ -144,7 +149,8 @@ function mdInputDirective($mdUtil) {
var ngModelCtrl = ctrls[1];
// scan for disabled and transpose the `type` value to the
element
- var isDisabled = $mdUtil.isParentDisabled(element);
+ var parent = element[0].parentNode;
+ var isDisabled = parent && parent.hasAttribute('disabled');
element.attr({
'tabindex': isDisabled ? -1 : 0,
@@ -191,6 +197,4 @@ function mdInputDirective($mdUtil) {
};
}
-
-
-
+})();
diff --git a/src/components/toast/README.md b/src/components/toast/README.md
deleted file mode 100644
index 96fccc87faf..00000000000
--- a/src/components/toast/README.md
+++ /dev/null
@@ -1 +0,0 @@
-Toasts are notifications that can be created on any part of the screen using the `$mdToast` service.
diff --git a/src/components/toast/_toast.scss b/src/components/toast/_toast.scss
index ee90c6e8ff2..1437d59a0fe 100644
--- a/src/components/toast/_toast.scss
+++ b/src/components/toast/_toast.scss
@@ -1,6 +1,6 @@
// See height set globally, depended on by buttons
md-toast {
- display: block;
+ display: flex;
position:absolute;
box-sizing: border-box;
align-items: center;
diff --git a/src/components/toast/demoBasicUsage/index.html b/src/components/toast/demoBasicUsage/index.html
index 61cac3d8e4b..774d50c1aac 100644
--- a/src/components/toast/demoBasicUsage/index.html
+++ b/src/components/toast/demoBasicUsage/index.html
@@ -2,13 +2,17 @@
Toast can be dismissed with a swipe, a timer, or a button.
-
+
+ Show Custom
+
+
+
Show Simple
- Show Advanced
+ ng-click="showActionToast()">
+ Show With Action
diff --git a/src/components/toast/demoBasicUsage/script.js b/src/components/toast/demoBasicUsage/script.js
index 1622477367e..099065af829 100644
--- a/src/components/toast/demoBasicUsage/script.js
+++ b/src/components/toast/demoBasicUsage/script.js
@@ -2,7 +2,7 @@
angular.module('toastDemo1', ['ngMaterial'])
.controller('AppCtrl', function($scope, $mdToast, $animate) {
-
+
$scope.toastPosition = {
bottom: false,
top: true,
@@ -16,7 +16,7 @@ angular.module('toastDemo1', ['ngMaterial'])
.join(' ');
};
- $scope.complexToastIt = function() {
+ $scope.showCustomToast = function() {
$mdToast.show({
controller: 'ToastCtrl',
templateUrl: 'toast-template.html',
@@ -25,11 +25,24 @@ angular.module('toastDemo1', ['ngMaterial'])
});
};
- $scope.toastIt = function() {
- $mdToast.show({
- template: '
Hello, ' + Math.random() + '',
- hideDelay: 2000,
- position: $scope.getToastPosition()
+ $scope.showSimpleToast = function() {
+ $mdToast.show(
+ $mdToast.simple()
+ .content('Hello World')
+ .position($scope.getToastPosition())
+ .hideDelay(0)
+ );
+ };
+
+ $scope.showActionToast = function() {
+ var toast = $mdToast.simple()
+ .content('Hello world')
+ .action('Click me')
+ .highlightAction(false)
+ .position($scope.getToastPosition());
+
+ $mdToast.show(toast).then(function() {
+ alert('Action clicked!');
});
};
diff --git a/src/components/toast/module.json b/src/components/toast/module.json
deleted file mode 100644
index 48c5a12feda..00000000000
--- a/src/components/toast/module.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "module": "material.components.toast",
- "name": "Toast",
- "demos": {
- "demo1": {
- "name": "Toast Basic Usage",
- "files": ["demo1/*"]
- }
- }
-}
diff --git a/src/components/toast/toast.js b/src/components/toast/toast.js
index 29b8443aad7..6220c0b7957 100644
--- a/src/components/toast/toast.js
+++ b/src/components/toast/toast.js
@@ -1,3 +1,6 @@
+(function() {
+'use strict';
+
/**
* @ngdoc module
* @name material.components.toast
@@ -5,19 +8,12 @@
* Toast
*/
angular.module('material.components.toast', [
- 'material.services.interimElement',
- 'material.components.swipe'
+ 'material.core',
+ 'material.components.swipe',
+ 'material.components.button'
])
- .directive('mdToast', [
- MdToastDirective
- ])
- .factory('$mdToast', [
- '$timeout',
- '$$interimElement',
- '$animate',
- '$mdSwipe',
- MdToastService
- ]);
+ .directive('mdToast', MdToastDirective)
+ .provider('$mdToast', MdToastProvider);
function MdToastDirective() {
return {
@@ -31,11 +27,11 @@ function MdToastDirective() {
* @module material.components.toast
*
* @description
- * `$mdToast` opens a toast nofication on any position on the screen with an optional
- * duration, and provides a simple promise API.
+ * `$mdToast` is a service to butild a toast nofication on any position
+ * on the screen with an optional duration, and provides a simple promise API.
*
*
- * ### Restrictions
+ * ### Restrictions on custom toasts
* - The toast's template must have an outer `
` element.
* - For a toast action, use element with class `md-action`.
* - Add the class `md-capsule` for curved corners.
@@ -53,10 +49,7 @@ function MdToastDirective() {
* var app = angular.module('app', ['ngMaterial']);
* app.controller('MyController', function($scope, $mdToast) {
* $scope.openToast = function($event) {
- * $mdToast.show({
- * template: 'Hello!',
- * hideDelay: 3000
- * });
+ * $mdToast.show($mdToast.simple().content('Hello!'));
* };
* });
*
@@ -64,12 +57,37 @@ function MdToastDirective() {
/**
* @ngdoc method
- * @name $mdToast#show
+ * @name $mdToast#simple
*
* @description
- * Show a toast dialog with the specified options.
+ * Builds a preconfigured toast.
+ *
+ * @returns {obj} a `$mdToastPreset` with the chainable configuration methods:
+ *
+ * - $mdToastPreset#content(string) - sets toast content to string
+ * - $mdToastPreset#action(string) - adds an action button, which resolves the promise returned from `show()` if clicked.
+ * - $mdToastPreset#highlightAction(boolean) - sets action button to be highlighted
+ * - $mdToastPreset#capsule(boolean) - adds 'md-capsule' class to the toast (curved corners)
+ */
+
+ /**
+ * @ngdoc method
+ * @name $mdToast#build
+ *
+ * @description
+ * Creates a custom `$mdToastPreset` that you can configure.
+ *
+ * @returns {obj} a `$mdToastPreset` with the chainable configuration methods for shows' options (see below).
+ */
+
+ /**
+ * @ngdoc method
+ * @name $mdToast#show
+ *
+ * @description Shows the toast.
*
- * @param {object} options An options object, with the following properties:
+ * @param {object} optionsOrPreset Either provide an `$mdToastPreset` returned from `simple()`
+ * and `build()`, or an options object with the following properties:
*
* - `templateUrl` - `{string=}`: The url of an html template file that will
* be used as the content of the toast. Restrictions: the template must
@@ -77,7 +95,7 @@ function MdToastDirective() {
* - `template` - `{string=}`: Same as templateUrl, except this is an actual
* template string.
* - `hideDelay` - `{number=}`: How many milliseconds the toast should stay
- * active before automatically closing. Set to 0 or false to have the toast stay open until
+ * active before automatically closing. Set to 0 or false to have the toast stay open until
* closed manually. Default: 3000.
* - `position` - `{string=}`: Where to place the toast. Available: any combination
* of 'bottom', 'left', 'top', 'right', 'fit'. Default: 'bottom left'.
@@ -93,7 +111,7 @@ function MdToastDirective() {
* - `controllerAs` - `{string=}`: An alias to assign the controller to on the scope.
*
* @returns {promise} A promise that can be resolved with `$mdToast.hide()` or
- * rejected with `$mdBottomSheet.cancel()`.
+ * rejected with `$mdToast.cancel()`.
*/
/**
@@ -101,7 +119,7 @@ function MdToastDirective() {
* @name $mdToast#hide
*
* @description
- * Hide the existing toast and resolve the promise returned from `$mdToast.show()`.
+ * Hide an existing toast and resolve the promise returned from `$mdToast.show()`.
*
* @param {*=} response An argument for the resolved promise.
*
@@ -112,51 +130,82 @@ function MdToastDirective() {
* @name $mdToast#cancel
*
* @description
- * Hide the existing toast and reject the promise returned from
+ * Hide the existing toast and reject the promise returned from
* `$mdToast.show()`.
*
* @param {*=} response An argument for the rejected promise.
*
*/
-function MdToastService($timeout, $$interimElement, $animate, $mdSwipe, $mdTheming) {
+function MdToastProvider($$interimElementProvider) {
- var factoryDef = {
- onShow: onShow,
- onRemove: onRemove,
- position: 'bottom left',
- themable: true,
- hideDelay: 3000
- };
-
- var $mdToast = $$interimElement(factoryDef);
- return $mdToast;
-
- function onShow(scope, element, options) {
- // 'top left' -> 'md-top md-left'
- element.addClass(options.position.split(' ').map(function(pos) {
- return 'md-' + pos;
- }).join(' '));
- options.parent.addClass(toastOpenClass(options.position));
-
- var configureSwipe = $mdSwipe(scope, 'swipeleft swiperight');
- options.detachSwipe = configureSwipe(element, function(ev) {
- //Add swipeleft/swiperight class to element so it can animate correctly
- element.addClass('md-' + ev.type);
- $timeout($mdToast.hide);
+ return $$interimElementProvider('$mdToast')
+ .setDefaults({
+ methods: ['position', 'hideDelay', 'capsule'],
+ options: toastDefaultOptions
+ })
+ .addPreset('simple', {
+ methods: ['content', 'action', 'highlightAction'],
+ options: /* @ngInject */ function($mdToast) {
+ return {
+ template: [
+ '',
+ '{{ toast.content }}',
+ '',
+ '{{toast.action}}',
+ '',
+ ''
+ ].join(''),
+ controller: function mdToastCtrl() {
+ this.resolve = function() {
+ $mdToast.hide();
+ };
+ },
+ controllerAs: 'toast',
+ bindToController: true
+ };
+ }
});
- return $animate.enter(element, options.parent);
- }
+ /* @ngInject */
+ function toastDefaultOptions($timeout, $animate, $mdSwipe, $mdTheming, $mdToast) {
+ return {
+ onShow: onShow,
+ onRemove: onRemove,
+ position: 'bottom left',
+ themable: true,
+ hideDelay: 3000
+ };
- function onRemove(scope, element, options) {
- options.detachSwipe();
- options.parent.removeClass(toastOpenClass(options.position));
- return $animate.leave(element);
- }
+ function onShow(scope, element, options) {
+ // 'top left' -> 'md-top md-left'
+ element.addClass(options.position.split(' ').map(function(pos) {
+ return 'md-' + pos;
+ }).join(' '));
+ options.parent.addClass(toastOpenClass(options.position));
+
+ var configureSwipe = $mdSwipe(scope, 'swipeleft swiperight');
+ options.detachSwipe = configureSwipe(element, function(ev) {
+ //Add swipeleft/swiperight class to element so it can animate correctly
+ element.addClass('md-' + ev.type);
+ $timeout($mdToast.cancel);
+ });
+
+ return $animate.enter(element, options.parent);
+ }
- function toastOpenClass(position) {
- return 'md-toast-open-' +
- (position.indexOf('top') > -1 ? 'top' : 'bottom');
+ function onRemove(scope, element, options) {
+ options.detachSwipe();
+ options.parent.removeClass(toastOpenClass(options.position));
+ return $animate.leave(element);
+ }
+
+ function toastOpenClass(position) {
+ return 'md-toast-open-' +
+ (position.indexOf('top') > -1 ? 'top' : 'bottom');
+ }
}
+
}
+
+})();
diff --git a/src/components/toast/toast.spec.js b/src/components/toast/toast.spec.js
index b4127b371d3..3bc005c9751 100644
--- a/src/components/toast/toast.spec.js
+++ b/src/components/toast/toast.spec.js
@@ -11,88 +11,168 @@ describe('$mdToast service', function() {
});
}
- describe('options', function() {
+ describe('simple()', function() {
+ hasConfigMethods(['content', 'action', 'capsule', 'highlightAction']);
- it('should hide after duration', inject(function($timeout, $animate, $rootElement) {
+ it('supports a basic toast', inject(function($mdToast, $rootScope, $timeout, $animate) {
+ var rejected = false;
var parent = angular.element('');
- setup({
- template: '
',
- hideTimeout: 1234
+ $mdToast.show(
+ $mdToast.simple({
+ parent: parent,
+ content: 'Do something',
+ capsule: true
+ })
+ ).catch(function() {
+ rejected = true;
});
- expect($rootElement.find('md-toast').length).toBe(1);
+ $rootScope.$digest();
+ expect(parent.find('span').text()).toBe('Do something');
+ expect(parent.find('md-toast')).toHaveClass('md-capsule');
+ $animate.triggerCallbacks();
$timeout.flush();
- expect($rootElement.find('md-toast').length).toBe(0);
+ $animate.triggerCallbacks();
+ expect(rejected).toBe(true);
}));
- it('should have template', inject(function($timeout, $rootScope, $rootElement) {
+ it('supports an action toast', inject(function($mdToast, $rootScope, $timeout, $animate) {
+ var resolved = false;
var parent = angular.element('
');
- setup({
- template: '
{{1}}234',
- appendTo: parent
- });
- var toast = $rootElement.find('md-toast');
- $timeout.flush();
- expect(toast.text()).toBe('1234');
- }));
-
- it('should have templateUrl', inject(function($timeout, $rootScope, $templateCache, $rootElement) {
- $templateCache.put('template.html', '
hello, {{1}}');
- setup({
- templateUrl: 'template.html',
+ $mdToast.show(
+ $mdToast.simple({
+ content: 'Do something',
+ parent: parent
+ })
+ .action('Click me')
+ .highlightAction(true)
+ ).then(function() {
+ resolved = true;
});
- var toast = $rootElement.find('md-toast');
- expect(toast.text()).toBe('hello, 1');
+ $rootScope.$digest();
+ $animate.triggerCallbacks();
+ var button = parent.find('button');
+ expect(button.text()).toBe('Click me');
+ button.triggerHandler('click');
+ $rootScope.$digest();
+ $animate.triggerCallbacks();
+ expect(resolved).toBe(true);
}));
- it('should add position class to tast', inject(function($rootElement, $timeout) {
- setup({
- template: '
',
- position: 'top left'
+ function hasConfigMethods(methods) {
+ angular.forEach(methods, function(method) {
+ return it('supports config method #' + method, inject(function($mdToast) {
+ var basic = $mdToast.simple();
+ expect(typeof basic[method]).toBe('function');
+ expect(basic[method]()).toBe(basic);
+ }));
});
- var toast = $rootElement.find('md-toast');
- $timeout.flush();
- expect(toast.hasClass('md-top')).toBe(true);
- expect(toast.hasClass('md-left')).toBe(true);
- }));
+ }
});
- describe('lifecycle', function() {
+ describe('build()', function() {
+ describe('options', function() {
+ it('should hide current toast when showing new one', inject(function($rootElement) {
+ setup({
+ template: ''
+ });
+ expect($rootElement[0].querySelector('md-toast.one')).toBeTruthy();
+ expect($rootElement[0].querySelector('md-toast.two')).toBeFalsy();
+ expect($rootElement[0].querySelector('md-toast.three')).toBeFalsy();
- it('should hide current toast when showing new one', inject(function($rootElement) {
- setup({
- template: ''
- });
- expect($rootElement[0].querySelector('md-toast.one')).toBeTruthy();
- expect($rootElement[0].querySelector('md-toast.two')).toBeFalsy();
- expect($rootElement[0].querySelector('md-toast.three')).toBeFalsy();
+ setup({
+ template: ''
+ });
+ expect($rootElement[0].querySelector('md-toast.one')).toBeFalsy();
+ expect($rootElement[0].querySelector('md-toast.two')).toBeTruthy();
+ expect($rootElement[0].querySelector('md-toast.three')).toBeFalsy();
- setup({
- template: ''
- });
- expect($rootElement[0].querySelector('md-toast.one')).toBeFalsy();
- expect($rootElement[0].querySelector('md-toast.two')).toBeTruthy();
- expect($rootElement[0].querySelector('md-toast.three')).toBeFalsy();
+ setup({
+ template: ''
+ });
+ expect($rootElement[0].querySelector('md-toast.one')).toBeFalsy();
+ expect($rootElement[0].querySelector('md-toast.two')).toBeFalsy();
+ expect($rootElement[0].querySelector('md-toast.three')).toBeTruthy();
+ }));
- setup({
- template: ''
- });
- expect($rootElement[0].querySelector('md-toast.one')).toBeFalsy();
- expect($rootElement[0].querySelector('md-toast.two')).toBeFalsy();
- expect($rootElement[0].querySelector('md-toast.three')).toBeTruthy();
- }));
+ it('should hide after duration', inject(function($timeout, $animate, $rootElement) {
+ var parent = angular.element('');
+ setup({
+ template: '
',
+ hideTimeout: 1234
+ });
+ expect($rootElement.find('md-toast').length).toBe(1);
+ $timeout.flush();
+ expect($rootElement.find('md-toast').length).toBe(0);
+ }));
- it('should add class to toastParent', inject(function($rootElement) {
- setup({
- template: '
'
- });
- expect($rootElement.hasClass('md-toast-open-bottom')).toBe(true);
+ it('should have template', inject(function($timeout, $rootScope, $rootElement) {
+ var parent = angular.element('');
+ setup({
+ template: '
{{1}}234',
+ appendTo: parent
+ });
+ var toast = $rootElement.find('md-toast');
+ $timeout.flush();
+ expect(toast.text()).toBe('1234');
+ }));
- setup({
- template: '
',
- position: 'top'
- });
- expect($rootElement.hasClass('md-toast-open-top')).toBe(true);
- }));
+ it('should have templateUrl', inject(function($timeout, $rootScope, $templateCache, $rootElement) {
+ $templateCache.put('template.html', 'hello, {{1}}');
+ setup({
+ templateUrl: 'template.html',
+ });
+ var toast = $rootElement.find('md-toast');
+ expect(toast.text()).toBe('hello, 1');
+ }));
+
+ it('should add position class to tast', inject(function($rootElement, $timeout) {
+ setup({
+ template: '',
+ position: 'top left'
+ });
+ var toast = $rootElement.find('md-toast');
+ $timeout.flush();
+ expect(toast.hasClass('md-top')).toBe(true);
+ expect(toast.hasClass('md-left')).toBe(true);
+ }));
+ });
+
+ describe('lifecycle', function() {
+ it('should hide current toast when showing new one', inject(function($rootElement) {
+ setup({
+ template: ''
+ });
+ expect($rootElement[0].querySelector('md-toast.one')).toBeTruthy();
+ expect($rootElement[0].querySelector('md-toast.two')).toBeFalsy();
+ expect($rootElement[0].querySelector('md-toast.three')).toBeFalsy();
+
+ setup({
+ template: ''
+ });
+ expect($rootElement[0].querySelector('md-toast.one')).toBeFalsy();
+ expect($rootElement[0].querySelector('md-toast.two')).toBeTruthy();
+ expect($rootElement[0].querySelector('md-toast.three')).toBeFalsy();
+ setup({
+ template: ''
+ });
+ expect($rootElement[0].querySelector('md-toast.one')).toBeFalsy();
+ expect($rootElement[0].querySelector('md-toast.two')).toBeFalsy();
+ expect($rootElement[0].querySelector('md-toast.three')).toBeTruthy();
+ }));
+
+ it('should add class to toastParent', inject(function($rootElement) {
+ setup({
+ template: ''
+ });
+ expect($rootElement.hasClass('md-toast-open-bottom')).toBe(true);
+
+ setup({
+ template: '',
+ position: 'top'
+ });
+ expect($rootElement.hasClass('md-toast-open-top')).toBe(true);
+ }));
+ });
});
});
diff --git a/src/components/toolbar/README.md b/src/components/toolbar/README.md
deleted file mode 100644
index 4b3947064f2..00000000000
--- a/src/components/toolbar/README.md
+++ /dev/null
@@ -1 +0,0 @@
-Toolbars, created using the `` directive.
diff --git a/src/components/toolbar/module.json b/src/components/toolbar/module.json
deleted file mode 100644
index 2551990f2a9..00000000000
--- a/src/components/toolbar/module.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "module": "material.components.toolbar",
- "name": "Toolbar",
- "demos": {
- "demo1": {
- "name": "Toolbar Basic Usage",
- "files": ["demo1/*"]
- },
- "demo2": {
- "name": "Scroll Shrinking Toolbar",
- "files": ["demo2/*"]
- }
- }
-}
diff --git a/src/components/toolbar/toolbar.js b/src/components/toolbar/toolbar.js
index aa2188f4e2a..aa3753817d7 100644
--- a/src/components/toolbar/toolbar.js
+++ b/src/components/toolbar/toolbar.js
@@ -1,20 +1,15 @@
+(function() {
+'use strict';
+
/**
* @ngdoc module
* @name material.components.toolbar
*/
angular.module('material.components.toolbar', [
'material.core',
- 'material.components.content',
- 'material.services.theming',
- 'material.animations'
+ 'material.components.content'
])
- .directive('mdToolbar', [
- '$$rAF',
- '$mdEffects',
- '$mdUtil',
- '$mdTheming',
- mdToolbarDirective
- ]);
+ .directive('mdToolbar', mdToolbarDirective);
/**
* @ngdoc directive
@@ -63,7 +58,7 @@ angular.module('material.components.toolbar', [
* shrinking by. For example, if 0.25 is given then the toolbar will shrink
* at one fourth the rate at which the user scrolls down. Default 0.5.
*/
-function mdToolbarDirective($$rAF, $mdEffects, $mdUtil, $mdTheming) {
+function mdToolbarDirective($$rAF, $mdConstant, $mdUtil, $mdTheming) {
return {
restrict: 'E',
@@ -95,7 +90,8 @@ function mdToolbarDirective($$rAF, $mdEffects, $mdUtil, $mdTheming) {
scope.$on('$mdContentLoaded', onMdContentLoad);
function onMdContentLoad($event, newContentEl) {
- if ($mdUtil.elementIsSibling(element, newContentEl)) {
+ // Toolbar and content must be siblings
+ if (element.parent()[0] === newContentEl.parent()[0]) {
// unhook old content event listener if exists
if (contentElement) {
contentElement.off('scroll', debouncedContentScroll);
@@ -135,11 +131,11 @@ function mdToolbarDirective($$rAF, $mdEffects, $mdUtil, $mdTheming) {
);
element.css(
- $mdEffects.TRANSFORM,
+ $mdConstant.CSS.TRANSFORM,
'translate3d(0,' + (-y * shrinkSpeedFactor) + 'px,0)'
);
contentElement.css(
- $mdEffects.TRANSFORM,
+ $mdConstant.CSS.TRANSFORM,
'translate3d(0,' + ((toolbarHeight - y) * shrinkSpeedFactor) + 'px,0)'
);
@@ -152,3 +148,4 @@ function mdToolbarDirective($$rAF, $mdEffects, $mdUtil, $mdTheming) {
};
}
+})();
diff --git a/src/components/toolbar/toolbar.spec.js b/src/components/toolbar/toolbar.spec.js
index e45b31f0aaf..c624c3820c0 100644
--- a/src/components/toolbar/toolbar.spec.js
+++ b/src/components/toolbar/toolbar.spec.js
@@ -13,7 +13,7 @@ describe('', function() {
$provide.value('$$rAF', raf);
}));
- it('with scrollShrink, it should shrink scrollbar when going to bottom', inject(function($compile, $rootScope, $mdEffects, mdToolbarDirective) {
+ it('with scrollShrink, it should shrink scrollbar when going to bottom', inject(function($compile, $rootScope, $mdConstant, mdToolbarDirective) {
var parent = angular.element('');
var toolbar = angular.element('
');
@@ -47,9 +47,9 @@ describe('', function() {
$rootScope.$broadcast('$mdContentLoaded', contentEl);
//Expect everything to be in its proper initial state.
- expect(toolbarCss[$mdEffects.TRANSFORM]).toEqual('translate3d(0,0px,0)');
+ expect(toolbarCss[$mdConstant.CSS.TRANSFORM]).toEqual('translate3d(0,0px,0)');
expect(contentCss['margin-top']).toEqual('-100px');
- expect(contentCss[$mdEffects.TRANSFORM]).toEqual('translate3d(0,100px,0)');
+ expect(contentCss[$mdConstant.CSS.TRANSFORM]).toEqual('translate3d(0,100px,0)');
// Fake scroll to the bottom
contentEl.triggerHandler({
@@ -57,8 +57,8 @@ describe('', function() {
target: { scrollTop: 500 }
});
- expect(toolbarCss[$mdEffects.TRANSFORM]).toEqual('translate3d(0,-100px,0)');
- expect(contentCss[$mdEffects.TRANSFORM]).toEqual('translate3d(0,0px,0)');
+ expect(toolbarCss[$mdConstant.CSS.TRANSFORM]).toEqual('translate3d(0,-100px,0)');
+ expect(contentCss[$mdConstant.CSS.TRANSFORM]).toEqual('translate3d(0,0px,0)');
// Fake scroll back to the top
contentEl.triggerHandler({
@@ -66,8 +66,8 @@ describe('', function() {
target: { scrollTop: 0 }
});
- expect(toolbarCss[$mdEffects.TRANSFORM]).toEqual('translate3d(0,0px,0)');
- expect(contentCss[$mdEffects.TRANSFORM]).toEqual('translate3d(0,100px,0)');
+ expect(toolbarCss[$mdConstant.CSS.TRANSFORM]).toEqual('translate3d(0,0px,0)');
+ expect(contentCss[$mdConstant.CSS.TRANSFORM]).toEqual('translate3d(0,100px,0)');
}));
});
diff --git a/src/components/tooltip/README.md b/src/components/tooltip/README.md
deleted file mode 100644
index b216d118544..00000000000
--- a/src/components/tooltip/README.md
+++ /dev/null
@@ -1 +0,0 @@
-Use [Tooltips](https://www.google.com/design/spec/components/tooltips.html#tooltips-usage) for elements that are 1) interactive and 2) primarily graphical (not textual):
diff --git a/src/components/tooltip/module.json b/src/components/tooltip/module.json
deleted file mode 100644
index fad57db45f5..00000000000
--- a/src/components/tooltip/module.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "module": "material.components.tooltip",
- "name": "Tooltip",
- "demos": {
- "demo1": {
- "name": "Tooltip Basic Usage",
- "files": ["demo1/*"]
- }
- }
-}
diff --git a/src/components/tooltip/tooltip.js b/src/components/tooltip/tooltip.js
index 465727b18a3..cd024a8e8cf 100644
--- a/src/components/tooltip/tooltip.js
+++ b/src/components/tooltip/tooltip.js
@@ -1,21 +1,14 @@
+(function() {
+'use strict';
+
/**
* @ngdoc module
* @name material.components.tooltip
*/
angular.module('material.components.tooltip', [
- 'material.core',
- 'material.services.theming'
+ 'material.core'
])
-
-.directive('mdTooltip', [
- '$timeout',
- '$window',
- '$$rAF',
- '$document',
- '$mdUtil',
- '$mdTheming',
- MdTooltipDirective
-]);
+ .directive('mdTooltip', MdTooltipDirective);
/**
* @ngdoc directive
@@ -190,3 +183,4 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe
}
}
+})();
diff --git a/src/components/whiteframe/README.md b/src/components/whiteframe/README.md
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/src/components/whiteframe/module.json b/src/components/whiteframe/module.json
deleted file mode 100644
index b460b07b0dd..00000000000
--- a/src/components/whiteframe/module.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "module": "material.components.whiteframe",
- "name": "Whiteframe",
- "demos": {
- "demo1": {
- "name": "Whiteframe Basic Usage",
- "files": ["demo1/*"]
- }
- }
-}
diff --git a/src/components/whiteframe/whiteframe.js b/src/components/whiteframe/whiteframe.js
index b90ac304009..b56e3913ac4 100644
--- a/src/components/whiteframe/whiteframe.js
+++ b/src/components/whiteframe/whiteframe.js
@@ -1,5 +1,9 @@
+(function() {
+'use strict';
+
/**
* @ngdoc module
* @name material.components.whiteframe
*/
angular.module('material.components.whiteframe', []);
+})();
diff --git a/src/core/core.js b/src/core/core.js
index a0542bd43f9..f0a809b3a6c 100644
--- a/src/core/core.js
+++ b/src/core/core.js
@@ -2,7 +2,7 @@
* Initialization function that validates environment
* requirements.
*/
-angular.module('material.core', [] )
+angular.module('material.core', [])
.run(function validateEnvironment() {
@@ -13,6 +13,7 @@ angular.module('material.core', [] )
}
})
+
.config(['$provide', function($provide) {
$provide.decorator('$$rAF', ['$delegate', '$rootScope', rAFDecorator]);
diff --git a/src/services/aria/aria.js b/src/core/services/aria/aria.js
similarity index 92%
rename from src/services/aria/aria.js
rename to src/core/services/aria/aria.js
index 631b021c153..40e96ae016c 100644
--- a/src/services/aria/aria.js
+++ b/src/core/services/aria/aria.js
@@ -1,10 +1,8 @@
-angular.module('material.services.aria', [])
+(function() {
+'use strict';
-.service('$mdAria', [
- '$$rAF',
- '$log',
- AriaService
-]);
+angular.module('material.core')
+ .service('$mdAria', AriaService);
function AriaService($$rAF, $log) {
@@ -50,3 +48,4 @@ function AriaService($$rAF, $log) {
}
}
+})();
diff --git a/src/services/compiler/compiler.js b/src/core/services/compiler/compiler.js
similarity index 91%
rename from src/services/compiler/compiler.js
rename to src/core/services/compiler/compiler.js
index 40efc735af3..4f24867f49f 100644
--- a/src/services/compiler/compiler.js
+++ b/src/core/services/compiler/compiler.js
@@ -1,26 +1,15 @@
-/*
- * @ngdoc module
- * @name material.services.compiler
- * @description compiler service
- */
-angular.module('material.services.compiler', [
-])
- .service('$mdCompiler', [
- '$q',
- '$http',
- '$injector',
- '$compile',
- '$controller',
- '$templateCache',
- mdCompilerService
- ]);
+(function() {
+'use strict';
+
+angular.module('material.core')
+ .service('$mdCompiler', mdCompilerService);
function mdCompilerService($q, $http, $injector, $compile, $controller, $templateCache) {
/*
* @ngdoc service
* @name $mdCompiler
- * @module material.services.compiler
+ * @module material.core
* @description
* The $mdCompiler service is an abstraction of angular's compiler, that allows the developer
* to easily compile an element with a templateUrl, controller, and locals.
@@ -73,7 +62,8 @@ function mdCompilerService($q, $http, $injector, $compile, $controller, $templat
* - `link` - `{function(scope)}`: A link function, which, when called, will compile
* the element and instantiate the provided controller (if given).
* - `locals` - `{object}`: The locals which will be passed into the controller once `link` is
- * called.
+ * called. If `bindToController` is true, they will be coppied to the ctrl instead
+ * - `bindToController` - `bool`: bind the locals to the controller, instead of passing them in
*/
this.compile = function(options) {
var templateUrl = options.templateUrl;
@@ -83,6 +73,7 @@ function mdCompilerService($q, $http, $injector, $compile, $controller, $templat
var resolve = options.resolve || {};
var locals = options.locals || {};
var transformTemplate = options.transformTemplate || angular.identity;
+ var bindToController = options.bindToController;
// Take resolve values and invoke them.
// Resolves can either be a string (value: 'MyRegisteredAngularConst'),
@@ -124,6 +115,9 @@ function mdCompilerService($q, $http, $injector, $compile, $controller, $templat
//Instantiate controller if it exists, because we have scope
if (controller) {
var ctrl = $controller(controller, locals);
+ if (bindToController) {
+ angular.extend(ctrl, locals);
+ }
//See angular-route source for this logic
element.data('$ngControllerController', ctrl);
element.children().data('$ngControllerController', ctrl);
@@ -140,3 +134,4 @@ function mdCompilerService($q, $http, $injector, $compile, $controller, $templat
};
}
+})();
diff --git a/src/services/compiler/compiler.spec.js b/src/core/services/compiler/compiler.spec.js
similarity index 86%
rename from src/services/compiler/compiler.spec.js
rename to src/core/services/compiler/compiler.spec.js
index e08d5f4e8c5..cc36fa5283a 100644
--- a/src/services/compiler/compiler.spec.js
+++ b/src/core/services/compiler/compiler.spec.js
@@ -1,5 +1,5 @@
describe('$mdCompiler service', function() {
- beforeEach(module('material.services.compiler'));
+ beforeEach(module('material.core'));
function compile(options) {
var compileData;
@@ -99,6 +99,19 @@ describe('$mdCompiler service', function() {
data.link(scope);
expect(scope.myControllerAs).toBe(data.element.controller());
}));
+
+ it('should work with bindToController', inject(function($rootScope) {
+ var data = compile({
+ template: 'hello',
+ controller: function() { },
+ controllerAs: 'ctrl',
+ bindToController: true,
+ locals: { name: 'Bob' }
+ });
+ var scope = $rootScope.$new();
+ data.link(scope);
+ expect(scope.ctrl.name).toBe('Bob');
+ }));
});
});
});
diff --git a/src/core/services/interimElement/interimElement.js b/src/core/services/interimElement/interimElement.js
new file mode 100644
index 00000000000..3cd9bfbcb49
--- /dev/null
+++ b/src/core/services/interimElement/interimElement.js
@@ -0,0 +1,308 @@
+(function() {
+'use strict';
+
+angular.module('material.core')
+ .provider('$$interimElement', InterimElementProvider);
+
+/*
+ * @ngdoc service
+ * @name $$interimElement
+ * @module material.core
+ *
+ * @description
+ *
+ * Factory that contructs `$$interimElement.$service` services.
+ * Used internally in material design for elements that appear on screen temporarily.
+ * The service provides a promise-like API for interacting with the temporary
+ * elements.
+ *
+ * ```js
+ * app.service('$mdToast', function($$interimElement) {
+ * var $mdToast = $$interimElement(toastDefaultOptions);
+ * return $mdToast;
+ * });
+ * ```
+ * @param {object=} defaultOptions Options used by default for the `show` method on the service.
+ *
+ * @returns {$$interimElement.$service}
+ *
+ */
+
+function InterimElementProvider() {
+ createInterimElementProvider.$get = InterimElementFactory;
+ return createInterimElementProvider;
+
+ /**
+ * Returns a new provider which allows configuration of a new interimElement
+ * service. Allows configuration of default options & methods for options,
+ * as well as configuration of 'preset' methods (eg dialog.basic(): basic is a preset method)
+ */
+ function createInterimElementProvider(interimFactoryName) {
+ var providerConfig = {
+ presets: {}
+ };
+ var provider = {
+ setDefaults: setDefaults,
+ addPreset: addPreset,
+ $get: factory
+ };
+
+ /**
+ * all interim elements will come with the 'build' preset
+ */
+ provider.addPreset('build', {
+ methods: ['controller', 'controllerAs', 'onRemove', 'onShow', 'resolve',
+ 'template', 'templateUrl', 'themable', 'transformTemplate']
+ });
+
+ return provider;
+
+ /**
+ * Save the configured defaults to be used when the factory is instantiated
+ */
+ function setDefaults(definition) {
+ providerConfig.optionsFactory = definition.options;
+ providerConfig.methods = definition.methods;
+ return provider;
+ }
+ /**
+ * Save the configured preset to be used when the factory is instantiated
+ */
+ function addPreset(name, definition) {
+ definition = definition || {};
+ definition.methods = definition.methods || [];
+ definition.options = definition.options || function() { return {}; };
+
+ if (/^cancel|hide|show$/.test(name)) {
+ throw new Error("Preset '" + name + "' in " + interimFactoryName + " is reserved!");
+ }
+ if (definition.methods.indexOf('_options') > -1) {
+ throw new Error("Method '_options' in " + interimFactoryName + " is reserved!");
+ }
+ providerConfig.presets[name] = {
+ methods: definition.methods,
+ optionsFactory: definition.options
+ };
+ return provider;
+ }
+
+ /**
+ * Create a factory that has the given methods & defaults implementing interimElement
+ */
+ /* @ngInject */
+ function factory($$interimElement, $animate, $injector) {
+ var defaultMethods;
+ var defaultOptions;
+ var interimElementService = $$interimElement();
+
+ /*
+ * publicService is what the developer will be using.
+ * It has methods hide(), cancel(), show(), build(), and any other
+ * presets which were set during the config phase.
+ */
+ var publicService = {};
+ publicService.hide = interimElementService.hide;
+ publicService.cancel = interimElementService.cancel;
+ publicService.show = showInterimElement;
+
+ defaultMethods = providerConfig.methods || [];
+ // This must be invoked after the publicService is initialized
+ defaultOptions = invokeFactory(providerConfig.optionsFactory, {});
+
+ angular.forEach(providerConfig.presets, function(definition, name) {
+ var presetDefaults = invokeFactory(definition.optionsFactory, {});
+ var presetMethods = (definition.methods || []).concat(defaultMethods);
+
+ // Every interimElement built with a preset has a field called `$type`,
+ // which matches the name of the preset.
+ // Eg in preset 'confirm', options.$type === 'confirm'
+ angular.extend(presetDefaults, { $type: name });
+
+ function Preset(opts) {
+ this._options = angular.extend({}, presetDefaults, opts);
+ }
+ angular.forEach(presetMethods, function(name) {
+ Preset.prototype[name] = function(value) {
+ this._options[name] = value;
+ return this;
+ };
+ });
+
+ publicService[name] = function(options) {
+ return new Preset(options);
+ };
+ });
+
+ return publicService;
+
+ function showInterimElement(opts) {
+ // opts is either a preset which stores its options on an _options field,
+ // or just an object made up of options
+ return interimElementService.show(
+ angular.extend({}, defaultOptions, (opts || {})._options || opts)
+ );
+ }
+
+ /**
+ * Helper to call $injector.invoke with a local of the factory name for
+ * this provider.
+ * If an $mdDialog is providing options for a dialog and tries to inject
+ * $mdDialog, a circular dependency error will happen.
+ * We get around that by manually injecting $mdDialog as a local.
+ */
+ function invokeFactory(fn, defaultVal) {
+ var locals = {};
+ locals[interimFactoryName] = publicService;
+ return $injector.invoke(fn || function() { return defaultVal; }, {}, locals);
+ }
+
+ }
+
+ }
+
+ /* @ngInject */
+ function InterimElementFactory($q, $rootScope, $timeout, $rootElement, $animate, $mdCompiler, $mdTheming) {
+
+ return function createInterimElementService() {
+ /*
+ * @ngdoc service
+ * @name $$interimElement.$service
+ *
+ * @description
+ * A service used to control inserting and removing an element into the DOM.
+ *
+ */
+ var stack = [];
+ var service;
+ return service = {
+ show: show,
+ hide: hide,
+ cancel: cancel,
+ };
+
+ function show(options) {
+ if (stack.length) {
+ service.cancel();
+ }
+
+ var interimElement = new InterimElement(options);
+
+ stack.push(interimElement);
+ return interimElement.show().then(function() {
+ return interimElement.deferred.promise;
+ });
+ }
+
+ /*
+ * @ngdoc method
+ * @name $$interimElement.$service#hide
+ * @kind function
+ *
+ * @description
+ * Removes the `$interimElement` from the DOM and resolves the promise returned from `show`
+ *
+ * @param {*} resolveParam Data to resolve the promise with
+ *
+ * @returns undefined data that resolves after the element has been removed.
+ *
+ */
+ function hide(response) {
+ var interimElement = stack.shift();
+ interimElement && interimElement.remove().then(function() {
+ interimElement.deferred.resolve(response);
+ });
+ }
+
+ /*
+ * @ngdoc method
+ * @name $$interimElement.$service#cancel
+ * @kind function
+ *
+ * @description
+ * Removes the `$interimElement` from the DOM and rejects the promise returned from `show`
+ *
+ * @param {*} reason Data to reject the promise with
+ *
+ * @returns undefined
+ *
+ */
+ function cancel(reason) {
+ var interimElement = stack.shift();
+ interimElement && interimElement.remove().then(function() {
+ interimElement.deferred.reject(reason);
+ });
+ }
+
+
+ /*
+ * Internal Interim Element Object
+ * Used internally to manage the DOM element and related data
+ */
+ function InterimElement(options) {
+ var self;
+ var hideTimeout, element;
+
+ options = options || {};
+ options = angular.extend({
+ scope: options.scope || $rootScope.$new(options.isolateScope),
+ onShow: function(scope, element, options) {
+ return $animate.enter(element, options.parent);
+ },
+ onRemove: function(scope, element, options) {
+ // Element could be undefined if a new element is shown before
+ // the old one finishes compiling.
+ return element && $animate.leave(element) || $q.when();
+ }
+ }, options);
+
+ return self = {
+ options: options,
+ deferred: $q.defer(),
+ show: function() {
+ return $mdCompiler.compile(options).then(function(compileData) {
+ angular.extend(compileData.locals, self.options);
+
+ // Search for parent at insertion time, if not specified
+ if (!options.parent) {
+ options.parent = $rootElement.find('body');
+ if (!options.parent.length) options.parent = $rootElement;
+ }
+ element = compileData.link(options.scope);
+ if (options.themable) $mdTheming(element);
+ var ret = options.onShow(options.scope, element, options);
+ return $q.when(ret)
+ .then(function(){
+ // Issue onComplete callback when the `show()` finishes
+ var notify = options.onComplete || angular.noop;
+ notify.apply(null, [options.scope, element, options]);
+ })
+ .then(startHideTimeout);
+
+ function startHideTimeout() {
+ if (options.hideDelay) {
+ hideTimeout = $timeout(service.cancel, options.hideDelay) ;
+ }
+ }
+ });
+ },
+ cancelTimeout: function() {
+ if (hideTimeout) {
+ $timeout.cancel(hideTimeout);
+ hideTimeout = undefined;
+ }
+ },
+ remove: function() {
+ self.cancelTimeout();
+ var ret = options.onRemove(options.scope, element, options);
+ return $q.when(ret).then(function() {
+ options.scope.$destroy();
+ });
+ }
+ };
+ }
+ };
+ }
+
+}
+
+})();
diff --git a/src/core/services/interimElement/interimElement.spec.js b/src/core/services/interimElement/interimElement.spec.js
new file mode 100644
index 00000000000..11745a6aede
--- /dev/null
+++ b/src/core/services/interimElement/interimElement.spec.js
@@ -0,0 +1,328 @@
+describe('$$interimElement service', function() {
+ beforeEach(module('material.core'));
+ var $compilerSpy, $themingSpy, resolvingPromise;
+
+ function setup() {
+ module('material.core', 'ngAnimateMock', function($provide) {
+ var $mdCompiler = { compile: angular.noop };
+ $compilerSpy = spyOn($mdCompiler, 'compile');
+ $themingSpy = jasmine.createSpy('$mdTheming');
+
+ $provide.value('$mdCompiler', $mdCompiler);
+ $provide.value('$mdTheming', $themingSpy);
+ });
+ inject(function($q, $compile, $rootScope) {
+ $compilerSpy.andCallFake(function(opts) {
+ var el = $compile(opts.template);
+ var deferred = $q.defer();
+ deferred.resolve({
+ link: el,
+ locals: {}
+ });
+ $rootScope.$apply();
+ return deferred.promise;
+ });
+ });
+ }
+
+ function createInterimProvider(providerName) {
+ var interimProvider;
+ module(function($$interimElementProvider, $provide) {
+ interimProvider = $$interimElementProvider(providerName);
+ $provide.provider(providerName, interimProvider);
+ });
+
+ setup();
+
+ return interimProvider;
+ }
+
+ describe('provider', function() {
+
+ it('by default create a factory with default methods', function() {
+ createInterimProvider('interimTest');
+ inject(function(interimTest) {
+ expect(interimTest.hide).toBeOfType('function');
+ expect(interimTest.build).toBeOfType('function');
+ expect(interimTest.cancel).toBeOfType('function');
+ expect(interimTest.show).toBeOfType('function');
+
+ var builder = interimTest.build();
+ [ 'controller', 'controllerAs', 'onRemove', 'onShow', 'resolve',
+ 'template', 'templateUrl', 'themable', 'transformTemplate'
+ ].forEach(function(methodName) {
+ expect(builder[methodName]).toBeOfType('function');
+ });
+ });
+ });
+
+ it('should show with provided builder', function() {
+ createInterimProvider('interimTest');
+ inject(function(interimTest, $rootScope) {
+ var shown = false;
+ interimTest.show(
+ interimTest.build({
+ controller: 'test ctrl',
+ })
+ .onShow(function(scope, element, options) {
+ shown = true;
+ expect(options.controller).toBe('test ctrl');
+ })
+ );
+
+ $rootScope.$apply();
+ expect(shown).toBe(true);
+ });
+ });
+
+ it('should add specified defaults', function() {
+ createInterimProvider('interimTest').setDefaults({
+ options: function($rootScope) {
+ return {
+ id: $rootScope.$id
+ };
+ },
+ methods: ['foo', 'bar']
+ });
+ inject(function(interimTest, $rootScope) {
+ var builder = interimTest.build({
+ onShow: function(scope, element, options) {
+ shown = true;
+ expect(options.id).toBe($rootScope.$id);
+ }
+ });
+ expect(builder.foo).toBeOfType('function');
+ expect(builder.bar).toBeOfType('function');
+
+ var shown = false;
+ interimTest.show(builder);
+ $rootScope.$apply();
+ shown = true;
+ });
+ });
+
+ it('should add specified builder with defaults', function() {
+ createInterimProvider('interimTest')
+ .setDefaults({
+ options: function() {
+ return {
+ pizza: 'pepperoni'
+ };
+ },
+ methods: ['banana']
+ })
+ .addPreset('bob', {
+ options: function() {
+ return {
+ nut: 'almond'
+ };
+ },
+ methods: ['mango']
+ });
+ inject(function(interimTest, $rootScope) {
+ var shown = false;
+ var builder = interimTest.bob({
+ onShow: function(scope, element, options) {
+ expect(options.pizza).toBe('pepperoni');
+ expect(options.nut).toBe('almond');
+ expect(options.banana).toBe(1);
+ expect(options.mango).toBe(2);
+ shown = true;
+ }
+ });
+ builder.banana(1);
+ builder.mango(2);
+ interimTest.show(builder);
+ $rootScope.$apply();
+ expect(shown).toBe(true);
+
+ });
+ });
+
+ it('should show with proper options', function() {
+ createInterimProvider('interimTest')
+ .setDefaults({
+ options: function() {
+ return { key: 'defaultValue' };
+ }
+ })
+ .addPreset('preset', {
+ options: function() {
+ return { key2: 'defaultValue2' };
+ },
+ methods: ['key2']
+ });
+ inject(function(interimTest, $rootScope) {
+ interimTest.show();
+ expect($compilerSpy.mostRecentCall.args[0].key).toBe('defaultValue');
+
+ $compilerSpy.reset();
+ interimTest.show({
+ key: 'newValue'
+ });
+ expect($compilerSpy.mostRecentCall.args[0].key).toBe('newValue');
+
+ $compilerSpy.reset();
+ interimTest.show(interimTest.preset());
+ expect($compilerSpy.mostRecentCall.args[0].key).toBe('defaultValue');
+ expect($compilerSpy.mostRecentCall.args[0].key2).toBe('defaultValue2');
+
+ $compilerSpy.reset();
+ interimTest.show(
+ interimTest.preset({
+ key: 'newValue',
+ key2: 'newValue2'
+ })
+ );
+ expect($compilerSpy.mostRecentCall.args[0].key).toBe('newValue');
+ expect($compilerSpy.mostRecentCall.args[0].key2).toBe('newValue2');
+
+ $compilerSpy.reset();
+ interimTest.show(
+ interimTest.preset({
+ key2: 'newValue2'
+ }).key2('superNewValue2')
+ );
+ expect($compilerSpy.mostRecentCall.args[0].key).toBe('defaultValue');
+ expect($compilerSpy.mostRecentCall.args[0].key2).toBe('superNewValue2');
+ });
+ });
+
+ });
+
+ describe('a service', function() {
+ var Service;
+ beforeEach(function() {
+ setup();
+ inject(function($$interimElement) {
+ Service = $$interimElement();
+ });
+ });
+
+ describe('instance#show', function() {
+ it('inherits default options', inject(function($$interimElement) {
+ var defaults = { templateUrl: 'testing.html' };
+ Service.show(defaults);
+ expect($compilerSpy.mostRecentCall.args[0].templateUrl).toBe('testing.html');
+ }));
+
+ it('forwards options to $mdCompiler', inject(function($$interimElement) {
+ var options = {template: ''};
+ Service.show(options);
+ expect($compilerSpy.mostRecentCall.args[0].template).toBe('');
+ }));
+
+ it('supports theming', inject(function($$interimElement, $rootScope) {
+ Service.show({themable: true});
+ $rootScope.$digest();
+ expect($themingSpy).toHaveBeenCalled();
+ }));
+
+ it('calls hide after hideDelay', inject(function($animate, $timeout, $rootScope) {
+ var hideSpy = spyOn(Service, 'cancel').andCallThrough();
+ Service.show({hideDelay: 1000});
+ $rootScope.$digest();
+ $animate.triggerCallbacks();
+ $timeout.flush();
+ expect(hideSpy).toHaveBeenCalled();
+ }));
+
+ it('calls onRemove', inject(function($rootScope) {
+ var onRemoveCalled = false;
+ Service.show({
+ template: '',
+ isPassingOptions: true,
+ onRemove: onRemove
+ });
+ $rootScope.$digest();
+ Service.hide();
+ $rootScope.$digest();
+ expect(onRemoveCalled).toBe(true);
+
+ function onRemove(scope, el, options) {
+ onRemoveCalled = true;
+ expect(options.isPassingOptions).toBe(true);
+ expect(el[0]).toBeTruthy();
+ }
+ }));
+
+ it('returns a promise', inject(function($$interimElement) {
+ expect(typeof Service.show().then).toBe('function');
+ }));
+ });
+
+
+ describe('#hide', function() {
+ it('calls onRemove', inject(function($rootScope) {
+ var onRemoveCalled = false;
+ Service.show({
+ template: '',
+ passingOptions: true,
+ onRemove: onRemove
+ });
+ $rootScope.$digest();
+ Service.hide();
+ $rootScope.$digest();
+ expect(onRemoveCalled).toBe(true);
+
+ function onRemove(scope, el, options) {
+ onRemoveCalled = true;
+ expect(options.passingOptions).toBe(true);
+ expect(el[0]).toBeTruthy();
+ }
+ }));
+
+ it('resolves the show promise', inject(function($animate, $rootScope) {
+ var resolved = false;
+
+ Service.show().then(function(arg) {
+ expect(arg).toBe('test');
+ resolved = true;
+ });
+ $rootScope.$digest();
+ $animate.triggerCallbacks();
+ Service.hide('test');
+ $rootScope.$digest();
+ $animate.triggerCallbacks();
+ expect(resolved).toBe(true);
+ }));
+ });
+
+ describe('#cancel', function() {
+ it('calls onRemove', inject(function($rootScope) {
+ var onRemoveCalled = false;
+ Service.show({
+ template: '',
+ passingOptions: true,
+ onRemove: onRemove
+ });
+ $rootScope.$digest();
+ Service.cancel();
+ $rootScope.$digest();
+ expect(onRemoveCalled).toBe(true);
+
+ function onRemove(scope, el, options) {
+ onRemoveCalled = true;
+ expect(options.passingOptions).toBe(true);
+ expect(el[0]).toBeTruthy();
+ }
+ }));
+
+ it('rejects the show promise', inject(function($animate, $rootScope) {
+ var rejected = false;
+
+ Service.show().catch(function(arg) {
+ expect(arg).toBe('test');
+ rejected = true;
+ });
+ $rootScope.$digest();
+ $animate.triggerCallbacks();
+ Service.cancel('test');
+ $rootScope.$digest();
+ $animate.triggerCallbacks();
+ expect(rejected).toBe(true);
+ }));
+ });
+ });
+});
+
diff --git a/src/components/animate/inkCssRipple.js b/src/core/services/ripple/ripple.js
similarity index 64%
rename from src/components/animate/inkCssRipple.js
rename to src/core/services/ripple/ripple.js
index 2a3ca4c345d..faa111b2eba 100644
--- a/src/components/animate/inkCssRipple.js
+++ b/src/core/services/ripple/ripple.js
@@ -1,19 +1,13 @@
+(function() {
+'use strict';
-angular.module('material.animations')
-.directive('inkRipple', [
- '$mdInkRipple',
- InkRippleDirective
-])
-
-.factory('$mdInkRipple', [
- '$window',
- '$$rAF',
- '$mdEffects',
- '$timeout',
- '$mdUtil',
- InkRippleService
-]);
+angular.module('material.core')
+ .factory('$mdInkRipple', InkRippleService)
+ .directive('inkRipple', InkRippleDirective)
+ .directive('noink', attrNoDirective())
+ .directive('nobar', attrNoDirective())
+ .directive('nostretch', attrNoDirective());
function InkRippleDirective($mdInkRipple) {
return function(scope, element, attr) {
@@ -25,7 +19,7 @@ function InkRippleDirective($mdInkRipple) {
};
}
-function InkRippleService($window, $$rAF, $mdEffects, $timeout, $mdUtil) {
+function InkRippleService($window, $$rAF, $mdUtil, $timeout, $mdConstant) {
return {
attachButtonBehavior: attachButtonBehavior,
@@ -88,16 +82,17 @@ function InkRippleService($window, $$rAF, $mdEffects, $timeout, $mdUtil) {
};
function rippleIsAllowed() {
- return !$mdUtil.isParentDisabled(element, 2);
+ return !element[0].hasAttribute('disabled') &&
+ !(element[0].parentNode && element[0].parentNode.hasAttribute('disabled'));
}
function createRipple(left, top, positionsAreAbsolute) {
var rippleEl = angular.element('')
- .css($mdEffects.ANIMATION_DURATION, options.animationDuration + 'ms')
- .css($mdEffects.ANIMATION_NAME, options.animationName)
- .css($mdEffects.ANIMATION_TIMING, options.animationTimingFunction)
- .on($mdEffects.ANIMATIONEND_EVENT, function() {
+ .css($mdConstant.CSS.ANIMATION_DURATION, options.animationDuration + 'ms')
+ .css($mdConstant.CSS.ANIMATION_NAME, options.animationName)
+ .css($mdConstant.CSS.ANIMATION_TIMING, options.animationTimingFunction)
+ .on($mdConstant.CSS.ANIMATIONEND, function() {
rippleEl.remove();
});
@@ -133,7 +128,7 @@ function InkRippleService($window, $$rAF, $mdEffects, $timeout, $mdUtil) {
top: (top - containerWidth / 2) + 'px',
height: containerWidth + 'px'
};
- css[$mdEffects.ANIMATION_DURATION] = options.fadeoutDuration + 'ms';
+ css[$mdConstant.CSS.ANIMATION_DURATION] = options.fadeoutDuration + 'ms';
rippleEl.css(css);
return rippleEl;
@@ -146,7 +141,7 @@ function InkRippleService($window, $$rAF, $mdEffects, $timeout, $mdUtil) {
rippleEl = createRipple(ev.center.x, ev.center.y, true);
pauseTimeout = $timeout(function() {
- rippleEl && rippleEl.css($mdEffects.ANIMATION_PLAY_STATE, 'paused');
+ rippleEl && rippleEl.css($mdConstant.CSS.ANIMATION_PLAY_STATE, 'paused');
}, options.mousedownPauseTime, false);
rippleEl.on('$destroy', function() {
@@ -155,10 +150,50 @@ function InkRippleService($window, $$rAF, $mdEffects, $timeout, $mdUtil) {
} else if (ev.eventType === Hammer.INPUT_END && ev.isFinal) {
$timeout.cancel(pauseTimeout);
- rippleEl && rippleEl.css($mdEffects.ANIMATION_PLAY_STATE, '');
+ rippleEl && rippleEl.css($mdConstant.CSS.ANIMATION_PLAY_STATE, '');
}
}
}
}
+
+/**
+ * noink/nobar/nostretch directive: make any element that has one of
+ * these attributes be given a controller, so that other directives can
+ * `require:` these and see if there is a `no
` parent attribute.
+ *
+ * @usage
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * myApp.directive('detectNo', function() {
+ * return {
+ * require: ['^?noink', ^?nobar'],
+ * link: function(scope, element, attr, ctrls) {
+ * var noinkCtrl = ctrls[0];
+ * var nobarCtrl = ctrls[1];
+ * if (noInkCtrl) {
+ * alert("the noink flag has been specified on an ancestor!");
+ * }
+ * if (nobarCtrl) {
+ * alert("the nobar flag has been specified on an ancestor!");
+ * }
+ * }
+ * };
+ * });
+ *
+ */
+function attrNoDirective() {
+ return function() {
+ return {
+ controller: angular.noop
+ };
+ };
+}
+})();
diff --git a/src/services/theming/theming.js b/src/core/services/theming/theming.js
similarity index 89%
rename from src/services/theming/theming.js
rename to src/core/services/theming/theming.js
index dc847c89b60..7a8c5257500 100644
--- a/src/services/theming/theming.js
+++ b/src/core/services/theming/theming.js
@@ -1,26 +1,16 @@
-/*
- * @ngdoc module
- * @name material.services.theming
- * @description Used to provide theming to angular-material directives
- */
+(function() {
+'use strict';
+
-angular.module('material.services.theming', [
-])
-.directive('mdTheme', [
- '$interpolate',
- ThemingDirective
-])
-.directive('mdThemable', [
- '$mdTheming',
- ThemableDirective
-])
-.provider('$mdTheming', [
- Theming
-]);
+angular.module('material.core')
+ .directive('mdTheme', ThemingDirective)
+ .directive('mdThemable', ThemableDirective)
+ .provider('$mdTheming', Theming);
/**
* @ngdoc provider
* @name $mdThemingProvider
+ * @module material.core
*
* @description Provider to configure the `$mdTheming` service.
*/
@@ -132,3 +122,4 @@ function ThemingDirective($interpolate) {
function ThemableDirective($mdTheming) {
return $mdTheming;
}
+})();
diff --git a/src/services/theming/theming.spec.js b/src/core/services/theming/theming.spec.js
similarity index 94%
rename from src/services/theming/theming.spec.js
rename to src/core/services/theming/theming.spec.js
index 408cd69c3df..7181241f74c 100644
--- a/src/services/theming/theming.spec.js
+++ b/src/core/services/theming/theming.spec.js
@@ -1,6 +1,6 @@
describe('$mdTheming service', function() {
var $mdThemingProvider;
- beforeEach(module('material.services.theming', function(_$mdThemingProvider_) {
+ beforeEach(module('material.core', function(_$mdThemingProvider_) {
$mdThemingProvider = _$mdThemingProvider_;
}));
@@ -63,7 +63,7 @@ describe('$mdTheming service', function() {
});
describe('md-theme directive', function() {
- beforeEach(module('material.services.theming'));
+ beforeEach(module('material.core'));
it('should observe and set mdTheme controller', inject(function($compile, $rootScope) {
$rootScope.themey = 'red';
@@ -77,10 +77,8 @@ describe('md-theme directive', function() {
});
describe('md-themable directive', function() {
- beforeEach(module('material.services.theming'));
-
var $mdThemingProvider;
- beforeEach(module('material.services.theming', function(_$mdThemingProvider_) {
+ beforeEach(module('material.core', function(_$mdThemingProvider_) {
$mdThemingProvider = _$mdThemingProvider_;
}));
diff --git a/src/core/style/structure.scss b/src/core/style/structure.scss
index a9216314c07..50d06514409 100644
--- a/src/core/style/structure.scss
+++ b/src/core/style/structure.scss
@@ -217,3 +217,54 @@ input {
.md-shadow-animated.md-shadow {
transition: box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1);
}
+
+// Button ripple: keep same opacity, but expand to 0.75 scale.
+// Then, fade out and expand to 2.0 scale.
+@keyframes inkRippleButton {
+ 0% {
+ transform: scale(0);
+ opacity: 0.15;
+ }
+ 50% {
+ transform: scale(0.75);
+ opacity: 0.15;
+ }
+ 100% {
+ transform: scale(2.0);
+ opacity: 0;
+ }
+}
+
+// Checkbox ripple: fully expand, then fade out.
+@keyframes inkRippleCheckbox {
+ 0% {
+ transform: scale(0);
+ opacity: 0.4;
+ }
+ 50% {
+ transform: scale(1.0);
+ opacity: 0.4;
+ }
+ 100% {
+ transform: scale(1.0);
+ opacity: 0;
+ }
+}
+
+/*
+ * A container inside of a rippling element (eg a button),
+ * which contains all of the individual ripples
+ */
+.md-ripple-container {
+ pointer-events: none;
+ position: absolute;
+ overflow: hidden;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+}
+
+.md-ripple {
+ position: absolute;
+}
diff --git a/src/core/util/constant.js b/src/core/util/constant.js
index 6bdd1ac4604..c15ccb527f7 100644
--- a/src/core/util/constant.js
+++ b/src/core/util/constant.js
@@ -1,12 +1,38 @@
+(function() {
+
angular.module('material.core')
-.constant('$mdConstant', {
- KEY_CODE: {
- ENTER: 13,
- ESCAPE: 27,
- SPACE: 32,
- LEFT_ARROW : 37,
- UP_ARROW : 38,
- RIGHT_ARROW : 39,
- DOWN_ARROW : 40
+.factory('$mdConstant', function($$rAF, $sniffer) {
+
+ var webkit = /webkit/i.test($sniffer.vendorPrefix);
+ function vendorProperty(name) {
+ return webkit ? ('webkit' + name.charAt(0).toUpperCase() + name.substring(1)) : name;
}
+
+ return {
+ KEY_CODE: {
+ ENTER: 13,
+ ESCAPE: 27,
+ SPACE: 32,
+ LEFT_ARROW : 37,
+ UP_ARROW : 38,
+ RIGHT_ARROW : 39,
+ DOWN_ARROW : 40
+ },
+ CSS: {
+ /* Constants */
+ TRANSITIONEND: 'transitionend' + (webkit ? ' webkitTransitionEnd' : ''),
+ ANIMATIONEND: 'animationend' + (webkit ? ' webkitAnimationEnd' : ''),
+
+ TRANSFORM: vendorProperty('transform'),
+ TRANSITION: vendorProperty('transition'),
+ TRANSITION_DURATION: vendorProperty('transitionDuration'),
+ ANIMATION_PLAY_STATE: vendorProperty('animationPlayState'),
+ ANIMATION_DURATION: vendorProperty('animationDuration'),
+ ANIMATION_NAME: vendorProperty('animationName'),
+ ANIMATION_TIMING: vendorProperty('animationTimingFunction'),
+ ANIMATION_DIRECTION: vendorProperty('animationDirection')
+ }
+ };
});
+
+})();
diff --git a/src/core/util/iterator.spec.js b/src/core/util/iterator.spec.js
index e85c31c6de7..5c469d57035 100644
--- a/src/core/util/iterator.spec.js
+++ b/src/core/util/iterator.spec.js
@@ -289,11 +289,6 @@ describe('iterator', function() {
});
-
-
-
});
-
-
});
diff --git a/src/core/util/util.js b/src/core/util/util.js
index a5286629489..9867bec204d 100644
--- a/src/core/util/util.js
+++ b/src/core/util/util.js
@@ -8,72 +8,10 @@ var nextUniqueId = ['0','0','0'];
angular.module('material.core')
.factory('$mdUtil', ['$cacheFactory', function($cacheFactory) {
- var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
-
var Util;
return Util = {
now: window.performance ? angular.bind(window.performance, window.performance.now) : Date.now,
- /**
- * Checks if the specified element has an ancestor (ancestor being parent, grandparent, etc)
- * with the given attribute defined.
- *
- * Also pass in an optional `limit` (levels of ancestry to scan), default 4.
- */
- ancestorHasAttribute: function ancestorHasAttribute(element, attrName, limit) {
- limit = limit || 4;
- var current = element;
- while (limit-- && current.length) {
- if (current[0].hasAttribute && current[0].hasAttribute(attrName)) {
- return true;
- }
- current = current.parent();
- }
- return false;
- },
-
- /**
- * Checks to see if the element or its parents are disabled.
- * @param element DOM element to start scanning for `disabled` attribute
- * @param limit Number of parent levels that should be scanned; defaults to 4
- * @returns {*} Boolean
- */
- isParentDisabled: function isParentDisabled(element, limit) {
- return Util.ancestorHasAttribute(element, 'disabled', limit);
- },
-
- /**
- * Checks if two elements have the same parent
- */
- elementIsSibling: function elementIsSibling(element, otherElement) {
- return element.parent().length &&
- (element.parent()[0] === otherElement.parent()[0]);
- },
-
- /**
- * Converts snake_case to camelCase.
- * @param name Name to normalize
- */
- camelCase: function camelCase(name) {
- return name
- .replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
- return offset ? letter.toUpperCase() : letter;
- });
- },
-
- /**
- * Selects 'n' words from a string
- * for use in an HTML attribute
- */
- stringFromTextBody: function stringFromTextBody(textBody, numWords) {
- var string = textBody.trim();
-
- if(string.split(/\s+/).length > numWords){
- string = textBody.split(/\s+/).slice(1, (numWords + 1)).join(" ") + '...';
- }
- return string;
- },
-
/**
* Publish the iterator facade to easily support iteration and accessors
* @see iterator below
@@ -119,23 +57,6 @@ angular.module('material.core')
};
},
- /**
- * Wraps an element with a tag
- *
- * @param el element to wrap
- * @param tag tag to wrap it with
- * @param [className] optional class to apply to the wrapper
- * @returns new element
- *
- */
- wrap: function(el, tag, className) {
- if(el.hasOwnProperty(0)) { el = el[0]; }
- var wrapper = document.createElement(tag);
- wrapper.className += className;
- wrapper.appendChild(el.parentNode.replaceChild(wrapper, el));
- return angular.element(wrapper);
- },
-
/**
* nextUid, from angular.js.
* A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric
diff --git a/src/services/attrBind/attrBind.js b/src/services/attrBind/attrBind.js
deleted file mode 100644
index 6ce5dcabfa2..00000000000
--- a/src/services/attrBind/attrBind.js
+++ /dev/null
@@ -1,91 +0,0 @@
-angular.module('material.services.attrBind', [
-])
- .factory('$attrBind', [
- '$parse',
- '$interpolate',
- MdAttrBind
- ]);
-
-/**
- * This service allows directives to easily databind attributes to private scope properties.
- *
- * @private
- */
-function MdAttrBind($parse, $interpolate) {
- var LOCAL_REGEXP = /^\s*([@=&])(\??)\s*(\w*)\s*$/;
-
- return function (scope, attrs, bindDefinition, bindDefaults) {
- angular.forEach(bindDefinition || {}, function (definition, scopeName) {
- //Adapted from angular.js $compile
- var match = definition.match(LOCAL_REGEXP) || [],
- attrName = match[3] || scopeName,
- mode = match[1], // @, =, or &
- parentGet,
- unWatchFn;
-
- switch (mode) {
- case '@': // One-way binding from attribute into scope
-
- attrs.$observe(attrName, function (value) {
- scope[scopeName] = value;
- });
- attrs.$$observers[attrName].$$scope = scope;
-
- if (!bypassWithDefaults(attrName, scopeName)) {
- // we trigger an interpolation to ensure
- // the value is there for use immediately
- scope[scopeName] = $interpolate(attrs[attrName])(scope);
- }
- break;
-
- case '=': // Two-way binding...
-
- if (!bypassWithDefaults(attrName, scopeName)) {
- // Immediate evaluation
- scope[scopeName] = (attrs[attrName] === "") ? true : scope.$eval(attrs[attrName]);
-
- // Data-bind attribute to scope (incoming) and
- // auto-release watcher when scope is destroyed
-
- unWatchFn = scope.$watch(attrs[attrName], function (value) {
- scope[scopeName] = value;
- });
- scope.$on('$destroy', unWatchFn);
- }
-
- break;
-
- case '&': // execute an attribute-defined expression in the context of the parent scope
-
- if (!bypassWithDefaults(attrName, scopeName, angular.noop)) {
- /* jshint -W044 */
- if (attrs[attrName] && attrs[attrName].match(RegExp(scopeName + '\(.*?\)'))) {
- throw new Error('& expression binding "' + scopeName + '" looks like it will recursively call "' +
- attrs[attrName] + '" and cause a stack overflow! Please choose a different scopeName.');
- }
-
- parentGet = $parse(attrs[attrName]);
- scope[scopeName] = function (locals) {
- return parentGet(scope, locals);
- };
- }
-
- break;
- }
- });
-
- /**
- * Optional fallback value if attribute is not specified on element
- * @param scopeName
- */
- function bypassWithDefaults(attrName, scopeName, defaultVal) {
- if (!angular.isDefined(attrs[attrName])) {
- var hasDefault = bindDefaults && bindDefaults.hasOwnProperty(scopeName);
- scope[scopeName] = hasDefault ? bindDefaults[scopeName] : defaultVal;
- return true;
- }
- return false;
- }
-
- };
-}
diff --git a/src/services/interimElement/interimElement.js b/src/services/interimElement/interimElement.js
deleted file mode 100644
index 15b91a1faed..00000000000
--- a/src/services/interimElement/interimElement.js
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * @ngdoc module
- * @name material.services.interimElement
- * @description InterimElement
- */
-
-angular.module('material.services.interimElement', [
- 'material.services.compiler',
- 'material.services.theming'
-])
-.factory('$$interimElement', [
- '$q',
- '$rootScope',
- '$timeout',
- '$rootElement',
- '$animate',
- '$mdCompiler',
- '$mdTheming',
- InterimElementFactory
-]);
-
-/*
- * @ngdoc service
- * @name $$interimElement
- *
- * @description
- *
- * Factory that contructs `$$interimElement.$service` services.
- * Used internally in material design for elements that appear on screen temporarily.
- * The service provides a promise-like API for interacting with the temporary
- * elements.
- *
- * ```js
- * app.service('$mdToast', function($$interimElement) {
- * var $mdToast = $$interimElement(toastDefaultOptions);
- * return $mdToast;
- * });
- * ```
- * @param {object=} defaultOptions Options used by default for the `show` method on the service.
- *
- * @returns {$$interimElement.$service}
- *
- */
-
-function InterimElementFactory($q, $rootScope, $timeout, $rootElement, $animate, $mdCompiler, $mdTheming) {
-
- return function createInterimElementService(defaults) {
-
- /*
- * @ngdoc service
- * @name $$interimElement.$service
- *
- * @description
- * A service used to control inserting and removing an element into the DOM.
- *
- */
-
-
- var stack = [];
-
- defaults = angular.extend({
- onShow: function(scope, $el, options) {
- return $animate.enter($el, options.parent);
- },
- onRemove: function(scope, $el, options) {
- return $animate.leave($el);
- },
- }, defaults || {});
-
- var service;
- return service = {
- show: show,
- hide: hide,
- cancel: cancel
- };
-
- /*
- * @ngdoc method
- * @name $$interimElement.$service#show
- * @kind function
- *
- * @description
- * Compiles and inserts an element into the DOM.
- *
- * @param {Object} options Options object to compile with.
- *
- * @returns {Promise} Promise that will resolve when the service
- * has `#close()` or `#cancel()` called.
- *
- */
- function show(options) {
- if (stack.length) {
- service.hide();
- }
-
- var interimElement = new InterimElement(options);
- stack.push(interimElement);
- return interimElement.show().then(function() {
- return interimElement.deferred.promise;
- });
- }
-
- /*
- * @ngdoc method
- * @name $$interimElement.$service#hide
- * @kind function
- *
- * @description
- * Removes the `$interimElement` from the DOM and resolves the promise returned from `show`
- *
- * @param {*} resolveParam Data to resolve the promise with
- *
- * @returns undefined data that resolves after the element has been removed.
- *
- */
- function hide(success) {
- var interimElement = stack.shift();
- interimElement && interimElement.remove().then(function() {
- interimElement.deferred.resolve(success);
- });
- }
-
- /*
- * @ngdoc method
- * @name $$interimElement.$service#cancel
- * @kind function
- *
- * @description
- * Removes the `$interimElement` from the DOM and rejects the promise returned from `show`
- *
- * @param {*} reason Data to reject the promise with
- *
- * @returns undefined
- *
- */
- function cancel(reason) {
- var interimElement = stack.shift();
- interimElement && interimElement.remove().then(function() {
- interimElement.deferred.reject(reason);
- });
- }
-
-
- /*
- * Internal Interim Element Object
- * Used internally to manage the DOM element and related data
- */
- function InterimElement(options) {
- var self;
- var hideTimeout, element;
-
- options = options || {};
-
- options = angular.extend({
- scope: options.scope || $rootScope.$new(options.isolateScope)
- }, defaults, options);
-
- return self = {
- options: options,
- deferred: $q.defer(),
- show: function() {
- return $mdCompiler.compile(options).then(function(compiledData) {
- // Search for parent at insertion time, if not specified
- if (!options.parent) {
- options.parent = $rootElement.find('body');
- if (!options.parent.length) options.parent = $rootElement;
- }
- element = compiledData.link(options.scope);
- if (options.themable) $mdTheming(element);
- var ret = options.onShow(options.scope, element, options);
- return $q.when(ret)
- .then(function(){
- // Issue onComplete callback when the `show()` finishes
- var notify = options.onComplete || angular.noop;
- notify.apply(null, [options.scope, element, options]);
- })
- .then(startHideTimeout);
-
- function startHideTimeout() {
- if (options.hideDelay) {
- hideTimeout = $timeout(service.hide, options.hideDelay) ;
- }
- }
- });
- },
- cancelTimeout: function() {
- if (hideTimeout) {
- $timeout.cancel(hideTimeout);
- hideTimeout = undefined;
- }
- },
- remove: function() {
- self.cancelTimeout();
- var ret = options.onRemove(options.scope, element, options);
- return $q.when(ret).then(function() {
- options.scope.$destroy();
- });
- }
- };
- }
- };
-}
-
diff --git a/src/services/interimElement/interimElement.spec.js b/src/services/interimElement/interimElement.spec.js
deleted file mode 100644
index 8c4f919eb33..00000000000
--- a/src/services/interimElement/interimElement.spec.js
+++ /dev/null
@@ -1,157 +0,0 @@
-describe('$$interimElement service', function() {
- var $compilerSpy, $themingSpy, resolvingPromise;
-
- beforeEach(module('material.services.interimElement', 'ngAnimateMock', function($provide) {
- var $mdCompiler = { compile: angular.noop };
- $compilerSpy = spyOn($mdCompiler, 'compile');
- $themingSpy = jasmine.createSpy('$mdTheming');
-
- $provide.value('$mdCompiler', $mdCompiler);
- $provide.value('$mdTheming', $themingSpy);
- }));
-
- beforeEach(inject(function($q, $compile) {
- $compilerSpy.andCallFake(function(opts) {
- var el = $compile(opts.template);
- var deferred = $q.defer();
- deferred.resolve({
- link: el
- });
- return deferred.promise;
- });
- }));
-
- describe('instance', function() {
- var Service;
- beforeEach(inject(function($$interimElement) {
- Service = $$interimElement();
- }));
-
- describe('#show', function() {
- it('inherits default options', inject(function($$interimElement) {
- var defaults = { templateUrl: 'testing.html' };
- Service = $$interimElement(defaults);
- Service.show();
- expect($compilerSpy.mostRecentCall.args[0].templateUrl).toBe('testing.html');
- }));
-
- it('forwards options to $mdCompiler', inject(function($$interimElement) {
- var options = {template: ''};
- Service.show(options);
- expect($compilerSpy.mostRecentCall.args[0].template).toBe('');
- }));
-
- it('supports theming', inject(function($$interimElement, $rootScope) {
- Service.show({themable: true});
- $rootScope.$digest();
- expect($themingSpy).toHaveBeenCalled();
- }));
-
- it('calls hide after hideDelay', inject(function($animate, $timeout, $rootScope) {
- var hideSpy = spyOn(Service, 'hide').andCallThrough();
- Service.show({hideDelay: 1000});
- $rootScope.$digest();
- $animate.triggerCallbacks();
- $timeout.flush();
- expect(hideSpy).toHaveBeenCalled();
- }));
-
- it('calls onRemove', inject(function($rootScope) {
- var onRemoveCalled = false;
- Service.show({
- template: '',
- isPassingOptions: true,
- onRemove: onRemove
- });
- $rootScope.$digest();
- Service.hide();
- $rootScope.$digest();
- expect(onRemoveCalled).toBe(true);
-
- function onRemove(scope, el, options) {
- onRemoveCalled = true;
- expect(options.isPassingOptions).toBe(true);
- expect(el[0]).toBeTruthy();
- }
- }));
-
- it('returns a promise', inject(function($$interimElement) {
- expect(typeof Service.show().then).toBe('function');
- }));
- });
-
- describe('#hide', function() {
- it('calls onRemove', inject(function($rootScope) {
- var onRemoveCalled = false;
- Service.show({
- template: '',
- passingOptions: true,
- onRemove: onRemove
- });
- $rootScope.$digest();
- Service.hide();
- $rootScope.$digest();
- expect(onRemoveCalled).toBe(true);
-
- function onRemove(scope, el, options) {
- onRemoveCalled = true;
- expect(options.passingOptions).toBe(true);
- expect(el[0]).toBeTruthy();
- }
- }));
-
- it('resolves the show promise', inject(function($animate, $rootScope) {
- var resolved = false;
-
- Service.show().then(function(arg) {
- expect(arg).toBe('test');
- resolved = true;
- });
- $rootScope.$digest();
- $animate.triggerCallbacks();
- Service.hide('test');
- $rootScope.$digest();
- $animate.triggerCallbacks();
- expect(resolved).toBe(true);
- }));
- });
-
- describe('#cancel', function() {
- it('calls onRemove', inject(function($rootScope) {
- var onRemoveCalled = false;
- Service.show({
- template: '',
- passingOptions: true,
- onRemove: onRemove
- });
- $rootScope.$digest();
- Service.cancel();
- $rootScope.$digest();
- expect(onRemoveCalled).toBe(true);
-
- function onRemove(scope, el, options) {
- onRemoveCalled = true;
- expect(options.passingOptions).toBe(true);
- expect(el[0]).toBeTruthy();
- }
- }));
-
- it('rejects the show promise', inject(function($animate, $rootScope) {
- var rejected = false;
-
- Service.show().then(undefined, function(arg) {
- expect(arg).toBe('test');
- rejected = true;
- });
- $rootScope.$digest();
- $animate.triggerCallbacks();
- Service.cancel('test');
- $rootScope.$digest();
- $animate.triggerCallbacks();
- expect(rejected).toBe(true);
- }));
- });
-
- });
-});
-
diff --git a/src/services/media/media.js b/src/services/media/media.js
deleted file mode 100644
index 79e0faa038b..00000000000
--- a/src/services/media/media.js
+++ /dev/null
@@ -1,54 +0,0 @@
-angular.module('material.services.media', [
- 'material.core'
-])
-
-.factory('$mdMedia', [
- '$window',
- '$mdUtil',
- '$timeout',
- mdMediaFactory
-]);
-
-function mdMediaFactory($window, $mdUtil, $timeout) {
- var cache = $mdUtil.cacheFactory('$mdMedia', { capacity: 15 });
- var presets = {
- sm: '(min-width: 600px)',
- md: '(min-width: 960px)',
- lg: '(min-width: 1200px)'
- };
-
- angular.element($window).on('resize', updateAll);
-
- return $mdMedia;
-
- function $mdMedia(query) {
- query = validate(query);
- var result;
- if ( !angular.isDefined(result = cache.get(query)) ) {
- return add(query);
- }
- return result;
- }
-
- function validate(query) {
- return presets[query] || (
- query.charAt(0) != '(' ? ('(' + query + ')') : query
- );
- }
-
- function add(query) {
- return cache.put(query, !!$window.matchMedia(query).matches);
- }
-
- function updateAll() {
- var keys = cache.keys();
- if (keys.length) {
- for (var i = 0, ii = keys.length; i < ii; i++) {
- cache.put(keys[i], !!$window.matchMedia(keys[i]).matches);
- }
- // trigger a $digest()
- $timeout(angular.noop);
- }
- }
-
-}
diff --git a/src/services/registry/registry.js b/src/services/registry/registry.js
deleted file mode 100644
index 89e8092b60d..00000000000
--- a/src/services/registry/registry.js
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * @ngdoc module
- * @name material.services.registry
- *
- * @description
- * A component registry system for accessing various component instances in an app.
- */
-angular.module('material.services.registry', [
-])
- .factory('$mdComponentRegistry', [
- '$log',
- mdComponentRegistry
- ]);
-
-/*
- * @ngdoc service
- * @name $mdComponentRegistry
- * @module material.services.registry
- *
- * @description
- * $mdComponentRegistry enables the user to interact with multiple instances of
- * certain complex components in a running app.
- */
-function mdComponentRegistry($log) {
- var instances = [];
-
- return {
- /**
- * Used to print an error when an instance for a handle isn't found.
- */
- notFoundError: function(handle) {
- $log.error('No instance found for handle', handle);
- },
- /**
- * Return all registered instances as an array.
- */
- getInstances: function() {
- return instances;
- },
-
- /**
- * Get a registered instance.
- * @param handle the String handle to look up for a registered instance.
- */
- get: function(handle) {
- var i, j, instance;
- for(i = 0, j = instances.length; i < j; i++) {
- instance = instances[i];
- if(instance.$$mdHandle === handle) {
- return instance;
- }
- }
- return null;
- },
-
- /**
- * Register an instance.
- * @param instance the instance to register
- * @param handle the handle to identify the instance under.
- */
- register: function(instance, handle) {
- instance.$$mdHandle = handle;
- instances.push(instance);
-
- return function deregister() {
- var index = instances.indexOf(instance);
- if (index !== -1) {
- instances.splice(index, 1);
- }
- };
- }
- }
-}
-