diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000000..d60776a4c2 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,265 @@ +{ + "extends": "eslint:recommended", + "rules": { + "accessor-pairs": "error", + "array-bracket-newline": "off", + "array-bracket-spacing": "off", + "array-callback-return": "error", + "array-element-newline": "off", + "arrow-body-style": "error", + "arrow-parens": "error", + "arrow-spacing": "error", + "block-scoped-var": "off", + "block-spacing": "off", + "brace-style": "off", + "callback-return": "off", + "camelcase": "off", + "capitalized-comments": "off", + "class-methods-use-this": "error", + "comma-dangle": "off", + "comma-spacing": "off", + "comma-style": [ + "error", + "last" + ], + "complexity": "off", + "computed-property-spacing": "off", + "consistent-return": "off", + "consistent-this": "off", + "curly": "off", + "default-case": "off", + "dot-location": "off", + "dot-notation": "off", + "eol-last": "off", + "eqeqeq": "off", + "for-direction": "error", + "func-call-spacing": "off", + "func-name-matching": "off", + "func-names": "off", + "func-style": "off", + "generator-star-spacing": "error", + "getter-return": "error", + "global-require": "error", + "guard-for-in": "off", + "handle-callback-err": "error", + "id-blacklist": "error", + "id-length": "off", + "id-match": "error", + "indent": "off", + "indent-legacy": "off", + "init-declarations": "off", + "jsx-quotes": "error", + "key-spacing": "off", + "keyword-spacing": "off", + "line-comment-position": "off", + "linebreak-style": "off", + "lines-around-comment": "off", + "lines-around-directive": "off", + "max-depth": "error", + "max-len": "off", + "max-lines": "off", + "max-nested-callbacks": "error", + "max-params": "off", + "max-statements": "off", + "max-statements-per-line": "off", + "multiline-ternary": "off", + "new-parens": "error", + "newline-after-var": "off", + "newline-before-return": "off", + "newline-per-chained-call": "off", + "no-alert": "off", + "no-array-constructor": "error", + "no-await-in-loop": "error", + "no-bitwise": "off", + "no-buffer-constructor": "error", + "no-caller": "error", + "no-catch-shadow": "error", + "no-confusing-arrow": "error", + "no-constant-condition": [ + "error", + { + "checkLoops": false + } + ], + "no-continue": "off", + "no-div-regex": "error", + "no-duplicate-imports": "error", + "no-else-return": "off", + "no-empty-function": "off", + "no-eq-null": "off", + "no-eval": "error", + "no-extend-native": "off", + "no-extra-bind": "off", + "no-extra-label": "error", + "no-extra-parens": "off", + "no-floating-decimal": "error", + "no-implicit-coercion": [ + "error", + { + "boolean": false, + "number": false, + "string": false + } + ], + "no-implicit-globals": "off", + "no-implied-eval": "error", + "no-inline-comments": "off", + "no-inner-declarations": [ + "error", + "functions" + ], + "no-invalid-this": "off", + "no-iterator": "error", + "no-label-var": "error", + "no-labels": "error", + "no-lone-blocks": "error", + "no-lonely-if": "off", + "no-loop-func": "error", + "no-magic-numbers": "off", + "no-mixed-operators": "off", + "no-mixed-requires": "error", + "no-multi-assign": "off", + "no-multi-spaces": "off", + "no-multi-str": "off", + "no-multiple-empty-lines": "off", + "no-native-reassign": "error", + "no-negated-condition": "off", + "no-negated-in-lhs": "error", + "no-nested-ternary": "off", + "no-new": "error", + "no-new-func": "error", + "no-new-object": "error", + "no-new-require": "error", + "no-new-wrappers": "error", + "no-octal-escape": "error", + "no-param-reassign": "off", + "no-path-concat": "error", + "no-plusplus": "off", + "no-process-env": "error", + "no-process-exit": "error", + "no-proto": "error", + "no-prototype-builtins": "off", + "no-restricted-globals": "error", + "no-restricted-imports": "error", + "no-restricted-modules": "error", + "no-restricted-properties": "error", + "no-restricted-syntax": "error", + "no-return-assign": "off", + "no-return-await": "error", + "no-script-url": "error", + "no-self-compare": "error", + "no-sequences": "off", + "no-shadow": "off", + "no-shadow-restricted-names": "error", + "no-spaced-func": "off", + "no-sync": "error", + "no-tabs": "off", + "no-template-curly-in-string": "error", + "no-ternary": "off", + "no-throw-literal": "off", + "no-trailing-spaces": "off", + "no-undef-init": "error", + "no-undefined": "off", + "no-underscore-dangle": "off", + "no-unmodified-loop-condition": "error", + "no-unneeded-ternary": "off", + "no-unused-expressions": "off", + "no-use-before-define": "off", + "no-useless-call": "error", + "no-useless-computed-key": "error", + "no-useless-concat": "off", + "no-useless-constructor": "error", + "no-useless-rename": "error", + "no-useless-return": "error", + "no-var": "off", + "no-void": "off", + "no-warning-comments": "off", + "no-whitespace-before-property": "error", + "no-with": "error", + "nonblock-statement-body-position": [ + "error", + "any" + ], + "object-curly-newline": "off", + "object-curly-spacing": "off", + "object-property-newline": "off", + "object-shorthand": "off", + "one-var": "off", + "one-var-declaration-per-line": "off", + "operator-assignment": "off", + "operator-linebreak": "off", + "padded-blocks": "off", + "padding-line-between-statements": "error", + "prefer-arrow-callback": "off", + "prefer-const": "error", + "prefer-destructuring": "off", + "prefer-numeric-literals": "error", + "prefer-promise-reject-errors": "error", + "prefer-reflect": "off", + "prefer-rest-params": "off", + "prefer-spread": "off", + "prefer-template": "off", + "quote-props": "off", + "quotes": "off", + "radix": "off", + "require-await": "error", + "require-jsdoc": "off", + "rest-spread-spacing": "error", + "semi": "off", + "semi-spacing": "off", + "semi-style": "off", + "sort-imports": "error", + "sort-keys": "off", + "sort-vars": "off", + "space-before-blocks": "off", + "space-before-function-paren": "off", + "space-in-parens": "off", + "space-infix-ops": "off", + "space-unary-ops": "off", + "spaced-comment": "off", + "strict": "off", + "switch-colon-spacing": "off", + "symbol-description": "error", + "template-curly-spacing": "error", + "template-tag-spacing": "error", + "unicode-bom": [ + "error", + "never" + ], + "valid-jsdoc": "off", + "vars-on-top": "off", + "wrap-iife": "off", + "wrap-regex": "off", + "yield-star-spacing": "error", + "yoda": "off", + "no-unused-vars": "off", + "no-cond-assign": "off", + "no-unexpected-multiline": "off" + }, + "globals": { + "angular": true + }, + "overrides": [ + { + "files": ["**/*.spec.js"], + "env": { + "browser": true, + "jasmine": true + }, + "rules": { + "no-native-reassign": "off", + "no-global-assign": "off" + }, + "globals": { + "module": true, + "inject": true, + "disableAnimations": true, + "createMockStyleSheet": true, + "$mdUtil": false, + "$timeout": false, + "$animate": false, + "$material": false + } + } + ] +} diff --git a/.travis.yml b/.travis.yml index 98900dda74..5c49bdffb8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,6 +28,7 @@ jobs: - env: "NG_VERSION=1.5" - env: "NG_VERSION=1.6" - env: "NG_VERSION=snapshot" + - env: "RUN_LINT=true" - stage: Deploy script: ./scripts/travis-build-init.sh --sha=$TRAVIS_COMMIT env: "MODE=release" diff --git a/package.json b/package.json index 61a3ad5dfb..9d572bc8c7 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "conventional-changelog": "^1.1.0", "dgeni": "^0.4.1", "dgeni-packages": "^0.13.0", + "eslint": "^4.3.0", "github-contributors-list": "^1.2.1", "glob": "~7.0.5", "gulp": "^3.9.1", @@ -83,6 +84,7 @@ "docs:build": "gulp docs", "docs:watch": "gulp watch site --dev", "test:fast": "gulp karma-fast", - "test:full": "gulp karma" + "test:full": "gulp karma", + "lint": "eslint src" } -} \ No newline at end of file +} diff --git a/scripts/travis-run-script.sh b/scripts/travis-run-script.sh index 6aa48c2620..6007af7369 100755 --- a/scripts/travis-run-script.sh +++ b/scripts/travis-run-script.sh @@ -6,6 +6,11 @@ set -xe # Ensure that scripts will run from project dir. cd $(dirname $0)/.. +if [[ -n "$RUN_LINT" ]]; then + npm run lint + exit +fi + # When Travis CI specifies an AngularJS version, try to install those for tests. if [[ -n "$NG_VERSION" ]]; then ./scripts/fetch-angular-version.sh "$NG_VERSION" diff --git a/src/components/autocomplete/demoBasicUsage/index.html b/src/components/autocomplete/demoBasicUsage/index.html index c23b66cc0f..3dd4dad474 100644 --- a/src/components/autocomplete/demoBasicUsage/index.html +++ b/src/components/autocomplete/demoBasicUsage/index.html @@ -18,7 +18,7 @@ No states matching "{{ctrl.searchText}}" were found. - Create a new one! + Create a new one!
diff --git a/src/components/autocomplete/demoBasicUsage/script.js b/src/components/autocomplete/demoBasicUsage/script.js index 35b757d69d..d6af45ec62 100644 --- a/src/components/autocomplete/demoBasicUsage/script.js +++ b/src/components/autocomplete/demoBasicUsage/script.js @@ -4,7 +4,7 @@ .module('autocompleteDemo', ['ngMaterial']) .controller('DemoCtrl', DemoCtrl); - function DemoCtrl ($timeout, $q, $log) { + function DemoCtrl ($timeout, $q, $log, $mdDialog) { var self = this; self.simulateQuery = false; @@ -18,8 +18,14 @@ self.newState = newState; - function newState(state) { - alert("Sorry! You'll need to create a Constitution for " + state + " first!"); + function newState(state, $event) { + $mdDialog.show( + $mdDialog + .alert() + .title('state creation failed.') + .textContent("Sorry! You'll need to create a Constitution for " + state + " first!") + .targetEvent($event) + ); } // ****************************** diff --git a/src/components/autocomplete/demoInsideDialog/script.js b/src/components/autocomplete/demoInsideDialog/script.js index d794b650a2..449e407ac3 100644 --- a/src/components/autocomplete/demoInsideDialog/script.js +++ b/src/components/autocomplete/demoInsideDialog/script.js @@ -4,7 +4,7 @@ .module('autocompleteDemoInsideDialog', ['ngMaterial']) .controller('DemoCtrl', DemoCtrl); - function DemoCtrl($mdDialog) { + function DemoCtrl($mdDialog, $document) { var self = this; self.openDialog = function($event) { @@ -12,7 +12,7 @@ controller: DialogCtrl, controllerAs: 'ctrl', templateUrl: 'dialog.tmpl.html', - parent: angular.element(document.body), + parent: $document.find('body'), targetEvent: $event, clickOutsideToClose:true }) diff --git a/src/components/autocomplete/js/autocompleteController.js b/src/components/autocomplete/js/autocompleteController.js index c68f88367a..a505dd4996 100644 --- a/src/components/autocomplete/js/autocompleteController.js +++ b/src/components/autocomplete/js/autocompleteController.js @@ -8,7 +8,8 @@ var ITEM_HEIGHT = 48, INPUT_PADDING = 2; // Padding provided by `md-input-container` function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming, $window, - $animate, $rootElement, $attrs, $q, $log, $mdLiveAnnouncer) { + $animate, $rootElement, $attrs, $q, $log, $mdLiveAnnouncer, + $document) { // Internal Variables. var ctrl = this, @@ -22,7 +23,8 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming, fetchesInProgress = 0, enableWrapScroll = null, inputModelCtrl = null, - debouncedOnResize = $mdUtil.debounce(onWindowResize); + debouncedOnResize = $mdUtil.debounce(onWindowResize), + document = $document[0]; // Public Exported Variables with handlers defineProperty('hidden', handleHiddenChange, true); diff --git a/src/components/autocomplete/js/highlightController.js b/src/components/autocomplete/js/highlightController.js index a0352fabe3..f424d8d853 100644 --- a/src/components/autocomplete/js/highlightController.js +++ b/src/components/autocomplete/js/highlightController.js @@ -2,10 +2,11 @@ angular .module('material.components.autocomplete') .controller('MdHighlightCtrl', MdHighlightCtrl); -function MdHighlightCtrl ($scope, $element, $attrs) { +function MdHighlightCtrl ($scope, $element, $attrs, $document) { this.$scope = $scope; this.$element = $element; this.$attrs = $attrs; + this.$document = $document; // Cache the Regex to avoid rebuilding each time. this.regex = null; @@ -64,7 +65,7 @@ MdHighlightCtrl.prototype.applyRegex = function(text) { this.$element.append(tokenEl); } else { - this.$element.append(document.createTextNode(token)); + this.$element.append(this.$document[0].createTextNode(token)); } }.bind(this)); @@ -109,10 +110,10 @@ MdHighlightCtrl.prototype.createRegex = function(term, flags) { if (flags.indexOf('^') >= 0) startFlag = '^'; if (flags.indexOf('$') >= 0) endFlag = '$'; - return new RegExp(startFlag + regexTerm + endFlag, flags.replace(/[$\^]/g, '')); + return new RegExp(startFlag + regexTerm + endFlag, flags.replace(/[$^]/g, '')); }; /** Sanitizes a regex by removing all common RegExp identifiers */ MdHighlightCtrl.prototype.sanitizeRegex = function(term) { - return term && term.toString().replace(/[\\\^\$\*\+\?\.\(\)\|\{}\[\]]/g, '\\$&'); + return term && term.toString().replace(/[\\^$*+?.()|{}[\]]/g, '\\$&'); }; diff --git a/src/components/chips/demoContactChips/index.html b/src/components/chips/demoContactChips/index.html index a0fbcad628..6662c9663c 100644 --- a/src/components/chips/demoContactChips/index.html +++ b/src/components/chips/demoContactChips/index.html @@ -1,6 +1,6 @@
- + * */ -function GridListDirective($interpolate, $mdConstant, $mdGridLayout, $mdMedia) { +function GridListDirective($interpolate, $mdConstant, $mdGridLayout, $mdMedia, $document) { return { restrict: 'E', controller: GridListController, @@ -270,6 +270,7 @@ function GridListDirective($interpolate, $mdConstant, $mdGridLayout, $mdMedia) { // The width and horizontal position of each tile is always calculated the same way, but the // height and vertical position depends on the rowMode. + var document = $document[0]; var ltr = document.dir != 'rtl' && document.body.dir != 'rtl'; var style = ltr ? { left: POSITION({ unit: hUnit, offset: position.col, gutter: gutter }), @@ -316,10 +317,10 @@ function GridListDirective($interpolate, $mdConstant, $mdGridLayout, $mdMedia) { var vGutterShare = (rowCount - 1) / rowCount; // Percent of the available vertical space that one row takes up. - var vShare = (1 / rowCount) * 100; + vShare = (1 / rowCount) * 100; // Base vertical size of a row. - var vUnit = UNIT({share: vShare, gutterShare: vGutterShare, gutter: gutter}); + vUnit = UNIT({share: vShare, gutterShare: vGutterShare, gutter: gutter}); style.top = POSITION({unit: vUnit, offset: position.row, gutter: gutter}); style.height = DIMENSION({unit: vUnit, span: spans.row, gutter: gutter}); diff --git a/src/components/icon/icon.spec.js b/src/components/icon/icon.spec.js index 5f57e644a4..39dbfe46b5 100644 --- a/src/components/icon/icon.spec.js +++ b/src/components/icon/icon.spec.js @@ -385,7 +385,7 @@ describe('MdIcon directive', function() { return style .replace(/ng-scope|ng-isolate-scope|md-default-theme/gi,'') .replace(/\s\s+/g,' ') - .replace(/\s+\"/g,'"') + .replace(/\s+"/g,'"') .trim(); } diff --git a/src/components/icon/js/iconService.js b/src/components/icon/js/iconService.js index 0832c1f11b..bd1f9c9fe9 100644 --- a/src/components/icon/js/iconService.js +++ b/src/components/icon/js/iconService.js @@ -9,7 +9,7 @@ 'mdCalendar': 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMTkgM2gtMVYxaC0ydjJIOFYxSDZ2Mkg1Yy0xLjExIDAtMS45OS45LTEuOTkgMkwzIDE5YzAgMS4xLjg5IDIgMiAyaDE0YzEuMSAwIDItLjkgMi0yVjVjMC0xLjEtLjktMi0yLTJ6bTAgMTZINVY4aDE0djExek03IDEwaDV2NUg3eiIvPjwvc3ZnPg==', 'mdChecked': 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxnPjxwYXRoIGQ9Ik05IDE2LjE3TDQuODMgMTJsLTEuNDIgMS40MUw5IDE5IDIxIDdsLTEuNDEtMS40MXoiLz48L2c+PC9zdmc+' }) - .provider('$mdIcon', MdIconProvider); + .provider('$mdIcon', mdIconProvider); /** * @ngdoc service @@ -277,75 +277,78 @@ * */ -var config = { - defaultViewBoxSize: 24, - defaultFontSet: 'material-icons', - fontSets: [] -}; +function mdIconProvider() { + var config = { + defaultViewBoxSize: 24, + defaultFontSet: 'material-icons', + fontSets: [] + }; -function MdIconProvider() { -} - -MdIconProvider.prototype = { - icon: function(id, url, viewBoxSize) { - if (id.indexOf(':') == -1) id = '$default:' + id; - - config[id] = new ConfigurationItem(url, viewBoxSize); - return this; - }, - - iconSet: function(id, url, viewBoxSize) { - config[id] = new ConfigurationItem(url, viewBoxSize); - return this; - }, + function configItem(url, viewBoxSize) { + return new ConfigurationItem(url, viewBoxSize || config.defaultViewBoxSize); + } - defaultIconSet: function(url, viewBoxSize) { - var setName = '$default'; + return { + icon: function(id, url, viewBoxSize) { + if (id.indexOf(':') == -1) id = '$default:' + id; - if (!config[setName]) { - config[setName] = new ConfigurationItem(url, viewBoxSize); - } + config[id] = configItem(url, viewBoxSize); + return this; + }, - config[setName].viewBoxSize = viewBoxSize || config.defaultViewBoxSize; + iconSet: function(id, url, viewBoxSize) { + config[id] = configItem(url, viewBoxSize); + return this; + }, - return this; - }, + defaultIconSet: function(url, viewBoxSize) { + var setName = '$default'; - defaultViewBoxSize: function(viewBoxSize) { - config.defaultViewBoxSize = viewBoxSize; - return this; - }, + if (!config[setName]) { + config[setName] = configItem(url, viewBoxSize); + } - /** - * Register an alias name associated with a font-icon library style ; - */ - fontSet: function fontSet(alias, className) { - config.fontSets.push({ - alias: alias, - fontSet: className || alias - }); - return this; - }, + config[setName].viewBoxSize = viewBoxSize || config.defaultViewBoxSize; - /** - * Specify a default style name associated with a font-icon library - * fallback to Material Icons. - * - */ - defaultFontSet: function defaultFontSet(className) { - config.defaultFontSet = !className ? '' : className; - return this; - }, + return this; + }, - defaultIconSize: function defaultIconSize(iconSize) { - config.defaultIconSize = iconSize; - return this; - }, + defaultViewBoxSize: function(viewBoxSize) { + config.defaultViewBoxSize = viewBoxSize; + return this; + }, - $get: ['$templateRequest', '$q', '$log', '$mdUtil', '$sce', function($templateRequest, $q, $log, $mdUtil, $sce) { - return MdIconService(config, $templateRequest, $q, $log, $mdUtil, $sce); - }] -}; + /** + * Register an alias name associated with a font-icon library style ; + */ + fontSet: function fontSet(alias, className) { + config.fontSets.push({ + alias: alias, + fontSet: className || alias + }); + return this; + }, + + /** + * Specify a default style name associated with a font-icon library + * fallback to Material Icons. + * + */ + defaultFontSet: function defaultFontSet(className) { + config.defaultFontSet = !className ? '' : className; + return this; + }, + + defaultIconSize: function defaultIconSize(iconSize) { + config.defaultIconSize = iconSize; + return this; + }, + + $get: ['$injector', function($injector) { + return $injector.invoke(mdIconService, undefined, { config: config }); + }] + }; +} /** * Configuration item stored in the Icon registry; used for lookups @@ -353,7 +356,7 @@ MdIconProvider.prototype = { */ function ConfigurationItem(url, viewBoxSize) { this.url = url; - this.viewBoxSize = viewBoxSize || config.defaultViewBoxSize; + this.viewBoxSize = viewBoxSize; } /** @@ -400,11 +403,11 @@ function ConfigurationItem(url, viewBoxSize) { */ /* @ngInject */ -function MdIconService(config, $templateRequest, $q, $log, $mdUtil, $sce) { +function mdIconService(config, $templateRequest, $q, $log, $mdUtil, $sce, $window) { var iconCache = {}; var svgCache = {}; - var urlRegex = /[-\w@:%\+.~#?&//=]{2,}\.[a-z]{2,4}\b(\/[-\w@:%\+.~#?&//=]*)?/i; - var dataUrlRegex = /^data:image\/svg\+xml[\s*;\w\-\=]*?(base64)?,(.*)$/i; + var urlRegex = /[-\w@:%+.~#?&//=]{2,}\.[a-z]{2,4}\b(\/[-\w@:%+.~#?&//=]*)?/i; + var dataUrlRegex = /^data:image\/svg\+xml[\s*;\w\-=]*?(base64)?,(.*)$/i; Icon.prototype = {clone: cloneSVG, prepare: prepareAndStyle}; getIcon.fontSet = findRegisteredFontSet; @@ -534,7 +537,7 @@ function MdIconService(config, $templateRequest, $q, $log, $mdUtil, $sce) { function loadByDataUrl(url) { var results = dataUrlRegex.exec(url); var isBase64 = /base64/i.test(url); - var data = isBase64 ? window.atob(results[2]) : results[2]; + var data = isBase64 ? $window.atob(results[2]) : results[2]; return $q.when(angular.element(data)[0]); } diff --git a/src/components/input/input-animations.spec.js b/src/components/input/input-animations.spec.js index e4d2d054e6..01f3ac5aad 100644 --- a/src/components/input/input-animations.spec.js +++ b/src/components/input/input-animations.spec.js @@ -1,6 +1,6 @@ describe('md-input-container animations', function() { var $rootScope, $compile, $material, $$mdInput, $window, $animate, $rootElement, $document, $timeout, - el, root, body, pageScope, computedStyle; + el, root, body, pageScope, computedStyle, invalidAnimation, messagesAnimation, messageAnimation; // Load our modules beforeEach(module('ngAnimate', 'ngMessages', 'material.components.input', 'material.components.checkbox')); diff --git a/src/components/input/input.js b/src/components/input/input.js index f904a847ab..7c0b95829c 100644 --- a/src/components/input/input.js +++ b/src/components/input/input.js @@ -1,8 +1,17 @@ +function mockService(service) { + return ['$injector', '$window', (function ($injector, $window) { + if (!$window._mdMocksIncluded) { + return; + } + return $injector.invoke(service); + })]; +} + /** * @ngdoc module * @name material.components.input */ -var inputModule = angular.module('material.components.input', [ +angular.module('material.components.input', [ 'material.core' ]) .directive('mdInputContainer', mdInputContainerDirective) @@ -18,26 +27,27 @@ var inputModule = angular.module('material.components.input', [ .animation('.md-input-invalid', mdInputInvalidMessagesAnimation) .animation('.md-input-messages-animation', ngMessagesAnimation) - .animation('.md-input-message-animation', ngMessageAnimation); - -// If we are running inside of tests; expose some extra services so that we can test them -if (window._mdMocksIncluded) { - inputModule.service('$$mdInput', function() { + .animation('.md-input-message-animation', ngMessageAnimation) + // If we are running inside of tests; expose some extra functions so that we can test them + .service('$$mdInput', mockService(['$window', function($window) { return { // special accessor to internals... useful for testing messages: { - show : showInputMessages, - hide : hideInputMessages, + show : function(element, done) { + return showInputMessages($window, element, done); + }, + hide : function(element, done) { + return hideInputMessages($window, element, done); + }, getElement : getMessagesElement } - } - }) + }; + }])) // Register a service for each animation so that we can easily inject them into unit tests - .service('mdInputInvalidAnimation', mdInputInvalidMessagesAnimation) - .service('mdInputMessagesAnimation', ngMessagesAnimation) - .service('mdInputMessageAnimation', ngMessageAnimation); -} + .service('mdInputInvalidAnimation', mockService(mdInputInvalidMessagesAnimation)) + .service('mdInputMessagesAnimation', mockService(ngMessagesAnimation)) + .service('mdInputMessageAnimation', mockService(ngMessageAnimation)); /** * @ngdoc directive @@ -858,7 +868,7 @@ function ngMessagesDirective() { } } -function ngMessageDirective($mdUtil) { +function ngMessageDirective($mdUtil, $window) { return { restrict: 'EA', compile: compile, @@ -887,7 +897,7 @@ function ngMessageDirective($mdUtil) { function isInsideFragment() { var nextNode = tElement[0]; while (nextNode = nextNode.parentNode) { - if (nextNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { + if (nextNode.nodeType === $window.Node.DOCUMENT_FRAGMENT_NODE) { return true; } } @@ -907,33 +917,33 @@ function ngMessageDirective($mdUtil) { var $$AnimateRunner, $animateCss, $mdUtil, $log; -function mdInputInvalidMessagesAnimation($$AnimateRunner, $animateCss, $mdUtil, $log) { +function mdInputInvalidMessagesAnimation($$AnimateRunner, $animateCss, $mdUtil, $log, $window) { saveSharedServices($$AnimateRunner, $animateCss, $mdUtil, $log); return { addClass: function(element, className, done) { - showInputMessages(element, done); + showInputMessages($window, element, done); } // NOTE: We do not need the removeClass method, because the message ng-leave animation will fire }; } -function ngMessagesAnimation($$AnimateRunner, $animateCss, $mdUtil, $log) { +function ngMessagesAnimation($$AnimateRunner, $animateCss, $mdUtil, $log, $window) { saveSharedServices($$AnimateRunner, $animateCss, $mdUtil, $log); return { enter: function(element, done) { - showInputMessages(element, done); + showInputMessages($window, element, done); }, leave: function(element, done) { - hideInputMessages(element, done); + hideInputMessages($window, element, done); }, addClass: function(element, className, done) { if (className == "ng-hide") { - hideInputMessages(element, done); + hideInputMessages($window, element, done); } else { done(); } @@ -941,7 +951,7 @@ function ngMessagesAnimation($$AnimateRunner, $animateCss, $mdUtil, $log) { removeClass: function(element, className, done) { if (className == "ng-hide") { - showInputMessages(element, done); + showInputMessages($window, element, done); } else { done(); } @@ -949,25 +959,25 @@ function ngMessagesAnimation($$AnimateRunner, $animateCss, $mdUtil, $log) { }; } -function ngMessageAnimation($$AnimateRunner, $animateCss, $mdUtil, $log) { +function ngMessageAnimation($$AnimateRunner, $animateCss, $mdUtil, $log, $window) { saveSharedServices($$AnimateRunner, $animateCss, $mdUtil, $log); return { enter: function(element, done) { - var animator = showMessage(element); + var animator = showMessage($window, element); animator.start().done(done); }, leave: function(element, done) { - var animator = hideMessage(element); + var animator = hideMessage($window, element); animator.start().done(done); } }; } -function showInputMessages(element, done) { +function showInputMessages($window, element, done) { var animators = [], animator; var messages = getMessagesElement(element); var children = messages.children(); @@ -979,7 +989,7 @@ function showInputMessages(element, done) { } angular.forEach(children, function(child) { - animator = showMessage(angular.element(child)); + animator = showMessage($window, angular.element(child)); animators.push(animator.start()); }); @@ -987,7 +997,7 @@ function showInputMessages(element, done) { $$AnimateRunner.all(animators, done); } -function hideInputMessages(element, done) { +function hideInputMessages($window, element, done) { var animators = [], animator; var messages = getMessagesElement(element); var children = messages.children(); @@ -999,7 +1009,7 @@ function hideInputMessages(element, done) { } angular.forEach(children, function(child) { - animator = hideMessage(angular.element(child)); + animator = hideMessage($window, angular.element(child)); animators.push(animator.start()); }); @@ -1007,9 +1017,9 @@ function hideInputMessages(element, done) { $$AnimateRunner.all(animators, done); } -function showMessage(element) { - var height = parseInt(window.getComputedStyle(element[0]).height); - var topMargin = parseInt(window.getComputedStyle(element[0]).marginTop); +function showMessage($window, element) { + var height = parseInt($window.getComputedStyle(element[0]).height); + var topMargin = parseInt($window.getComputedStyle(element[0]).marginTop); var messages = getMessagesElement(element); var container = getInputElement(element); @@ -1031,9 +1041,9 @@ function showMessage(element) { }); } -function hideMessage(element) { +function hideMessage($window, element) { var height = element[0].offsetHeight; - var styles = window.getComputedStyle(element[0]); + var styles = $window.getComputedStyle(element[0]); // If we are already hidden, just return an empty animation if (parseInt(styles.opacity) === 0) { diff --git a/src/components/navBar/demoBasicUsage/index.html b/src/components/navBar/demoBasicUsage/index.html index d30fb0dc1c..f354401a87 100644 --- a/src/components/navBar/demoBasicUsage/index.html +++ b/src/components/navBar/demoBasicUsage/index.html @@ -23,6 +23,7 @@ --> + {{status}}
External content for `{{currentNavItem}}`.
@@ -30,4 +31,4 @@ Disable Ink Bar
-
\ No newline at end of file + diff --git a/src/components/navBar/demoBasicUsage/script.js b/src/components/navBar/demoBasicUsage/script.js index d1c15c0764..20c1dcb65b 100644 --- a/src/components/navBar/demoBasicUsage/script.js +++ b/src/components/navBar/demoBasicUsage/script.js @@ -8,7 +8,7 @@ $scope.currentNavItem = 'page1'; $scope.goto = function(page) { - console.log("Goto " + page); - } + $scope.status = "Goto " + page; + }; } })(); diff --git a/src/components/panel/demoBasicUsage/script.js b/src/components/panel/demoBasicUsage/script.js index 510b6bc87f..4b3496265c 100644 --- a/src/components/panel/demoBasicUsage/script.js +++ b/src/components/panel/demoBasicUsage/script.js @@ -6,8 +6,9 @@ angular.module('panelDemo', ['ngMaterial']) .controller('PanelDialogCtrl', PanelDialogCtrl); -function BasicDemoCtrl($mdPanel) { +function BasicDemoCtrl($mdPanel, $document) { this._mdPanel = $mdPanel; + this.$document = $document; this.desserts = [ 'Apple Pie', @@ -24,12 +25,13 @@ function BasicDemoCtrl($mdPanel) { BasicDemoCtrl.prototype.showDialog = function() { + var $document = this.$document; var position = this._mdPanel.newPanelPosition() .absolute() .center(); var config = { - attachTo: angular.element(document.body), + attachTo: angular.element($document[0].body), controller: PanelDialogCtrl, controllerAs: 'ctrl', disableParentScroll: this.disableParentScroll, @@ -49,12 +51,13 @@ BasicDemoCtrl.prototype.showDialog = function() { BasicDemoCtrl.prototype.showMenu = function(ev) { + var $document = this.$document; var position = this._mdPanel.newPanelPosition() .relativeTo('.demo-menu-open-button') .addPanelPosition(this._mdPanel.xPosition.ALIGN_START, this._mdPanel.yPosition.BELOW); var config = { - attachTo: angular.element(document.body), + attachTo: angular.element($document[0].body), controller: PanelMenuCtrl, controllerAs: 'ctrl', template: @@ -95,10 +98,11 @@ function PanelDialogCtrl(mdPanelRef) { PanelDialogCtrl.prototype.closeDialog = function() { + var $document = this.$document; var panelRef = this._mdPanelRef; panelRef && panelRef.close().then(function() { - angular.element(document.querySelector('.demo-dialog-open-button')).focus(); + angular.element($document[0].querySelector('.demo-dialog-open-button')).focus(); panelRef.destroy(); }); }; @@ -106,41 +110,44 @@ PanelDialogCtrl.prototype.closeDialog = function() { function PanelMenuCtrl(mdPanelRef, $timeout) { + var $document = this.$document; this._mdPanelRef = mdPanelRef; this.favoriteDessert = this.selected.favoriteDessert; $timeout(function() { - var selected = document.querySelector('.demo-menu-item.selected'); + var selected = $document[0].querySelector('.demo-menu-item.selected'); if (selected) { angular.element(selected).focus(); } else { - angular.element(document.querySelectorAll('.demo-menu-item')[0]).focus(); + angular.element($document[0].querySelectorAll('.demo-menu-item')[0]).focus(); } }); } PanelMenuCtrl.prototype.selectDessert = function(dessert) { + var $document = this.$document; this.selected.favoriteDessert = dessert; this._mdPanelRef && this._mdPanelRef.close().then(function() { - angular.element(document.querySelector('.demo-menu-open-button')).focus(); + angular.element($document[0].querySelector('.demo-menu-open-button')).focus(); }); }; PanelMenuCtrl.prototype.onKeydown = function($event, dessert) { + var $document = this.$document; var handled, els, index, prevIndex, nextIndex; switch ($event.which) { case 38: // Up Arrow. - els = document.querySelectorAll('.demo-menu-item'); - index = indexOf(els, document.activeElement); + els = $document[0].querySelectorAll('.demo-menu-item'); + index = indexOf(els, $document[0].activeElement); prevIndex = (index + els.length - 1) % els.length; els[prevIndex].focus(); handled = true; break; case 40: // Down Arrow. - els = document.querySelectorAll('.demo-menu-item'); - index = indexOf(els, document.activeElement); + els = $document[0].querySelectorAll('.demo-menu-item'); + index = indexOf(els, $document[0].activeElement); nextIndex = (index + 1) % els.length; els[nextIndex].focus(); handled = true; diff --git a/src/components/panel/demoGroups/script.js b/src/components/panel/demoGroups/script.js index 76443ef564..f2f4b027c5 100644 --- a/src/components/panel/demoGroups/script.js +++ b/src/components/panel/demoGroups/script.js @@ -6,7 +6,7 @@ .controller('PanelGroupsCtrl', PanelGroupsCtrl) .controller('PanelMenuCtrl', PanelMenuCtrl); - function PanelGroupsCtrl($mdPanel) { + function PanelGroupsCtrl($mdPanel, $document) { this.settings = { name: 'settings', items: [ @@ -80,7 +80,7 @@ var config = { id: 'toolbar_' + menu.name, - attachTo: angular.element(document.body), + attachTo: angular.element($document[0].body), controller: PanelMenuCtrl, controllerAs: 'ctrl', template: template, @@ -111,7 +111,7 @@ var config = { id: 'content_' + menu.name, - attachTo: angular.element(document.body), + attachTo: angular.element($document[0].body), controller: PanelMenuCtrl, controllerAs: 'ctrl', template: template, diff --git a/src/components/panel/demoPanelAnimations/script.js b/src/components/panel/demoPanelAnimations/script.js index 1731a80a26..04135d6583 100644 --- a/src/components/panel/demoPanelAnimations/script.js +++ b/src/components/panel/demoPanelAnimations/script.js @@ -6,7 +6,7 @@ angular.module('panelAnimationsDemo', ['ngMaterial']) .controller('DialogCtrl', DialogCtrl); -function AnimationCtrl($mdPanel) { +function AnimationCtrl($mdPanel, $document) { this._mdPanel = $mdPanel; this.openFrom = 'button'; this.closeTo = 'button'; @@ -26,6 +26,7 @@ AnimationCtrl.prototype.showDialog = function() { .top(); var animation = this._mdPanel.newPanelAnimation(); + var document = this.$document[0]; animation.duration(this.duration || this.separateDurations); diff --git a/src/components/panel/demoPanelProvider/script.js b/src/components/panel/demoPanelProvider/script.js index cdbe42d18e..63871a789d 100644 --- a/src/components/panel/demoPanelProvider/script.js +++ b/src/components/panel/demoPanelProvider/script.js @@ -17,32 +17,34 @@ * API. */ function PanelProviderConfig($mdPanelProvider) { - $mdPanelProvider.definePreset('demoPreset', { - attachTo: angular.element(document.body), - controller: PanelMenuCtrl, - controllerAs: 'ctrl', - template: '' + - '', - panelClass: 'menu-panel-container', - focusOnOpen: false, - zIndex: 100, - propagateContainerEvents: true, - groupName: 'menus' - }); + $mdPanelProvider.definePreset('demoPreset', ['$document', function($document) { + return { + attachTo: angular.element($document[0].body), + controller: PanelMenuCtrl, + controllerAs: 'ctrl', + template: '' + + '', + panelClass: 'menu-panel-container', + focusOnOpen: false, + zIndex: 100, + propagateContainerEvents: true, + groupName: 'menus' + }; + }]); } function PanelProviderCtrl($mdPanel) { diff --git a/src/components/panel/panel.js b/src/components/panel/panel.js index 4bde463344..a44fd85a63 100644 --- a/src/components/panel/panel.js +++ b/src/components/panel/panel.js @@ -1196,12 +1196,19 @@ MdPanelService.prototype.open = function(preset, config) { * @returns {!Object} The preset configuration object. */ MdPanelService.prototype._getPresetByName = function(preset) { - if (!this._presets[preset]) { + var $injector = this._$injector; + var v = this._presets[preset]; + if (!v) { throw new Error('mdPanel: The panel preset configuration that you ' + 'requested does not exist. Use the $mdPanelProvider to create a ' + 'preset before requesting one.'); } - return this._presets[preset]; + + if (angular.isArray(v) || angular.isFunction(v)) { + return $injector.invoke(v); + } + + return v; }; @@ -1378,6 +1385,9 @@ function MdPanelRef(config, $injector) { /** @private @const {!angular.$window} */ this._$window = $injector.get('$window'); + /** @private @const {!angular.$document} */ + this._$document = $injector.get('$document'); + /** @private @const {!Function} */ this._$$rAF = $injector.get('$$rAF'); @@ -1662,6 +1672,7 @@ MdPanelRef.prototype.show = function() { * the panel has hidden and animations finish. */ MdPanelRef.prototype.hide = function() { + var getElement = getElementGetter(this._$document); if (!this.panelContainer) { return this._$q(function(resolve, reject) { reject('mdPanel: Panel does not exist yet. Call open() or attach().'); @@ -1814,6 +1825,7 @@ MdPanelRef.prototype.toggleClass = function(toggleClass, onElement) { * @private */ MdPanelRef.prototype._compile = function() { + var getElement = getElementGetter(this._$document); var self = this; // Compile the element via $mdCompiler. Note that when using a @@ -1857,6 +1869,7 @@ MdPanelRef.prototype._compile = function() { * @private */ MdPanelRef.prototype._createPanel = function() { + var getElement = getElementGetter(this._$document); var self = this; return this._$q(function(resolve, reject) { @@ -2101,6 +2114,7 @@ MdPanelRef.prototype._removeEventListeners = function() { * @private */ MdPanelRef.prototype._configureEscapeToClose = function() { + var getElement = getElementGetter(this._$document); if (this.config['escapeToClose']) { var parentTarget = getElement(this.config['attachTo']); var self = this; @@ -2132,9 +2146,10 @@ MdPanelRef.prototype._configureEscapeToClose = function() { * @private */ MdPanelRef.prototype._configureClickOutsideToClose = function() { + var $document = this._$document; if (this.config['clickOutsideToClose']) { var target = this.config['propagateContainerEvents'] ? - angular.element(document.body) : + angular.element($document[0].body) : this.panelContainer; var sourceEl; @@ -2532,6 +2547,9 @@ function MdPanelPosition($injector) { /** @private @const {!angular.$mdConstant} */ this._$mdConstant = $injector.get('$mdConstant'); + /** @private @const {!angular.$document} */ + this._$document = $injector.get('$document'); + /** @private {boolean} */ this._absolute = false; @@ -2759,6 +2777,7 @@ MdPanelPosition.prototype.center = function() { * @returns {!MdPanelPosition} */ MdPanelPosition.prototype.relativeTo = function(element) { + var getElement = getElementGetter(this._$document); this._absolute = false; this._relativeToEl = getElement(element); return this; @@ -2941,7 +2960,7 @@ MdPanelPosition.prototype._isOnscreen = function(panelEl) { if (this._translateX.length || this._translateY.length) { var prefixedTransform = this._$mdConstant.CSS.TRANSFORM; - var offsets = getComputedTranslations(panelEl, prefixedTransform); + var offsets = getComputedTranslations(this._$window, panelEl, prefixedTransform); left += offsets.x; top += offsets.y; } @@ -3182,6 +3201,9 @@ function MdPanelAnimation($injector) { /** @private @const {!angular.$mdUtil} */ this._$mdUtil = $injector.get('$mdUtil'); + /** @private @const {!angular.$document} */ + this._$document = $injector.get('$document'); + /** * @private {{element: !angular.JQLite|undefined, bounds: !DOMRect}| * undefined} @@ -3286,6 +3308,7 @@ MdPanelAnimation.prototype.duration = function(duration) { * @private */ MdPanelAnimation.prototype._getPanelAnimationTarget = function(location) { + var getElement = getElementGetter(this._$document); if (angular.isDefined(location.top) || angular.isDefined(location.left)) { return { element: undefined, @@ -3498,28 +3521,31 @@ MdPanelAnimation.prototype._getBoundingClientRect = function(element) { /** - * Returns the angular element associated with a css selector or element. - * @param el {string|!angular.JQLite|!Element} - * @returns {!angular.JQLite} + * Returns a function that returns the angular element associated with a css selector or element. + * @param $document {!angular.JQLite} + * @returns {(el: string,!angular.JQLite,!Element) => !angular.JQLite} */ -function getElement(el) { - var queryResult = angular.isString(el) ? - document.querySelector(el) : el; - return angular.element(queryResult); +function getElementGetter($document) { + return function getElement(el) { + var queryResult = angular.isString(el) ? + $document[0].querySelector(el) : el; + return angular.element(queryResult); + }; } /** * Gets the computed values for an element's translateX and translateY in px. + * @param {!angular.$window} $window * @param {!angular.JQLite|!Element} el * @param {string} property * @return {{x: number, y: number}} */ -function getComputedTranslations(el, property) { +function getComputedTranslations($window, el, property) { // The transform being returned by `getComputedStyle` is in the format: // `matrix(a, b, c, d, translateX, translateY)` if defined and `none` // if the element doesn't have a transform. - var transform = getComputedStyle(el[0] || el)[property]; + var transform = $window.getComputedStyle(el[0] || el)[property]; var openIndex = transform.indexOf('('); var closeIndex = transform.lastIndexOf(')'); var output = { x: 0, y: 0 }; diff --git a/src/components/radioButton/demoBasicUsage/index.html b/src/components/radioButton/demoBasicUsage/index.html index 7bb088244f..a4c5da5eaa 100644 --- a/src/components/radioButton/demoBasicUsage/index.html +++ b/src/components/radioButton/demoBasicUsage/index.html @@ -1,5 +1,5 @@
-
+

Selected Value: {{ data.group1 }}

diff --git a/src/components/radioButton/demoBasicUsage/script.js b/src/components/radioButton/demoBasicUsage/script.js index c57d08235e..a1477b9883 100644 --- a/src/components/radioButton/demoBasicUsage/script.js +++ b/src/components/radioButton/demoBasicUsage/script.js @@ -1,7 +1,7 @@ angular .module('radioDemo1', ['ngMaterial']) - .controller('AppCtrl', function($scope) { + .controller('AppCtrl', function($scope, $mdDialog) { $scope.data = { group1 : 'Banana', @@ -31,8 +31,14 @@ angular ]; - $scope.submit = function() { - alert('submit'); + $scope.submit = function($event) { + $mdDialog.show( + $mdDialog + .alert() + .title('Action') + .textContent('submit') + .event($event) + ); }; $scope.addItem = function() { diff --git a/src/components/select/demoValidations/index.html b/src/components/select/demoValidations/index.html index 99796aa3f8..ffe8efcdac 100755 --- a/src/components/select/demoValidations/index.html +++ b/src/components/select/demoValidations/index.html @@ -33,7 +33,7 @@
Clear - Save + Save
diff --git a/src/components/select/demoValidations/script.js b/src/components/select/demoValidations/script.js index 69e208a098..069868c1a1 100755 --- a/src/components/select/demoValidations/script.js +++ b/src/components/select/demoValidations/script.js @@ -1,16 +1,21 @@ angular.module('selectDemoValidation', ['ngMaterial', 'ngMessages']) -.controller('AppCtrl', function($scope) { +.controller('AppCtrl', function($scope, $mdDialog) { $scope.clearValue = function() { $scope.quest = undefined; $scope.favoriteColor = undefined; $scope.myForm.$setPristine(); }; - $scope.save = function() { - if ($scope.myForm.$valid) { + $scope.save = function(event) { + var valid = $scope.myForm.$valid; + if (valid) { $scope.myForm.$setSubmitted(); - alert('Form was valid.'); - } else { - alert('Form was invalid!'); } + + $mdDialog.show( + $mdDialog + .alert() + .title('Form submit attempt.') + .textContent('Form was ' + valid ? 'valid' : 'invalid!') + ); }; }); diff --git a/src/components/select/select.js b/src/components/select/select.js index d50f4caad4..1cc32d807b 100755 --- a/src/components/select/select.js +++ b/src/components/select/select.js @@ -672,7 +672,15 @@ function SelectMenuDirective($parse, $mdUtil, $mdConstant, $mdTheming) { } } - function SelectMenuController($scope, $attrs, $element) { + function SelectMenuController($scope, $attrs, $element, $timeout) { + function setTimeout(fn, delay) { + return $timeout(fn, delay, false); + } + + function clearTimeout(obj) { + $timeout.cancel(obj); + } + var self = this; self.isMultiple = angular.isDefined($attrs.multiple); // selected is an object with keys matching all of the selected options' hashed values @@ -1631,7 +1639,7 @@ function SelectProvider($$interimElementProvider) { minWidth = Math.min(targetRect.width + centeredRect.paddingLeft + centeredRect.paddingRight, maxWidth); - fontSize = window.getComputedStyle(targetNode)['font-size']; + fontSize = $window.getComputedStyle(targetNode)['font-size']; } // Keep left and top within the window diff --git a/src/components/select/select.spec.js b/src/components/select/select.spec.js index b0aaa83ffe..6146528df0 100755 --- a/src/components/select/select.spec.js +++ b/src/components/select/select.spec.js @@ -1459,7 +1459,9 @@ describe('', function() { el.triggerHandler('click'); $material.flushInterimElement(); el.triggerHandler('blur'); - } catch (e) { } + } catch (e) { + // ignore error + } } function closeSelect() { diff --git a/src/components/slider/slider.js b/src/components/slider/slider.js index 1025415e50..443243ee58 100644 --- a/src/components/slider/slider.js +++ b/src/components/slider/slider.js @@ -22,7 +22,10 @@ * * */ -function SliderContainerDirective() { +/** + * @ngInject + */ +function SliderContainerDirective($window) { return { controller: function () {}, compile: function (elem) { @@ -74,7 +77,7 @@ function SliderContainerDirective() { var input = element[0].querySelector('md-input-container'); if (input) { - var computedStyle = getComputedStyle(input); + var computedStyle = $window.getComputedStyle(input); var minWidth = parseInt(computedStyle.minWidth); var padding = parseInt(computedStyle.padding) * 2; @@ -160,6 +163,10 @@ function SliderDirective($$rAF, $window, $mdAria, $mdUtil, $mdConstant, $mdThemi compile: compile }; + function setTimeout(fn, delay) { + return $timeout(fn, delay, false); + } + // ********************************************************** // Private Methods // ********************************************************** diff --git a/src/components/swipe/demoBasicUsage/index.html b/src/components/swipe/demoBasicUsage/index.html index efe7bf6e95..e8ede2ac3a 100644 --- a/src/components/swipe/demoBasicUsage/index.html +++ b/src/components/swipe/demoBasicUsage/index.html @@ -1,23 +1,23 @@
-
+
Swipe me to the left
-
+
Swipe me to the right
+ class="demo-swipe" md-swipe-up="onSwipe('up', $event)"> Swipe me up
+ class="demo-swipe" md-swipe-down="onSwipe('down', $event)"> Swipe me down
diff --git a/src/components/swipe/demoBasicUsage/script.js b/src/components/swipe/demoBasicUsage/script.js index 2450b8a260..3e4ca637de 100644 --- a/src/components/swipe/demoBasicUsage/script.js +++ b/src/components/swipe/demoBasicUsage/script.js @@ -1,17 +1,12 @@ angular.module('demoSwipe', ['ngMaterial']) - .controller('demoSwipeCtrl', function($scope) { - $scope.onSwipeLeft = function(ev) { - alert('You swiped left!!'); - }; - - $scope.onSwipeRight = function(ev) { - alert('You swiped right!!'); - }; - $scope.onSwipeUp = function(ev) { - alert('You swiped up!!'); - }; - - $scope.onSwipeDown = function(ev) { - alert('You swiped down!!'); + .controller('demoSwipeCtrl', function($scope, $mdDialog) { + $scope.onSwipe = function onSwipe(direction, ev) { + $mdDialog.show( + $mdDialog + .alert() + .title('Swipe action') + .textContent('You swiped ' + direction + '!!') + .event(ev) + ); }; }); diff --git a/src/components/switch/demoBasicUsage/script.js b/src/components/switch/demoBasicUsage/script.js index 7991f6422d..f5aed5a171 100644 --- a/src/components/switch/demoBasicUsage/script.js +++ b/src/components/switch/demoBasicUsage/script.js @@ -9,6 +9,6 @@ angular.module('switchDemo1', ['ngMaterial']) $scope.message = 'false'; $scope.onChange = function(cbState) { - $scope.message = cbState; + $scope.message = cbState; }; }); diff --git a/src/components/tabs/js/tabsController.js b/src/components/tabs/js/tabsController.js index b12e91901c..7de71d5806 100644 --- a/src/components/tabs/js/tabsController.js +++ b/src/components/tabs/js/tabsController.js @@ -683,7 +683,7 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp index = ctrl[ key ]; for (newIndex = index + inc; ctrl.tabs[ newIndex ] && ctrl.tabs[ newIndex ].scope.disabled; - newIndex += inc) {} + newIndex += inc) { /* do nothing */ } newIndex = (index + inc + ctrl.tabs.length) % ctrl.tabs.length; diff --git a/src/components/tabs/js/tabsDummyWrapperDirective.js b/src/components/tabs/js/tabsDummyWrapperDirective.js index aaed701e03..59735251bd 100644 --- a/src/components/tabs/js/tabsDummyWrapperDirective.js +++ b/src/components/tabs/js/tabsDummyWrapperDirective.js @@ -36,7 +36,7 @@ function MdTabsDummyWrapper ($mdUtil, $window) { characterData: true }; - observer = new MutationObserver(mutationCallback); + observer = new $window.MutationObserver(mutationCallback); observer.observe(element[0], config); disconnect = observer.disconnect.bind(observer); } else { diff --git a/src/components/tabs/tabsPaginationService.spec.js b/src/components/tabs/tabsPaginationService.spec.js index e95f587664..506414049a 100644 --- a/src/components/tabs/tabsPaginationService.spec.js +++ b/src/components/tabs/tabsPaginationService.spec.js @@ -1,6 +1,6 @@ describe('MdTabsPaginationService', function() { - const TAB_WIDTH = 100; + var TAB_WIDTH = 100; var MdTabsPaginationService; diff --git a/src/components/toast/demoBasicUsage/script.js b/src/components/toast/demoBasicUsage/script.js index 0f39479932..fb39695df1 100644 --- a/src/components/toast/demoBasicUsage/script.js +++ b/src/components/toast/demoBasicUsage/script.js @@ -1,7 +1,7 @@ angular.module('toastDemo1', ['ngMaterial']) -.controller('AppCtrl', function($scope, $mdToast) { +.controller('AppCtrl', function($scope, $mdToast, $mdDialog) { var last = { bottom: false, top: true, @@ -52,7 +52,12 @@ angular.module('toastDemo1', ['ngMaterial']) $mdToast.show(toast).then(function(response) { if ( response == 'ok' ) { - alert('You clicked the \'UNDO\' action.'); + $mdDialog.show( + $mdDialog + .alert() + .title('Action clicked') + .textContent("You clicked the 'UNDO' action.") + ); } }); }; diff --git a/src/components/toast/toast.js b/src/components/toast/toast.js index a95f365cba..b40fcf65f2 100644 --- a/src/components/toast/toast.js +++ b/src/components/toast/toast.js @@ -365,7 +365,7 @@ function MdToastProvider($$interimElementProvider) { } /* @ngInject */ - function toastDefaultOptions($animate, $mdToast, $mdUtil, $mdMedia) { + function toastDefaultOptions($animate, $mdToast, $mdUtil, $mdMedia, $document) { var SWIPE_EVENTS = '$md.swipeleft $md.swiperight $md.swipeup $md.swipedown'; return { onShow: onShow, @@ -382,7 +382,7 @@ function MdToastProvider($$interimElementProvider) { // Root element of template will be . We need to wrap all of its content inside of // of
. All templates provided here should be static, developer-controlled // content (meaning we're not attempting to guard against XSS). - var templateRoot = document.createElement('md-template'); + var templateRoot = $document[0].createElement('md-template'); templateRoot.innerHTML = template; // Iterate through all root children, to detect possible md-toast directives. diff --git a/src/components/toolbar/toolbar.spec.js b/src/components/toolbar/toolbar.spec.js index f4afbafebd..7d0060f542 100644 --- a/src/components/toolbar/toolbar.spec.js +++ b/src/components/toolbar/toolbar.spec.js @@ -35,6 +35,7 @@ describe('', function() { }); var contentCss = {}; spyOn(contentEl, 'css').and.callFake(function(properties, value) { + var k; if (angular.isObject(properties)) { for (k in properties) { if (properties.hasOwnProperty(k)) { diff --git a/src/components/tooltip/tooltip.js b/src/components/tooltip/tooltip.js index c69201ee5e..b9697a0b94 100644 --- a/src/components/tooltip/tooltip.js +++ b/src/components/tooltip/tooltip.js @@ -159,7 +159,7 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $interpolate, // for it in the form of viable host(parent[0]). if (parent[0] && 'MutationObserver' in $window) { // Use a mutationObserver to tackle #2602. - var attributeObserver = new MutationObserver(function(mutations) { + var attributeObserver = new $window.MutationObserver(function(mutations) { if (isDisabledMutation(mutations)) { $mdUtil.nextTick(function() { setVisible(false); @@ -197,7 +197,7 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $interpolate, } function windowBlurEventHandler() { - elementFocusedOnWindowBlur = document.activeElement === parent[0]; + elementFocusedOnWindowBlur = $document[0].activeElement === parent[0]; } function enterEventHandler($event) { @@ -268,7 +268,7 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $interpolate, function configureWatchers() { if (element[0] && 'MutationObserver' in $window) { - var attributeObserver = new MutationObserver(function(mutations) { + var attributeObserver = new $window.MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if (mutation.attributeName === 'md-visible' && !scope.visibleWatcher ) { @@ -373,7 +373,7 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $interpolate, } if (!panelRef) { - var attachTo = angular.element(document.body); + var attachTo = angular.element($document[0].body); var panelAnimation = $mdPanel.newPanelAnimation() .openFrom(parent) .closeTo(parent) @@ -417,9 +417,9 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $interpolate, * * @ngInject */ -function MdTooltipRegistry() { +function MdTooltipRegistry($window) { var listeners = {}; - var ngWindow = angular.element(window); + var ngWindow = angular.element($window); return { register: register, @@ -449,7 +449,7 @@ function MdTooltipRegistry() { var handlers = listeners[type] = listeners[type] || []; if (!handlers.length) { - useCapture ? window.addEventListener(type, globalEventHandler, true) : + useCapture ? $window.addEventListener(type, globalEventHandler, true) : ngWindow.on(type, globalEventHandler); } @@ -472,7 +472,7 @@ function MdTooltipRegistry() { handlers.splice(index, 1); if (handlers.length === 0) { - useCapture ? window.removeEventListener(type, globalEventHandler, true) : + useCapture ? $window.removeEventListener(type, globalEventHandler, true) : ngWindow.off(type, globalEventHandler); } } diff --git a/src/components/virtualRepeat/virtual-repeater.js b/src/components/virtualRepeat/virtual-repeater.js index 6d79d276ce..6cbca1194a 100644 --- a/src/components/virtualRepeat/virtual-repeater.js +++ b/src/components/virtualRepeat/virtual-repeater.js @@ -96,11 +96,12 @@ var NUM_EXTRA = 3; /** @ngInject */ function VirtualRepeatContainerController($$rAF, $mdUtil, $mdConstant, $parse, $rootScope, $window, $scope, - $element, $attrs) { + $element, $attrs, $document) { this.$rootScope = $rootScope; this.$scope = $scope; this.$element = $element; this.$attrs = $attrs; + this.$document = $document; /** @type {number} The width or height of the container */ this.size = 0; @@ -254,6 +255,7 @@ VirtualRepeatContainerController.prototype.getDimensionName_ = function() { VirtualRepeatContainerController.prototype.sizeScroller_ = function(size) { var dimension = this.getDimensionName_(); var crossDimension = this.isHorizontal() ? 'height' : 'width'; + var $document = this.$document; // Clear any existing dimensions. this.sizer.innerHTML = ''; @@ -271,7 +273,7 @@ VirtualRepeatContainerController.prototype.sizeScroller_ = function(size) { var numChildren = Math.floor(size / this.maxElementPixels); // Element template to clone for each max-size piece. - var sizerChild = document.createElement('div'); + var sizerChild = $document[0].createElement('div'); sizerChild.style[dimension] = this.maxElementPixels + 'px'; sizerChild.style[crossDimension] = '1px'; @@ -377,6 +379,7 @@ VirtualRepeatContainerController.prototype.resetScroll = function() { VirtualRepeatContainerController.prototype.handleScroll_ = function() { + var document = this.$document[0]; var ltr = document.dir != 'rtl' && document.body.dir != 'rtl'; if(!ltr && !this.maxSize) { this.scroller.scrollLeft = this.scrollSize; diff --git a/src/core/services/aria/aria.js b/src/core/services/aria/aria.js index bc44b83b6e..78dc144169 100644 --- a/src/core/services/aria/aria.js +++ b/src/core/services/aria/aria.js @@ -37,8 +37,8 @@ function MdAriaProvider() { return { disableWarnings: disableWarnings, - $get: function($$rAF, $log, $window, $interpolate) { - return MdAriaService.apply(config, arguments); + $get: function($injector) { + return $injector.invoke(mdAriaService, undefined, config); } }; @@ -55,12 +55,7 @@ function MdAriaProvider() { /* * @ngInject */ -function MdAriaService($$rAF, $log, $window, $interpolate) { - - // Load the showWarnings option from the current context and store it inside of a scope variable, - // because the context will be probably lost in some function calls. - var showWarnings = this.showWarnings; - +function mdAriaService($$rAF, $log, $window, $interpolate, $document, showWarnings) { return { expect: expect, expectAsync: expectAsync, @@ -130,7 +125,7 @@ function MdAriaService($$rAF, $log, $window, $interpolate) { function getText(element) { element = element[0] || element; - var walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT, null, false); + var walker = $document[0].createTreeWalker(element, $window.NodeFilter.SHOW_TEXT, null, false); var text = ''; var node; diff --git a/src/core/services/compiler/compiler.js b/src/core/services/compiler/compiler.js index 825236f0fc..bf582f0578 100644 --- a/src/core/services/compiler/compiler.js +++ b/src/core/services/compiler/compiler.js @@ -172,12 +172,14 @@ function MdCompilerProvider($compileProvider) { return false; } - this.$get = ["$q", "$templateRequest", "$injector", "$compile", "$controller", - function($q, $templateRequest, $injector, $compile, $controller) { - return new MdCompilerService($q, $templateRequest, $injector, $compile, $controller); + this.$get = ["$injector", + function($injector) { + return $injector.instantiate(MdCompilerService); }]; - - function MdCompilerService($q, $templateRequest, $injector, $compile, $controller) { + /** + * @ngInject + */ + function MdCompilerService($q, $templateRequest, $injector, $compile, $controller, $document) { /** @private @const {!angular.$q} */ this.$q = $q; @@ -193,6 +195,9 @@ function MdCompilerProvider($compileProvider) { /** @private @const {!angular.$controller} */ this.$controller = $controller; + + /** @private @const {!angular.$controller} */ + this.$document = $document; } /** @@ -395,7 +400,7 @@ function MdCompilerProvider($compileProvider) { * @returns {{element: !JQLite, restore: !Function}} */ MdCompilerService.prototype._fetchContentElement = function(options) { - + var document = this.$document[0]; var contentEl = options.contentElement; var restoreFn = null; diff --git a/src/core/services/gesture/gesture.js b/src/core/services/gesture/gesture.js index 884cd081d1..7108fb591b 100644 --- a/src/core/services/gesture/gesture.js +++ b/src/core/services/gesture/gesture.js @@ -66,8 +66,8 @@ MdGestureProvider.prototype = { * $get is used to build an instance of $mdGesture * @ngInject */ - $get : function($$MdGestureHandler, $$rAF, $timeout) { - return new MdGesture($$MdGestureHandler, $$rAF, $timeout); + $get : function($injector) { + return $injector.instantiate(MdGesture); } }; @@ -77,7 +77,9 @@ MdGestureProvider.prototype = { * MdGesture factory construction function * @ngInject */ -function MdGesture($$MdGestureHandler, $$rAF, $timeout) { +function MdGesture($$MdGestureHandler, $$rAF, $timeout, $window, $document) { + var window = $window; + var navigator = $window.navigator; var userAgent = navigator.userAgent || navigator.vendor || window.opera; var isIos = userAgent.match(/ipad|iphone|ipod/i); var isAndroid = userAgent.match(/android/i); @@ -328,7 +330,7 @@ function MdGesture($$MdGestureHandler, $$rAF, $timeout) { }); function getTouchAction() { - var testEl = document.createElement('div'); + var testEl = $document[0].createElement('div'); var vendorPrefixes = ['', 'webkit', 'Moz', 'MS', 'ms', 'o']; for (var i = 0; i < vendorPrefixes.length; i++) { @@ -358,8 +360,11 @@ function GestureHandler (name) { this.state = {}; } -function MdGestureHandler() { - var hasJQuery = (typeof window.jQuery !== 'undefined') && (angular.element === window.jQuery); +/** + * @ngInject + */ +function MdGestureHandler($window, $document) { + var hasJQuery = (typeof $window.jQuery !== 'undefined') && (angular.element === $window.jQuery); GestureHandler.prototype = { options: {}, @@ -481,13 +486,14 @@ function MdGestureHandler() { * @param eventPointer the pointer object that matches this event. */ function nativeDispatchEvent(srcEvent, eventType, eventPointer) { + var document = $document[0]; eventPointer = eventPointer || pointer; var eventObj; if (eventType === 'click' || eventType == 'mouseup' || eventType == 'mousedown' ) { eventObj = document.createEvent('MouseEvents'); eventObj.initMouseEvent( - eventType, true, true, window, srcEvent.detail, + eventType, true, true, $window, srcEvent.detail, eventPointer.x, eventPointer.y, eventPointer.x, eventPointer.y, srcEvent.ctrlKey, srcEvent.altKey, srcEvent.shiftKey, srcEvent.metaKey, srcEvent.button, srcEvent.relatedTarget || null @@ -509,10 +515,11 @@ function MdGestureHandler() { * Attach Gestures: hook document and check shouldHijack clicks * @ngInject */ -function attachToDocument( $mdGesture, $$MdGestureHandler ) { +function attachToDocument( $mdGesture, $$MdGestureHandler, $document) { // Polyfill document.contains for IE11. - // TODO: move to util + // TODO: avoid muating $document! + var document = $document[0]; document.contains || (document.contains = function (node) { return document.body.contains(node); }); diff --git a/src/core/services/interaction/interaction.js b/src/core/services/interaction/interaction.js index 845012c9c9..6e2853dd67 100644 --- a/src/core/services/interaction/interaction.js +++ b/src/core/services/interaction/interaction.js @@ -33,11 +33,16 @@ angular * * */ -function MdInteractionService($timeout, $mdUtil) { +/** + * @ngInject + */ +function MdInteractionService($timeout, $mdUtil, $document, $window) { this.$timeout = $timeout; this.$mdUtil = $mdUtil; + this.$window = $window; + this.$document = $document; - this.bodyElement = angular.element(document.body); + this.bodyElement = angular.element($document[0].body); this.isBuffering = false; this.bufferTimeout = null; this.lastInteractionType = null; @@ -72,6 +77,8 @@ function MdInteractionService($timeout, $mdUtil) { * body element. */ MdInteractionService.prototype.initializeEvents = function() { + var window = this.$window; + var document = this.$document[0]; // IE browsers can also trigger pointer events, which also leads to an interaction. var pointerEvent = 'MSPointerEvent' in window ? 'MSPointerDown' : 'PointerEvent' in window ? 'pointerdown' : null; diff --git a/src/core/services/layout/layout.js b/src/core/services/layout/layout.js index 191c9af4d0..0f04158eb1 100644 --- a/src/core/services/layout/layout.js +++ b/src/core/services/layout/layout.js @@ -11,25 +11,6 @@ var ALIGNMENT_MAIN_AXIS= [ "", "start", "center", "end", "stretch", "space-around", "space-between" ]; var ALIGNMENT_CROSS_AXIS= [ "", "start", "center", "end", "stretch" ]; - var config = { - /** - * Enable directive attribute-to-class conversions - * Developers can use `` to quickly - * disable the Layout directives and prohibit the injection of Layout classNames - */ - enabled: true, - - /** - * List of mediaQuery breakpoints and associated suffixes - * - * [ - * { suffix: "sm", mediaQuery: "screen and (max-width: 599px)" }, - * { suffix: "md", mediaQuery: "screen and (min-width: 600px) and (max-width: 959px)" } - * ] - */ - breakpoints: [] - }; - registerLayoutAPI( angular.module('material.core.layout', ['ng']) ); /** @@ -73,8 +54,8 @@ * ``` */ function registerLayoutAPI(module){ - var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i; - var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g; + var PREFIX_REGEXP = /^((?:x|data)[:\-_])/i; + var SPECIAL_CHARS_REGEXP = /([:\-_]+(.))/g; // NOTE: these are also defined in constants::MEDIA_PRIORITY and constants::MEDIA var BREAKPOINTS = [ "", "xs", "gt-xs", "sm", "gt-sm", "md", "gt-md", "lg", "gt-lg", "xl", "print" ]; @@ -102,10 +83,28 @@ // Register other, special directive functions for the Layout features: module - .provider('$$mdLayout' , function() { + .provider('$$mdLayout', function() { + /** + * Enable directive attribute-to-class conversions + * Developers can use `` to quickly + * disable the Layout directives and prohibit the injection of Layout classNames + */ + var enabled = true; // Publish internal service for Layouts return { - $get : angular.noop, + $get : function() { + return { + enabled: function() { + return enabled; + }, + disabled: function() { + return !enabled; + }, + disable: function() { + enabled = false; + } + }; + }, validateAttributeValue : validateAttributeValue, validateAttributeUsage : validateAttributeUsage, /** @@ -113,7 +112,7 @@ * When disabled, this stops all attribute-to-classname generations */ disableLayouts : function(isDisabled) { - config.enabled = (isDisabled !== true); + enabled = (isDisabled !== true); } }; }) @@ -145,8 +144,7 @@ .directive('showLtMd' , warnAttrNotSupported('show-lt-md')) .directive('showLtLg' , warnAttrNotSupported('show-lt-lg')) - // Determine if - .config( detectDisabledLayouts ); + .run( detectDisabledLayouts ); /** * Converts snake_case to camelCase. @@ -173,9 +171,10 @@ /** * @ngInject */ - function detectDisabledLayouts() { - var isDisabled = !!document.querySelector('[md-layouts-disabled]'); - config.enabled = !isDisabled; + function detectDisabledLayouts($document, $$mdLayout) { + if ($document[0].querySelector('[md-layouts-disabled]')) { + $$mdLayout.disable(); + } } /** @@ -198,9 +197,12 @@ * conversions; this would obviate the use of the `md-layout-css` directive * */ - function disableLayoutDirective() { + /** + * @ngInject + */ + function disableLayoutDirective($$mdLayout) { // Return a 1x-only, first-match attribute directive - config.enabled = false; + $$mdLayout.disable(); return { restrict : 'A', @@ -213,12 +215,12 @@ * finish processing. Eliminates flicker with Material.Layouts */ function buildCloakInterceptor(className) { - return [ '$timeout', function($timeout){ + return [ '$timeout', '$$mdLayout', function($timeout, $$mdLayout){ return { restrict : 'A', priority : -10, // run after normal ng-cloak compile : function( element ) { - if (!config.enabled) return angular.noop; + if ($$mdLayout.disabled()) return angular.noop; // Re-add the cloak element.addClass(className); @@ -253,7 +255,7 @@ */ function attributeWithObserve(className) { - return ['$mdUtil', '$interpolate', "$log", function(_$mdUtil_, _$interpolate_, _$log_) { + return ['$mdUtil', '$interpolate', "$log", '$$mdLayout', function(_$mdUtil_, _$interpolate_, _$log_, $$mdLayout) { $mdUtil = _$mdUtil_; $interpolate = _$interpolate_; $log = _$log_; @@ -262,7 +264,7 @@ restrict: 'A', compile: function(element, attr) { var linkFn; - if (config.enabled) { + if ($$mdLayout.enabled()) { // immediately replace static (non-interpolated) invalid values... validateAttributeUsage(className, attr, element, $log); @@ -300,7 +302,7 @@ * any attribute value */ function attributeWithoutValue(className) { - return ['$mdUtil', '$interpolate', "$log", function(_$mdUtil_, _$interpolate_, _$log_) { + return ['$mdUtil', '$interpolate', "$log", '$$mdLayout', function(_$mdUtil_, _$interpolate_, _$log_, $$mdLayout) { $mdUtil = _$mdUtil_; $interpolate = _$interpolate_; $log = _$log_; @@ -309,7 +311,7 @@ restrict: 'A', compile: function(element, attr) { var linkFn; - if (config.enabled) { + if ($$mdLayout.enabled()) { // immediately replace static (non-interpolated) invalid values... validateAttributeValue( className, @@ -441,7 +443,6 @@ case 'layout-margin' : case 'layout-fill' : case 'layout-wrap' : - case 'layout-nowrap' : case 'layout-nowrap' : value = ''; break; diff --git a/src/core/services/liveAnnouncer/live-announcer.js b/src/core/services/liveAnnouncer/live-announcer.js index 45dad2a9fa..2bd22cfc13 100644 --- a/src/core/services/liveAnnouncer/live-announcer.js +++ b/src/core/services/liveAnnouncer/live-announcer.js @@ -30,12 +30,12 @@ angular * * */ -function MdLiveAnnouncer($timeout) { +function MdLiveAnnouncer($timeout, $document) { /** @private @const @type {!angular.$timeout} */ this._$timeout = $timeout; /** @private @const @type {!HTMLElement} */ - this._liveElement = this._createLiveElement(); + this._liveElement = createLiveElement($document[0]); /** @private @const @type {!number} */ this._announceTimeout = 100; @@ -72,9 +72,8 @@ MdLiveAnnouncer.prototype.announce = function(message, politeness) { * Creates a live announcer element, which listens for DOM changes and announces them * to the screenreaders. * @returns {!HTMLElement} - * @private */ -MdLiveAnnouncer.prototype._createLiveElement = function() { +function createLiveElement(document) { var liveEl = document.createElement('div'); liveEl.classList.add('md-visually-hidden'); @@ -85,4 +84,4 @@ MdLiveAnnouncer.prototype._createLiveElement = function() { document.body.appendChild(liveEl); return liveEl; -}; +} diff --git a/src/core/services/meta/meta.js b/src/core/services/meta/meta.js index 96011155af..3450ba74e2 100644 --- a/src/core/services/meta/meta.js +++ b/src/core/services/meta/meta.js @@ -5,16 +5,16 @@ * * @description * - * A provider and a service that simplifies meta tags access + * A service that simplifies meta tags access * * Note: This is intended only for use with dynamic meta tags such as browser color and title. * Tags that are only processed when the page is rendered (such as `charset`, and `http-equiv`) * will not work since `$$mdMeta` adds the tags after the page has already been loaded. * * ```js - * app.config(function($$mdMetaProvider) { - * var removeMeta = $$mdMetaProvider.setMeta('meta-name', 'content'); - * var metaValue = $$mdMetaProvider.getMeta('meta-name'); // -> 'content' + * app.run(function($$mdMeta) { + * var removeMeta = $$mdMeta.setMeta('meta-name', 'content'); + * var metaValue = $$mdMeta.getMeta('meta-name'); // -> 'content' * * removeMeta(); * }); @@ -31,8 +31,8 @@ * */ angular.module('material.core.meta', []) - .provider('$$mdMeta', function () { - var head = angular.element(document.head); + .factory('$$mdMeta', ['$document', function ($document) { + var head = angular.element($document[0].head); var metaElements = {}; /** @@ -46,7 +46,7 @@ angular.module('material.core.meta', []) return true; } - var element = document.getElementsByName(name)[0]; + var element = $document[0].getElementsByName(name)[0]; if (!element) { return false; @@ -107,14 +107,8 @@ angular.module('material.core.meta', []) return metaElements[name].attr('content'); } - var module = { + return { setMeta: setMeta, getMeta: getMeta }; - - return angular.extend({}, module, { - $get: function () { - return module; - } - }); - }); \ No newline at end of file + }]); diff --git a/src/core/services/theming/theming.js b/src/core/services/theming/theming.js index d8663474ff..8f123ea6b4 100644 --- a/src/core/services/theming/theming.js +++ b/src/core/services/theming/theming.js @@ -11,21 +11,8 @@ angular.module('material.core.theming', ['material.core.theming.palette', 'mater .directive('mdThemable', ThemableDirective) .directive('mdThemesDisabled', disableThemesDirective ) .provider('$mdTheming', ThemingProvider) - .config( detectDisabledThemes ) .run(generateAllThemes); -/** - * Detect if the HTML or the BODY tags has a [md-themes-disabled] attribute - * If yes, then immediately disable all theme stylesheet generation and DOM injection - */ -/** - * @ngInject - */ -function detectDisabledThemes($mdThemingProvider) { - var isDisabled = !!document.querySelector('[md-themes-disabled]'); - $mdThemingProvider.disableTheming(isDisabled); -} - /** * @ngdoc service * @name $mdThemingProvider @@ -252,17 +239,20 @@ var VALID_HUE_VALUES = [ '700', '800', '900', 'A100', 'A200', 'A400', 'A700' ]; -var themeConfig = { - disableTheming : false, // Generate our themes at run time; also disable stylesheet DOM injection - generateOnDemand : false, // Whether or not themes are to be generated on-demand (vs. eagerly). - registeredStyles : [], // Custom styles registered to be used in the theming of custom components. - nonce : null // Nonce to be added as an attribute to the generated themes style tags. -}; +function browserColorSetter() { + var browserColor = {}; + return { + setBrowserColor: function setBrowserColor(color) { + browserColor.color = color; + }, + browserColor: browserColor, + } +} /** - * + * @ngInject */ -function ThemingProvider($mdColorPalette, $$mdMetaProvider) { +function ThemingProvider($mdColorPalette, $injector) { PALETTES = { }; var THEMES = { }; @@ -270,6 +260,9 @@ function ThemingProvider($mdColorPalette, $$mdMetaProvider) { var alwaysWatchTheme = false; var defaultTheme = 'default'; + var bcs = browserColorSetter(); + var doBrowserColor = bcs.setBrowserColor; + var browserColor = bcs.browserColor; // Load JS Defined Palettes angular.extend(PALETTES, $mdColorPalette); @@ -281,11 +274,11 @@ function ThemingProvider($mdColorPalette, $$mdMetaProvider) { * @param {string} color Hex value of the wanted browser color * @returns {Function} Remove function of the meta tags */ - var setBrowserColor = function (color) { + var setBrowserColor = function ($$mdMeta, color) { // Chrome, Firefox OS and Opera - var removeChrome = $$mdMetaProvider.setMeta('theme-color', color); + var removeChrome = $$mdMeta.setMeta('theme-color', color); // Windows Phone - var removeWindows = $$mdMetaProvider.setMeta('msapplication-navbutton-color', color); + var removeWindows = $$mdMeta.setMeta('msapplication-navbutton-color', color); return function () { removeChrome(); @@ -309,7 +302,7 @@ function ThemingProvider($mdColorPalette, $$mdMetaProvider) { * @param {Object=} options Options object for the browser color * @returns {Function} remove function of the browser color */ - var enableBrowserColor = function (options) { + var enableBrowserColor = function ($$mdMeta, options) { options = angular.isObject(options) ? options : {}; var theme = options.theme || 'default'; @@ -320,7 +313,14 @@ function ThemingProvider($mdColorPalette, $$mdMetaProvider) { var color = angular.isObject(palette[hue]) ? palette[hue].hex : palette[hue]; - return setBrowserColor(color); + return setBrowserColor($$mdMeta, color); + }; + + var themeConfig = { + disableTheming : false, // Generate our themes at run time; also disable stylesheet DOM injection + generateOnDemand : false, // Whether or not themes are to be generated on-demand (vs. eagerly). + registeredStyles : [], // Custom styles registered to be used in the theming of custom components. + nonce : null // Nonce to be added as an attribute to the generated themes style tags. }; return themingProvider = { @@ -368,9 +368,13 @@ function ThemingProvider($mdColorPalette, $$mdMetaProvider) { alwaysWatchTheme = alwaysWatch; }, - enableBrowserColor: enableBrowserColor, + enableBrowserColor: function(color) { + doBrowserColor(color); + }, - $get: ThemingService, + $get: ['$injector', function($injector) { + return $injector.invoke(themingService, undefined, { browserColor: browserColor }); + }], _LIGHT_DEFAULT_HUES: LIGHT_DEFAULT_HUES, _DARK_DEFAULT_HUES: DARK_DEFAULT_HUES, _PALETTES: PALETTES, @@ -510,7 +514,8 @@ function ThemingProvider($mdColorPalette, $$mdMetaProvider) { self[colorType + 'Color'] = function() { var args = Array.prototype.slice.call(arguments); - console.warn('$mdThemingProviderTheme.' + colorType + 'Color() has been deprecated. ' + + var $log = $injector.get('$log'); + $log.warn('$mdThemingProviderTheme.' + colorType + 'Color() has been deprecated. ' + 'Use $mdThemingProviderTheme.' + colorType + 'Palette() instead.'); return self[colorType + 'Palette'].apply(self, args); }; @@ -630,7 +635,7 @@ function ThemingProvider($mdColorPalette, $$mdMetaProvider) { */ /* @ngInject */ - function ThemingService($rootScope, $mdUtil, $q, $log) { + function themingService($rootScope, $mdUtil, $q, $log, $document, $$mdMeta, browserColor) { // Allow us to be invoked via a linking function signature. var applyTheme = function (scope, el) { if (el === undefined) { el = scope; scope = undefined; } @@ -656,7 +661,7 @@ function ThemingProvider($mdColorPalette, $$mdMetaProvider) { applyTheme.inherit = inheritTheme; applyTheme.registered = registered; applyTheme.defaultTheme = function() { return defaultTheme; }; - applyTheme.generateTheme = function(name) { generateTheme(THEMES[name], name, themeConfig.nonce); }; + applyTheme.generateTheme = function(name) { generateTheme($document[0], THEMES[name], name, themeConfig.nonce); }; applyTheme.defineTheme = function(name, options) { options = options || {}; @@ -682,7 +687,36 @@ function ThemingProvider($mdColorPalette, $$mdMetaProvider) { return $q.resolve(name); }; - applyTheme.setBrowserColor = enableBrowserColor; + + applyTheme.setBrowserColor = function (color) { + return enableBrowserColor($$mdMeta, color); + }; + + doBrowserColor = function (color) { + $log.warn("$mdThemingProvider.enableBrowserColor() is deprecated, use $mdTheming.setBrowserColor(); in run."); + applyTheme.setBrowserColor(color); + }; + + if ('color' in browserColor) { + doBrowserColor(browserColor.color); + delete browserColor.color; + } + + applyTheme.isDisabled = function() { + return themeConfig.disableTheming; + }; + + applyTheme.registeredStyles = function() { + return themeConfig.registeredStyles.join(''); + }; + + applyTheme.generateOnDemand = function() { + return themeConfig.generateOnDemand; + }; + + applyTheme.nonce = function() { + return themeConfig.nonce; + }; return applyTheme; @@ -863,8 +897,6 @@ function ThemingDirective($mdTheming, $interpolate, $parse, $mdUtil, $q, $log) { * */ function disableThemesDirective() { - themeConfig.disableTheming = true; - // Return a 1x-only, first-match attribute directive return { restrict : 'A', @@ -885,8 +917,8 @@ function parseRules(theme, colorType, rules) { var themeNameRegex = new RegExp('\\.md-' + theme.name + '-theme', 'g'); // Matches '{{ primary-color }}', etc - var hueRegex = new RegExp('(\'|")?{{\\s*(' + colorType + ')-(color|contrast)-?(\\d\\.?\\d*)?\\s*}}(\"|\')?','g'); - var simpleVariableRegex = /'?"?\{\{\s*([a-zA-Z]+)-(A?\d+|hue\-[0-3]|shadow|default)-?(\d\.?\d*)?(contrast)?\s*\}\}'?"?/g; + var hueRegex = new RegExp('(\'|")?{{\\s*(' + colorType + ')-(color|contrast)-?(\\d\\.?\\d*)?\\s*}}("|\')?','g'); + var simpleVariableRegex = /'?"?\{\{\s*([a-zA-Z]+)-(A?\d+|hue-[0-3]|shadow|default)-?(\d\.?\d*)?(contrast)?\s*\}\}'?"?/g; var palette = PALETTES[color.name]; // find and replace simple variables where we use a specific hue, not an entire palette @@ -938,13 +970,25 @@ function parseRules(theme, colorType, rules) { var rulesByType = {}; // Generate our themes at run time given the state of THEMES and PALETTES -function generateAllThemes($injector, $mdTheming) { - var head = document.head; +/** + * @ngInject + */ +function generateAllThemes($injector, $mdTheming, $document) { + /** + * Detect if the HTML or the BODY tags has a [md-themes-disabled] attribute + * If yes, then immediately disable all theme stylesheet generation and DOM injection + */ + var isDisabled = ( + !$mdTheming.isDisabled || // tests tinker with $mdTheming + $mdTheming.isDisabled() || + $document[0].querySelector('[md-themes-disabled]') + ); + var head = $document[0].head; var firstChild = head ? head.firstElementChild : null; - var themeCss = !themeConfig.disableTheming && $injector.has('$MD_THEME_CSS') ? $injector.get('$MD_THEME_CSS') : ''; + var themeCss = !isDisabled && $injector.has('$MD_THEME_CSS') ? $injector.get('$MD_THEME_CSS') : ''; // Append our custom registered styles to the theme stylesheet. - themeCss += themeConfig.registeredStyles.join(''); + themeCss += $mdTheming.registeredStyles ? $mdTheming.registeredStyles() : ''; if ( !firstChild ) return; if (themeCss.length === 0) return; // no rules, so no point in running this expensive task @@ -993,11 +1037,11 @@ function generateAllThemes($injector, $mdTheming) { // If themes are being generated on-demand, quit here. The user will later manually // call generateTheme to do this on a theme-by-theme basis. - if (themeConfig.generateOnDemand) return; + if ($mdTheming.generateOnDemand()) return; angular.forEach($mdTheming.THEMES, function(theme) { if (!GENERATED[theme.name] && !($mdTheming.defaultTheme() !== 'default' && theme.name === 'default')) { - generateTheme(theme, theme.name, themeConfig.nonce); + generateTheme($document[0], theme, theme.name, $mdTheming.nonce()); } }); @@ -1063,7 +1107,7 @@ function generateAllThemes($injector, $mdTheming) { } } -function generateTheme(theme, name, nonce) { +function generateTheme(document, theme, name, nonce) { var head = document.head; var firstChild = head ? head.firstElementChild : null; @@ -1139,4 +1183,4 @@ function rgba(rgbArray, opacity) { } -})(window.angular); +})(angular); diff --git a/src/core/services/theming/theming.spec.js b/src/core/services/theming/theming.spec.js index 047da92a63..173be19687 100644 --- a/src/core/services/theming/theming.spec.js +++ b/src/core/services/theming/theming.spec.js @@ -5,6 +5,7 @@ describe('$mdThemingProvider', function() { var testTheme; var testPalette; var startAngular = inject; + var postRun; beforeEach(function() { @@ -20,7 +21,21 @@ describe('$mdThemingProvider', function() { }); function setup() { - module('material.core', function($mdThemingProvider) { + var testM = 'material.theming.test'; + var coreM = 'material.core'; + var didRun; + // eslint-disable-next-line no-undef + postRun = new Promise(function (resolve) { + didRun = resolve; + }); + + angular + .module(testM, [coreM]) + .run(function ($mdTheming) { + didRun(); + }); + + module(coreM, testM, function($mdThemingProvider) { themingProvider = $mdThemingProvider; testPalette = themingProvider._PALETTES.testPalette = themingProvider._PALETTES.otherTestPalette = { @@ -357,8 +372,10 @@ describe('$mdThemingProvider', function() { themingProvider.enableBrowserColor(); - expect(document.getElementsByName(name).length).toBe(1); - expect(angular.element(document.getElementsByName(name)[0]).attr('content')).toBe(content); + return postRun.then(function() { + expect(document.getElementsByName(name).length).toBe(1); + expect(angular.element(document.getElementsByName(name)[0]).attr('content')).toBe(content); + }); }); it('should use default primary color with hue `200`', function () { @@ -372,8 +389,10 @@ describe('$mdThemingProvider', function() { themingProvider.enableBrowserColor({ hue: hue }); - expect(document.getElementsByName(name).length).toBe(1); - expect(angular.element(document.getElementsByName(name)[0]).attr('content')).toBe(content); + return postRun.then(function() { + expect(document.getElementsByName(name).length).toBe(1); + expect(angular.element(document.getElementsByName(name)[0]).attr('content')).toBe(content); + }); }); it('should use red palette', function () { @@ -385,8 +404,10 @@ describe('$mdThemingProvider', function() { themingProvider.enableBrowserColor({ palette: 'red' }); - expect(document.getElementsByName(name).length).toBe(1); - expect(angular.element(document.getElementsByName(name)[0]).attr('content')).toBe(content); + return postRun.then(function() { + expect(document.getElementsByName(name).length).toBe(1); + expect(angular.element(document.getElementsByName(name)[0]).attr('content')).toBe(content); + }); }); it('should use test theme', function () { @@ -398,10 +419,12 @@ describe('$mdThemingProvider', function() { themingProvider.enableBrowserColor({ theme: 'test' }); - expect(document.getElementsByName(name).length).toBe(1); - expect(angular.element(document.getElementsByName(name)[0]).attr('content')).toBe(content); + return postRun.then(function() { + expect(document.getElementsByName(name).length).toBe(1); + expect(angular.element(document.getElementsByName(name)[0]).attr('content')).toBe(content); + }); }); - }) + }); describe('configuration', function () { beforeEach(function () { @@ -593,7 +616,7 @@ describe('$mdThemeProvider with disabled themes', function() { it('should not set any classnames', function() { inject(function($rootScope, $compile, $mdTheming) { - el = $compile('

Test

')($rootScope); + var el = $compile('

Test

')($rootScope); $mdTheming(el); expect(el.hasClass('md-default-theme')).toBe(false); }); @@ -601,7 +624,7 @@ describe('$mdThemeProvider with disabled themes', function() { it('should not inject any styles', function() { inject(function($rootScope, $compile, $mdTheming) { - el = $compile('

Test

')($rootScope); + var el = $compile('

Test

')($rootScope); $mdTheming(el); var styles = getThemeStyleElements(); diff --git a/src/core/util/animation/animate.js b/src/core/util/animation/animate.js index 96322a4921..d27bd0344d 100644 --- a/src/core/util/animation/animate.js +++ b/src/core/util/animation/animate.js @@ -1,20 +1,23 @@ // Polyfill angular < 1.4 (provide $animateCss) angular .module('material.core') - .factory('$$mdAnimate', function($q, $timeout, $mdConstant, $animateCss){ + .factory('$$mdAnimate', function($injector){ // Since $$mdAnimate is injected into $mdUtil... use a wrapper function // to subsequently inject $mdUtil as an argument to the AnimateDomUtils return function($mdUtil) { - return AnimateDomUtils( $mdUtil, $q, $timeout, $mdConstant, $animateCss); + return $injector.invoke(animateDomUtils, undefined, { $mdUtil: $mdUtil }); }; }); /** * Factory function that requires special injections */ -function AnimateDomUtils($mdUtil, $q, $timeout, $mdConstant, $animateCss) { +/** + * @ngInject + */ +function animateDomUtils($mdUtil, $q, $timeout, $mdConstant, $animateCss, $window) { var self; return self = { /** @@ -92,7 +95,7 @@ function AnimateDomUtils($mdUtil, $q, $timeout, $mdConstant, $animateCss) { * @returns {boolean} True if there is no transition/duration; false otherwise. */ function noTransitionFound(styles) { - styles = styles || window.getComputedStyle(element[0]); + styles = styles || $window.getComputedStyle(element[0]); return styles.transitionDuration == '0s' || (!styles.transition && !styles.transitionProperty); } diff --git a/src/core/util/animation/animateCss.js b/src/core/util/animation/animateCss.js index 51dc161873..1232fd0955 100644 --- a/src/core/util/animation/animateCss.js +++ b/src/core/util/animation/animateCss.js @@ -6,14 +6,6 @@ if (angular.version.minor >= 4) { var forEach = angular.forEach; - var WEBKIT = angular.isDefined(document.documentElement.style.WebkitAppearance); - var TRANSITION_PROP = WEBKIT ? 'WebkitTransition' : 'transition'; - var ANIMATION_PROP = WEBKIT ? 'WebkitAnimation' : 'animation'; - var PREFIX = WEBKIT ? '-webkit-' : ''; - - var TRANSITION_EVENTS = (WEBKIT ? 'webkitTransitionEnd ' : '') + 'transitionend'; - var ANIMATION_EVENTS = (WEBKIT ? 'webkitAnimationEnd ' : '') + 'animationend'; - var $$ForceReflowFactory = ['$document', function($document) { return function() { return $document[0].body.clientWidth + 1; @@ -155,8 +147,16 @@ if (angular.version.minor >= 4) { .factory('$$forceReflow', $$ForceReflowFactory) .factory('$$AnimateRunner', $$AnimateRunnerFactory) .factory('$$rAFMutex', $$rAFMutexFactory) - .factory('$animateCss', ['$window', '$$rAF', '$$AnimateRunner', '$$forceReflow', '$$jqLite', '$timeout', '$animate', - function($window, $$rAF, $$AnimateRunner, $$forceReflow, $$jqLite, $timeout, $animate) { + .factory('$animateCss', ['$window', '$$rAF', '$$AnimateRunner', '$$forceReflow', '$$jqLite', '$timeout', '$animate', '$document', + function($window, $$rAF, $$AnimateRunner, $$forceReflow, $$jqLite, $timeout, $animate, $document) { + + var WEBKIT = angular.isDefined($document[0].documentElement.style.WebkitAppearance); + var TRANSITION_PROP = WEBKIT ? 'WebkitTransition' : 'transition'; + var ANIMATION_PROP = WEBKIT ? 'WebkitAnimation' : 'animation'; + var PREFIX = WEBKIT ? '-webkit-' : ''; + + var TRANSITION_EVENTS = (WEBKIT ? 'webkitTransitionEnd ' : '') + 'transitionend'; + var ANIMATION_EVENTS = (WEBKIT ? 'webkitAnimationEnd ' : '') + 'animationend'; function init(element, options) { diff --git a/src/core/util/animation/animateCss.spec.js b/src/core/util/animation/animateCss.spec.js index 5f1bf7dcbb..639e414966 100644 --- a/src/core/util/animation/animateCss.spec.js +++ b/src/core/util/animation/animateCss.spec.js @@ -450,14 +450,17 @@ describe('$animateCss', function() { function assertStyle(element, prop, val, not) { var node = element[0]; var webKit = '-webkit-'; + var otherVal; + var otherAssertion; + var key; if (typeof prop === 'string') { var assertion = expect(node.style[camelCase(prop)] || node.style[camelCase(webKit+prop)]); not ? assertion.not.toBe(val) : assertion.toBe(val); } else { - for (var key in prop) { - var val = prop[key]; - var assertion = expect(node.style[camelCase(key)] || node.style[camelCase(webKit+key)]); - not ? assertion.not.toBe(val) : assertion.toBe(val); + for (key in prop) { + otherVal = prop[key]; + otherAssertion = expect(node.style[camelCase(key)] || node.style[camelCase(webKit+key)]); + not ? otherAssertion.not.toBe(otherVal) : otherAssertion.toBe(otherVal); } } } diff --git a/src/core/util/color.js b/src/core/util/color.js index b91726bb13..2e9135d6c7 100644 --- a/src/core/util/color.js +++ b/src/core/util/color.js @@ -60,7 +60,7 @@ function ColorUtilFactory() { */ function rgbaToRgb (color) { return color - ? color.replace('rgba', 'rgb').replace(/,[^\),]+\)/, ')') + ? color.replace('rgba', 'rgb').replace(/,[^),]+\)/, ')') : 'rgb(0,0,0)'; } diff --git a/src/core/util/constant.js b/src/core/util/constant.js index 62c560a8f8..a081695489 100755 --- a/src/core/util/constant.js +++ b/src/core/util/constant.js @@ -5,9 +5,9 @@ angular.module('material.core') * Factory function that creates the grab-bag $mdConstant service. * @ngInject */ -function MdConstantFactory() { +function MdConstantFactory($document) { - var prefixTestEl = document.createElement('div'); + var prefixTestEl = $document[0].createElement('div'); var vendorPrefix = getVendorPrefix(prefixTestEl); var isWebkit = /webkit/i.test(vendorPrefix); var SPECIAL_CHARS_REGEXP = /([:\-_]+(.))/g; diff --git a/src/core/util/util.js b/src/core/util/util.js index 0b3b82636e..70f29216d3 100644 --- a/src/core/util/util.js +++ b/src/core/util/util.js @@ -59,8 +59,8 @@ function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate, $in var $mdUtil = { dom: {}, - now: window.performance && window.performance.now ? - angular.bind(window.performance, window.performance.now) : Date.now || function() { + now: $window.performance && $window.performance.now ? + angular.bind($window.performance, $window.performance.now) : Date.now || function() { return new Date().getTime(); }, @@ -121,7 +121,7 @@ function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate, $in clientRect: function(element, offsetParent, isOffsetRect) { var node = getNode(element); - offsetParent = getNode(offsetParent || node.offsetParent || document.body); + offsetParent = getNode(offsetParent || node.offsetParent || $document[0].body); var nodeRect = node.getBoundingClientRect(); // The user can ask for an offsetRect: a rect relative to the offsetParent, @@ -157,7 +157,7 @@ function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate, $in * @returns {number} */ getViewportTop: function() { - return window.scrollY || window.pageYOffset || 0; + return $window.scrollY || $window.pageYOffset || 0; }, /** @@ -348,7 +348,7 @@ function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate, $in forceFocus: function(element) { var node = element[0] || element; - document.addEventListener('click', function focusOnClick(ev) { + $document[0].addEventListener('click', function focusOnClick(ev) { if (ev.target === node && ev.$focus) { node.focus(); ev.stopImmediatePropagation(); @@ -357,8 +357,8 @@ function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate, $in } }, true); - var newEvent = document.createEvent('MouseEvents'); - newEvent.initMouseEvent('click', false, true, window, {}, 0, 0, 0, 0, + var newEvent = $document[0].createEvent('MouseEvents'); + newEvent.initMouseEvent('click', false, true, $window, {}, 0, 0, 0, 0, false, false, false, false, 0, null); newEvent.$material = true; newEvent.$focus = true; @@ -380,7 +380,7 @@ function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate, $in * be property names, property chains, or array indices. */ supplant: function(template, values, pattern) { - pattern = pattern || /\{([^\{\}]*)\}/g; + pattern = pattern || /\{([^{}]*)\}/g; return template.replace(pattern, function(a, b) { var p = b.split('.'), r = values; @@ -570,7 +570,7 @@ function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate, $in * Build polyfill for the Node.contains feature (if needed) */ elementContains: function(node, child) { - var hasContains = (window.Node && window.Node.prototype && Node.prototype.contains); + var hasContains = ($window.Node && $window.Node.prototype && $window.Node.prototype.contains); var findFn = hasContains ? angular.bind(node, node.contains) : angular.bind(node, function(arg) { // compares the positions of two nodes and returns a bitmask return (node === child) || !!(this.compareDocumentPosition(arg) & 16) @@ -600,7 +600,7 @@ function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate, $in * Breadth-First tree scan for element with matching `nodeName` */ function scanTree(element) { - return scanLevel(element) || (!!scanDeep ? scanChildren(element) : null); + return scanLevel(element) || (scanDeep ? scanChildren(element) : null); } /** @@ -736,7 +736,7 @@ function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate, $in getNearestContentElement: function (element) { var current = element.parent()[0]; // Look for the nearest parent md-content, stopping at the rootElement. - while (current && current !== $rootElement[0] && current !== document.body && current.nodeName.toUpperCase() !== 'MD-CONTENT') { + while (current && current !== $rootElement[0] && current !== $document[0].body && current.nodeName.toUpperCase() !== 'MD-CONTENT') { current = current.parentNode; } return current;