From f11e259b0a9aceec738a9415d658299c871cb8c3 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 9 Dec 2014 12:08:31 -0700 Subject: [PATCH 1/3] refactor(): create internal `$mdUtil.pannable(scope, element, opts)` function --- src/core/util/util.js | 95 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 88 insertions(+), 7 deletions(-) diff --git a/src/core/util/util.js b/src/core/util/util.js index a25954843fa..c4fe191a447 100644 --- a/src/core/util/util.js +++ b/src/core/util/util.js @@ -1,20 +1,22 @@ (function() { 'use strict'; -/* +/* * This var has to be outside the angular factory, otherwise when * there are multiple material apps on the same page, each app - * will create its own instance of this array and the app's IDs + * will create its own instance of this array and the app's IDs * will not be unique. */ var nextUniqueId = ['0','0','0']; angular.module('material.core') -.factory('$mdUtil', ['$cacheFactory', function($cacheFactory) { +.factory('$mdUtil', function($cacheFactory, $document) { var Util; return Util = { now: window.performance ? angular.bind(window.performance, window.performance.now) : Date.now, + pannable: pannable, + /** * Publish the iterator facade to easily support iteration and accessors * @see iterator below @@ -291,7 +293,7 @@ angular.module('material.core') } /* - * Find the next item. If reloop is true and at the end of the list, it will + * Find the next item. If reloop is true and at the end of the list, it will * go back to the first item. If given ,the `validate` callback will be used * determine whether the next item is valid. If not valid, it will try to find the * next item again. @@ -313,7 +315,7 @@ angular.module('material.core') } /* - * Find the previous item. If reloop is true and at the beginning of the list, it will + * Find the previous item. If reloop is true and at the beginning of the list, it will * go back to the last item. If given ,the `validate` callback will be used * determine whether the previous item is valid. If not valid, it will try to find the * previous item again. @@ -351,6 +353,84 @@ angular.module('material.core') } } + function pannable(scope, element, options) { + // The state of the current pan + var pan; + // Whether the pointer is currently down on this element. + var pointerIsDown; + var START_EVENTS = 'mousedown touchstart pointerdown'; + var MOVE_EVENTS = 'mousemove touchmove pointermove'; + var END_EVENTS = 'mouseup mouseleave touchend touchcancel pointerup pointercancel'; + + // Listen to move and end events on document. End events especially could have bubbled up + // from the child. + element.on(START_EVENTS, startPan); + $document.on(MOVE_EVENTS, doPan) + .on(END_EVENTS, endPan); + + scope.$on('$destroy', cleanup); + + return cleanup; + + function cleanup() { + if (cleanup.called) return; + cleanup.called = true; + + element.off(START_EVENTS, startPan); + $document.off(MOVE_EVENTS, doPan) + .off(END_EVENTS, endPan); + pan = pointerIsDown = false; + } + + function startPan(ev) { + if (pointerIsDown) return; + pointerIsDown = true; + + pan = { + // Restrict this pan to whatever started it: if a mousedown started the pan, + // don't let anything but mouse events continue it. + pointerType: ev.type.charAt(0), + startX: getPosition(ev), + startTime: Util.now() + }; + + element.one('$mdPanStart', function(ev) { + // Allow user to cancel by preventing default + if (ev.defaultPrevented) pan = null; + }); + element.triggerHandler('$md.panstart', pan); + } + function doPan(ev) { + if (!pan || !isProperEventType(ev)) return; + + updatePanState(ev); + element.triggerHandler('$md.pan', pan); + } + function endPan(ev) { + pointerIsDown = false; + if (!pan || !isProperEventType(ev)) return; + + updatePanState(ev); + element.triggerHandler('$md.panend', pan); + pan = null; + } + + function updatePanState(ev) { + var x = getPosition(ev); + pan.distance = pan.startX - x; + pan.direction = pan.distance > 0 ? 'left' : (pan.distance < 0 ? 'right' : ''); + pan.time = pan.startTime - Util.now(); + pan.velocity = Math.abs(pan.distance) / pan.time; + } + function getPosition(ev) { + ev = ev.originalEvent || ev; //support jQuery events + return (ev.touches ? ev.touches[0] : ev).pageX; + } + function isProperEventType(ev) { + return pan && ev && (ev.type || '').charAt(0) === pan.pointerType; + } + } + /* * Angular's $cacheFactory doesn't have a keys() method, * so we add one ourself. @@ -376,9 +456,10 @@ angular.module('material.core') return cache; } -}]); -/* +}); + +/* * Since removing jQuery from the demos, some code that uses `element.focus()` is broken. * * We need to add `element.focus()`, because it's testable unlike `element[0].focus`. From ea42c1e518c34bdeee9a5ce80542f204befcdbcb Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 9 Dec 2014 12:10:04 -0700 Subject: [PATCH 2/3] refactor(): create $mdUtil.fakeNgModel() for when no ngModel supplied --- src/components/checkbox/checkbox.js | 13 ++----------- src/components/radioButton/radioButton.js | 6 ++---- src/core/util/util.js | 12 ++++++++++++ 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/components/checkbox/checkbox.js b/src/components/checkbox/checkbox.js index 34fc5cbda6e..b6bc5972fbb 100644 --- a/src/components/checkbox/checkbox.js +++ b/src/components/checkbox/checkbox.js @@ -46,9 +46,8 @@ angular.module('material.components.checkbox', [ * * */ -function MdCheckboxDirective(inputDirective, $mdInkRipple, $mdAria, $mdConstant, $mdTheming) { +function MdCheckboxDirective(inputDirective, $mdInkRipple, $mdAria, $mdConstant, $mdTheming, $mdUtil) { inputDirective = inputDirective[0]; - var CHECKED_CSS = 'md-checked'; return { @@ -74,18 +73,10 @@ function MdCheckboxDirective(inputDirective, $mdInkRipple, $mdAria, $mdConstant, tElement.attr('role', tAttrs.type); return function postLink(scope, element, attr, ngModelCtrl) { + ngModelCtrl = ngModelCtrl || $mdUtil.fakeNgModel(); var checked = false; $mdTheming(element); - // Create a mock ngModel if the user doesn't provide one - ngModelCtrl = ngModelCtrl || { - $setViewValue: function(value) { - this.$viewValue = value; - }, - $parsers: [], - $formatters: [] - }; - $mdAria.expectWithText(tElement, 'aria-label'); // Reuse the original input[type=checkbox] directive from Angular core. diff --git a/src/components/radioButton/radioButton.js b/src/components/radioButton/radioButton.js index c248f31e80b..1ea19c30bf5 100644 --- a/src/components/radioButton/radioButton.js +++ b/src/components/radioButton/radioButton.js @@ -61,10 +61,8 @@ function mdRadioGroupDirective($mdUtil, $mdConstant, $mdTheming) { function linkRadioGroup(scope, element, attr, ctrls) { $mdTheming(element); - var rgCtrl = ctrls[0], - ngModelCtrl = ctrls[1] || { - $setViewValue: angular.noop - }; + var rgCtrl = ctrls[0]; + var ngModelCtrl = ctrls[1] || $mdUtil.fakeNgModel(); function keydownListener(ev) { if (ev.keyCode === $mdConstant.KEY_CODE.LEFT_ARROW || ev.keyCode === $mdConstant.KEY_CODE.UP_ARROW) { diff --git a/src/core/util/util.js b/src/core/util/util.js index c4fe191a447..445092efab6 100644 --- a/src/core/util/util.js +++ b/src/core/util/util.js @@ -23,6 +23,18 @@ angular.module('material.core') */ iterator: iterator, + fakeNgModel: function() { + return { + $setViewValue: function(value) { + this.$viewValue = value; + this.$render(value); + }, + $parsers: [], + $formatters: [], + $render: angular.noop + }; + }, + /** * @see cacheFactory below */ From bb57297eed03c4d23decfecbf5ff887cce1bd5f6 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 9 Dec 2014 12:10:22 -0700 Subject: [PATCH 3/3] refactor(switch): refactor to match new spec --- src/components/checkbox/checkbox.js | 6 +- src/components/switch/_switch.scss | 80 +++++++++++----- .../switch/demoBasicUsage/index.html | 4 +- .../switch/demoBasicUsage/script.js | 8 +- src/components/switch/switch-theme.scss | 47 ++++++++-- src/components/switch/switch.js | 83 ++++++++++++++--- src/components/switch/switch.spec.js | 93 ++++++++++++++----- src/core/services/ripple/ripple.js | 9 +- src/core/util/util.js | 64 ++++++------- 9 files changed, 295 insertions(+), 99 deletions(-) diff --git a/src/components/checkbox/checkbox.js b/src/components/checkbox/checkbox.js index b6bc5972fbb..ea5d3bc7d80 100644 --- a/src/components/checkbox/checkbox.js +++ b/src/components/checkbox/checkbox.js @@ -87,7 +87,11 @@ function MdCheckboxDirective(inputDirective, $mdInkRipple, $mdAria, $mdConstant, 0: {} }, attr, [ngModelCtrl]); - element.on('click', listener); + // Used by switch. in Switch, we don't want click listeners; we have more granular + // touchup/touchdown listening. + if (!attr.mdNoClick) { + element.on('click', listener); + } element.on('keypress', keypressHandler); ngModelCtrl.$render = render; diff --git a/src/components/switch/_switch.scss b/src/components/switch/_switch.scss index 674f666819d..d123e2ed4f9 100644 --- a/src/components/switch/_switch.scss +++ b/src/components/switch/_switch.scss @@ -1,42 +1,80 @@ -$switch-width: $baseline-grid * 8; +$switch-width: 36px !default; +$switch-height: $baseline-grid * 3 !default; +$switch-bar-height: 14px !default; +$switch-thumb-size: 20px !default; md-switch { - display: block; - position: relative; - height: $baseline-grid * 3; - margin: $baseline-grid; display: flex; align-items: center; - .md-switch-bar { + .md-container { + width: $switch-width; + height: $switch-height; + position: relative; + user-select: none; + margin-right: 8px; + } + + .md-text { + border: 1px solid transparent; + } + + .md-bar { + left: 1px; + width: $switch-width - 2px; + top: $switch-height / 2 - $switch-bar-height / 2; + height: $switch-bar-height; + border-radius: 8px; position: absolute; - left: $baseline-grid * 2; - top: $baseline-grid * 1.5; - width: $baseline-grid * 4; - height: 1px; - pointer-events: none; } - /* used also in _radio-button.scss */ - .md-switch-thumb { + .md-thumb-container { + top: $switch-height / 2 - $switch-thumb-size / 2; + left: 0; + width: $switch-width - $switch-thumb-size; + position: absolute; + transform: translate3d(0,0,0); + z-index: 1; + } + &.md-checked .md-thumb-container { + transform: translate3d(100%,0,0); + } + + .md-thumb { position: absolute; margin: 0; left: 0; top: 0; outline: none; + height: $switch-thumb-size; + width: $switch-thumb-size; + border-radius: 50%; + box-shadow: $whiteframe-shadow-z1; - .md-container { + .md-ripple-container { position: absolute; - transition: transform 0.2s linear; - transform: translate3d(0,0,0); - } - &.md-checked .md-container { - transform: translate3d($switch-width - 16,0,0); + display: block; + width: auto; + height: auto; + left: -$switch-thumb-size; + top: -$switch-thumb-size; + right: -$switch-thumb-size; + bottom: -$switch-thumb-size; } + } - .md-label { - margin-left: $baseline-grid * 9; + &.transition { + .md-bar, + .md-thumb-container, + .md-thumb { + transition: $swift-ease-in-out; + transition-property: transform, background-color; + } + .md-bar, + .md-thumb { + transition-delay: 0.05s; } } + } diff --git a/src/components/switch/demoBasicUsage/index.html b/src/components/switch/demoBasicUsage/index.html index c400d520568..cceb0f8279a 100644 --- a/src/components/switch/demoBasicUsage/index.html +++ b/src/components/switch/demoBasicUsage/index.html @@ -1,4 +1,4 @@ -
+
Switch 1: {{ data.cb1 }} @@ -11,7 +11,7 @@ Switch (Disabled) - + Switch (Disabled, Active) diff --git a/src/components/switch/demoBasicUsage/script.js b/src/components/switch/demoBasicUsage/script.js index 90489e17770..afbe39b0a57 100644 --- a/src/components/switch/demoBasicUsage/script.js +++ b/src/components/switch/demoBasicUsage/script.js @@ -1 +1,7 @@ -angular.module('switchDemo1', ['ngMaterial']); +angular.module('switchDemo1', ['ngMaterial']) +.controller('SwitchDemoCtrl', function($scope) { + $scope.data = { + cb1: true, + cb4: true + }; +}); diff --git a/src/components/switch/switch-theme.scss b/src/components/switch/switch-theme.scss index e0f461631c9..a7b1aa8b140 100644 --- a/src/components/switch/switch-theme.scss +++ b/src/components/switch/switch-theme.scss @@ -1,13 +1,46 @@ -$switch-color: $foreground-secondary-color !default; -$switch-focus-color: map-get($foreground-color-palette, '1000'); +$switch-color-palette: $primary-color-palette !default; +$switch-off-color-palette: $foreground-color-palette !default; + +$switch-on-color: map-get($switch-color-palette, '500') !default; +$switch-on-bar-color: rgba($switch-on-color, 0.5) !default; + +$switch-off-color: map-get($switch-off-color-palette, '50') !default; +$switch-off-bar-color: map-get($switch-off-color-palette, '500') !default; + +$switch-disabled-color: map-get($switch-off-color-palette, '400') !default; +$switch-disabled-bar-color: rgba(#000, 0.12); md-switch.md-#{$theme-name}-theme { - .md-switch-bar { - background-color: $switch-color; + .md-thumb { + background-color: $switch-off-color; + } + .md-bar { + background-color: $switch-off-bar-color; + } + + &.md-checked { + .md-thumb { + background-color: $switch-on-color; + } + .md-bar { + background-color: $switch-on-bar-color; + } } - .md-switch-thumb { - &:focus .md-label { - border: 1px dotted black; + + &[disabled] { + .md-thumb { + background-color: $switch-disabled-color; + } + .md-bar { + background-color: $switch-disabled-bar-color; } } + + &:focus { + .md-text { + border-color: black; + border-style: dotted; + } + } + } diff --git a/src/components/switch/switch.js b/src/components/switch/switch.js index b4ec806a0fc..8a3bb0ef34e 100644 --- a/src/components/switch/switch.js +++ b/src/components/switch/switch.js @@ -9,8 +9,7 @@ angular.module('material.components.switch', [ 'material.core', - 'material.components.checkbox', - 'material.components.radioButton' + 'material.components.checkbox' ]) .directive('mdSwitch', MdSwitch); @@ -47,30 +46,90 @@ angular.module('material.components.switch', [ * * */ -function MdSwitch(mdCheckboxDirective, mdRadioButtonDirective, $mdTheming) { +function MdSwitch(mdCheckboxDirective, $mdTheming, $mdUtil, $document, $mdConstant, $parse, $$rAF) { var checkboxDirective = mdCheckboxDirective[0]; - var radioButtonDirective = mdRadioButtonDirective[0]; return { restrict: 'E', transclude: true, template: - '
' + - '
' + - radioButtonDirective.template + + '
' + + '
' + + '
' + + '
' + + '
'+ + '
' + + '
' + '
', require: '?ngModel', compile: compile }; function compile(element, attr) { - var thumb = angular.element(element[0].querySelector('.md-switch-thumb')); - var checkboxLink = checkboxDirective.compile(thumb, attr); + var checkboxLink = checkboxDirective.compile(element, attr); - return function (scope, element, attr, ngModelCtrl) { - $mdTheming(element); - return checkboxLink(scope, thumb, attr, ngModelCtrl); + return function (scope, element, attr, ngModel) { + ngModel = ngModel || $mdUtil.fakeNgModel(); + var disabledGetter = $parse(attr.ngDisabled); + var thumbContainer = angular.element(element[0].querySelector('.md-thumb-container')); + var switchContainer = angular.element(element[0].querySelector('.md-container')); + + // no transition on initial load + $$rAF(function() { + element.addClass('transition'); + }); + + // Tell the checkbox we don't want a click listener. + // Our drag listener tells us everything, using more granular events. + attr.mdNoClick = true; + checkboxLink(scope, element, attr, ngModel); + + $mdUtil.attachDragBehavior(scope, switchContainer); + + // These events are triggered by setup drag + switchContainer.on('$md.dragstart', onPanStart) + .on('$md.drag', onPan) + .on('$md.dragend', onPanEnd); + + function onPanStart(ev, drag) { + // Don't go if ng-disabled===true + if (disabledGetter(scope)) return ev.preventDefault(); + + drag.width = thumbContainer.prop('offsetWidth'); + element.removeClass('transition'); + } + function onPan(ev, drag) { + var percent = drag.distance / drag.width; + + //if checked, start from right. else, start from left + var translate = ngModel.$viewValue ? 1 - percent : -percent; + // Make sure the switch stays inside its bounds, 0-1% + translate = Math.max(0, Math.min(1, translate)); + + thumbContainer.css($mdConstant.CSS.TRANSFORM, 'translate3d(' + (100*translate) + '%,0,0)'); + drag.translate = translate; + } + function onPanEnd(ev, drag) { + if (disabledGetter(scope)) return false; + + element.addClass('transition'); + thumbContainer.css($mdConstant.CSS.TRANSFORM, ''); + + // We changed if there is no distance (this is a click a click), + // or if the drag distance is >50% of the total. + var isChanged = Math.abs(drag.distance || 0) < 5 || + (ngModel.$viewValue ? drag.translate < 0.5 : drag.translate > 0.5); + if (isChanged) { + scope.$apply(function() { + ngModel.$setViewValue(!ngModel.$viewValue); + ngModel.$render(); + }); + } + } }; } + + } + })(); diff --git a/src/components/switch/switch.spec.js b/src/components/switch/switch.spec.js index 3d78a50418f..4a4f1efd1f2 100644 --- a/src/components/switch/switch.spec.js +++ b/src/components/switch/switch.spec.js @@ -2,8 +2,7 @@ describe('', function() { var CHECKED_CSS = 'md-checked'; beforeEach(TestUtil.mockRaf); - beforeEach(module('ngAria')); - beforeEach(module('material.components.switch')); + beforeEach(module('ngAria', 'material.components.switch')); it('should set checked css class and aria-checked attributes', inject(function($compile, $rootScope) { var element = $compile('
' + @@ -18,33 +17,85 @@ describe('', function() { $rootScope.green = true; }); - var cbElements = angular.element(element[0].querySelectorAll('.md-switch-thumb')); + var switches = angular.element(element[0].querySelectorAll('md-switch')); - expect(cbElements.eq(0).hasClass(CHECKED_CSS)).toEqual(false); - expect(cbElements.eq(1).hasClass(CHECKED_CSS)).toEqual(true); - // expect(cbElements.eq(0).attr('aria-checked')).toEqual('false'); - // expect(cbElements.eq(1).attr('aria-checked')).toEqual('true'); - expect(cbElements.eq(0).attr('role')).toEqual('checkbox'); + expect(switches.eq(0).hasClass(CHECKED_CSS)).toEqual(false); + expect(switches.eq(1).hasClass(CHECKED_CSS)).toEqual(true); + expect(switches.eq(0).attr('aria-checked')).toEqual('false'); + expect(switches.eq(1).attr('aria-checked')).toEqual('true'); + expect(switches.eq(0).attr('role')).toEqual('checkbox'); + + $rootScope.$apply(function(){ + $rootScope.blue = true; + $rootScope.green = false; + }); + + expect(switches.eq(1).hasClass(CHECKED_CSS)).toEqual(false); + expect(switches.eq(0).hasClass(CHECKED_CSS)).toEqual(true); + expect(switches.eq(1).attr('aria-checked')).toEqual('false'); + expect(switches.eq(0).attr('aria-checked')).toEqual('true'); + expect(switches.eq(1).attr('role')).toEqual('checkbox'); })); - it('should be disabled with disabled attr', inject(function($compile, $rootScope) { - var element = $compile('
' + - '' + - '' + - '
')($rootScope); + it('should change on panstart/panend if distance < 5', inject(function($compile, $rootScope) { + var element = $compile('')($rootScope); + var switchContainer = angular.element(element[0].querySelector('.md-container')); + + $rootScope.$apply('banana = false'); + + expect($rootScope.banana).toBe(false); + expect(element.hasClass(CHECKED_CSS)).toBe(false); + + switchContainer.triggerHandler('$md.dragstart', {}); + switchContainer.triggerHandler('$md.dragend', {distance: 3}); + + expect($rootScope.banana).toBe(true); + expect(element.hasClass(CHECKED_CSS)).toBe(true); + + switchContainer.triggerHandler('$md.dragstart', {}); + switchContainer.triggerHandler('$md.dragend', {distance: 15}); + + expect($rootScope.banana).toBe(true); + expect(element.hasClass(CHECKED_CSS)).toBe(true); + + switchContainer.triggerHandler('$md.dragstart', {}); + switchContainer.triggerHandler('$md.dragend', {distance: -4}); + + expect($rootScope.banana).toBe(false); + expect(element.hasClass(CHECKED_CSS)).toBe(false); + })); + + it('should check on panend if translate > 50%', inject(function($compile, $rootScope) { + var element = $compile('')($rootScope); + var switchContainer = angular.element(element[0].querySelector('.md-container')); + var drag; + + drag = { distance: -55 }; + switchContainer.triggerHandler('$md.dragstart', {}); + drag.width = 100; + switchContainer.triggerHandler('$md.drag', drag); + switchContainer.triggerHandler('$md.dragend', drag); - var switchThumb = angular.element(element[0].querySelectorAll('.md-switch-thumb')); + expect($rootScope.banana).toBe(true); + expect(element.hasClass(CHECKED_CSS)).toBe(true); - $rootScope.$apply('blue = false'); - switchThumb.attr('disabled', 'true'); + drag = { distance: 45 }; + switchContainer.triggerHandler('$md.dragstart', {}); + drag.width = 100; + switchContainer.triggerHandler('$md.drag', drag); + switchContainer.triggerHandler('$md.dragend', drag); - switchThumb.triggerHandler('click'); - expect($rootScope.blue).toBe(false); + expect($rootScope.banana).toBe(true); + expect(element.hasClass(CHECKED_CSS)).toBe(true); - switchThumb.removeAttr('disabled'); + drag = { distance: 85 }; + switchContainer.triggerHandler('$md.dragstart', {}); + drag.width = 100; + switchContainer.triggerHandler('$md.drag', drag); + switchContainer.triggerHandler('$md.dragend', drag); - switchThumb.triggerHandler('click'); - expect($rootScope.blue).toBe(true); + expect($rootScope.banana).toBe(false); + expect(element.hasClass(CHECKED_CSS)).toBe(false); })); }); diff --git a/src/core/services/ripple/ripple.js b/src/core/services/ripple/ripple.js index 0f397cc83f5..1834f20d8d8 100644 --- a/src/core/services/ripple/ripple.js +++ b/src/core/services/ripple/ripple.js @@ -347,7 +347,12 @@ function InkRippleService($window, $timeout) { */ function isRippleAllowed() { var parent = node.parentNode; - return !node.hasAttribute('disabled') && !(parent && parent.hasAttribute('disabled')); + var grandparent = parent && parent.parentNode; + var ancestor = grandparent && grandparent.parentNode; + return !node.hasAttribute('disabled') && + !(parent && parent.hasAttribute('disabled')) && + !(grandparent && grandparent.hasAttribute('disabled')) && + !(ancestor && ancestor.hasAttribute('disabled')); } } } @@ -355,7 +360,7 @@ function InkRippleService($window, $timeout) { /** * noink/nobar/nostretch directive: make any element that has one of - * these attributes be given a controller, so that other directives can + * these attributes be given a controller, so that other directives can * `require:` these and see if there is a `no` parent attribute. * * @usage diff --git a/src/core/util/util.js b/src/core/util/util.js index 445092efab6..ab5883567b3 100644 --- a/src/core/util/util.js +++ b/src/core/util/util.js @@ -15,7 +15,7 @@ angular.module('material.core') return Util = { now: window.performance ? angular.bind(window.performance, window.performance.now) : Date.now, - pannable: pannable, + attachDragBehavior: attachDragBehavior, /** * Publish the iterator facade to easily support iteration and accessors @@ -365,9 +365,9 @@ angular.module('material.core') } } - function pannable(scope, element, options) { - // The state of the current pan - var pan; + function attachDragBehavior(scope, element, options) { + // The state of the current drag + var drag; // Whether the pointer is currently down on this element. var pointerIsDown; var START_EVENTS = 'mousedown touchstart pointerdown'; @@ -376,9 +376,9 @@ angular.module('material.core') // Listen to move and end events on document. End events especially could have bubbled up // from the child. - element.on(START_EVENTS, startPan); - $document.on(MOVE_EVENTS, doPan) - .on(END_EVENTS, endPan); + element.on(START_EVENTS, startDrag); + $document.on(MOVE_EVENTS, doDrag) + .on(END_EVENTS, endDrag); scope.$on('$destroy', cleanup); @@ -388,58 +388,58 @@ angular.module('material.core') if (cleanup.called) return; cleanup.called = true; - element.off(START_EVENTS, startPan); - $document.off(MOVE_EVENTS, doPan) - .off(END_EVENTS, endPan); - pan = pointerIsDown = false; + element.off(START_EVENTS, startDrag); + $document.off(MOVE_EVENTS, doDrag) + .off(END_EVENTS, endDrag); + drag = pointerIsDown = false; } - function startPan(ev) { + function startDrag(ev) { if (pointerIsDown) return; pointerIsDown = true; - pan = { - // Restrict this pan to whatever started it: if a mousedown started the pan, + drag = { + // Restrict this drag to whatever started it: if a mousedown started the drag, // don't let anything but mouse events continue it. pointerType: ev.type.charAt(0), startX: getPosition(ev), startTime: Util.now() }; - element.one('$mdPanStart', function(ev) { + element.one('$md.dragstart', function(ev) { // Allow user to cancel by preventing default - if (ev.defaultPrevented) pan = null; + if (ev.defaultPrevented) drag = null; }); - element.triggerHandler('$md.panstart', pan); + element.triggerHandler('$md.dragstart', drag); } - function doPan(ev) { - if (!pan || !isProperEventType(ev)) return; + function doDrag(ev) { + if (!drag || !isProperEventType(ev)) return; - updatePanState(ev); - element.triggerHandler('$md.pan', pan); + updateDragState(ev); + element.triggerHandler('$md.drag', drag); } - function endPan(ev) { + function endDrag(ev) { pointerIsDown = false; - if (!pan || !isProperEventType(ev)) return; + if (!drag || !isProperEventType(ev)) return; - updatePanState(ev); - element.triggerHandler('$md.panend', pan); - pan = null; + updateDragState(ev); + element.triggerHandler('$md.dragend', drag); + drag = null; } - function updatePanState(ev) { + function updateDragState(ev) { var x = getPosition(ev); - pan.distance = pan.startX - x; - pan.direction = pan.distance > 0 ? 'left' : (pan.distance < 0 ? 'right' : ''); - pan.time = pan.startTime - Util.now(); - pan.velocity = Math.abs(pan.distance) / pan.time; + drag.distance = drag.startX - x; + drag.direction = drag.distance > 0 ? 'left' : (drag.distance < 0 ? 'right' : ''); + drag.time = drag.startTime - Util.now(); + drag.velocity = Math.abs(drag.distance) / drag.time; } function getPosition(ev) { ev = ev.originalEvent || ev; //support jQuery events return (ev.touches ? ev.touches[0] : ev).pageX; } function isProperEventType(ev) { - return pan && ev && (ev.type || '').charAt(0) === pan.pointerType; + return drag && ev && (ev.type || '').charAt(0) === drag.pointerType; } }