Skip to content

Commit d7814ae

Browse files
committed
move input feature detection into service
1 parent 5832b34 commit d7814ae

File tree

11 files changed

+125
-72
lines changed

11 files changed

+125
-72
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<p>Is Android: {{ platform.ANDROID }}</p>
2+
<p>Is iOS: {{ platform.IOS }}</p>
3+
<p>Is Firefox: {{ platform.FIREFOX }}</p>
4+
<p>Is Blink: {{ platform.BLINK }}</p>
5+
<p>Is Webkit: {{ platform.WEBKIT }}</p>
6+
<p>Is Trident: {{ platform.TRIDENT }}</p>
7+
<p>Is Edge: {{ platform.EDGE }}</p>
8+
9+
<p>
10+
Supported input types:
11+
<span *ngFor="let type of feature.supportedInputTypes">{{ type }}, </span>
12+
</p>
Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
import {Component} from '@angular/core';
2-
import {MdPlatform} from '@angular/material';
2+
import {MdFeatureDetector, MdPlatform} from '@angular/material';
3+
34

45
@Component({
5-
template: `
6-
<p>Is Android: {{ platform.ANDROID }}</p>
7-
<p>Is iOS: {{ platform.IOS }}</p>
8-
<p>Is Firefox: {{ platform.FIREFOX }}</p>
9-
<p>Is Blink: {{ platform.BLINK }}</p>
10-
<p>Is Webkit: {{ platform.WEBKIT }}</p>
11-
<p>Is Trident: {{ platform.TRIDENT }}</p>
12-
<p>Is Edge: {{ platform.EDGE }}</p>
13-
`
6+
moduleId: module.id,
7+
selector: 'platform-demo',
8+
templateUrl: 'platform-demo.html',
149
})
1510
export class PlatformDemo {
16-
constructor(public platform: MdPlatform) {}
11+
constructor(public feature: MdFeatureDetector, public platform: MdPlatform) {}
1712
}

src/lib/core/a11y/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ import {FocusTrap} from './focus-trap';
33
import {MdLiveAnnouncer} from './live-announcer';
44
import {InteractivityChecker} from './interactivity-checker';
55
import {CommonModule} from '@angular/common';
6-
import {PlatformModule} from '../platform/platform';
6+
import {MdPlatformModule} from '../platform/index';
77

88
export const A11Y_PROVIDERS = [MdLiveAnnouncer, InteractivityChecker];
99

1010
@NgModule({
11-
imports: [CommonModule, PlatformModule],
11+
imports: [CommonModule, MdPlatformModule],
1212
declarations: [FocusTrap],
1313
exports: [FocusTrap],
1414
})

src/lib/core/core.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export * from './projection/projection';
3131

3232
// Platform
3333
export * from './platform/platform';
34+
export * from './platform/feature-detector';
3435

3536
// Overlay
3637
export {Overlay, OVERLAY_PROVIDERS} from './overlay/overlay';
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import {Injectable} from '@angular/core';
2+
3+
4+
@Injectable()
5+
export class MdFeatureDetector {
6+
/** @returns {Set<string>} the input types supported by this browser. */
7+
get supportedInputTypes(): Set<string> {
8+
if (!this._supportedInputTypes) {
9+
let featureTestInput = document.createElement('input');
10+
this._supportedInputTypes = new Set([
11+
'button',
12+
'checkbox',
13+
'color',
14+
'date',
15+
'datetime-local',
16+
'email',
17+
'file',
18+
'hidden',
19+
'image',
20+
'month',
21+
'number',
22+
'password',
23+
'radio',
24+
'range',
25+
'reset',
26+
'search',
27+
'submit',
28+
'tel',
29+
'text',
30+
'time',
31+
'url',
32+
'week',
33+
].filter(value => {
34+
featureTestInput.setAttribute('type', value);
35+
return featureTestInput.type === value;
36+
}));
37+
}
38+
return this._supportedInputTypes;
39+
}
40+
private _supportedInputTypes: Set<string>;
41+
}

src/lib/core/platform/index.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import {NgModule, ModuleWithProviders} from '@angular/core';
2+
import {MdPlatform} from './platform';
3+
import {MdFeatureDetector} from './feature-detector';
4+
5+
6+
@NgModule({})
7+
export class MdPlatformModule {
8+
static forRoot(): ModuleWithProviders {
9+
return {
10+
ngModule: MdPlatformModule,
11+
providers: [MdFeatureDetector, MdPlatform],
12+
};
13+
}
14+
}

src/lib/core/platform/platform.ts

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Injectable, NgModule, ModuleWithProviders} from '@angular/core';
1+
import {Injectable} from '@angular/core';
22

33
declare const window: any;
44

@@ -12,7 +12,6 @@ const hasV8BreakIterator = (window.Intl && (window.Intl as any).v8BreakIterator)
1212
*/
1313
@Injectable()
1414
export class MdPlatform {
15-
1615
/** Layout Engines */
1716
EDGE = /(edge)/i.test(navigator.userAgent);
1817
TRIDENT = /(msie|trident)/i.test(navigator.userAgent);
@@ -35,15 +34,4 @@ export class MdPlatform {
3534

3635
// Trident on mobile adds the android platform to the userAgent to trick detections.
3736
ANDROID = /android/i.test(navigator.userAgent) && !this.TRIDENT;
38-
39-
}
40-
41-
@NgModule({})
42-
export class PlatformModule {
43-
static forRoot(): ModuleWithProviders {
44-
return {
45-
ngModule: PlatformModule,
46-
providers: [MdPlatform],
47-
};
48-
}
4937
}

src/lib/input/input-wrapper.spec.ts

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
1-
import {async, TestBed, ComponentFixture} from '@angular/core/testing';
1+
import {async, TestBed, ComponentFixture, inject} from '@angular/core/testing';
22
import {Component} from '@angular/core';
33
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
44
import {By} from '@angular/platform-browser';
55
import {MdInputModule} from './input';
66
import {MdInputWrapper} from './input-wrapper';
77
import {MdPlatform} from '../core/platform/platform';
8+
import {MdPlatformModule} from '../core/platform/index';
89

910

1011
describe('MdInputWrapper', function () {
11-
let platform: MdPlatform = new MdPlatform();
12-
1312
beforeEach(async(() => {
1413
TestBed.configureTestingModule({
15-
imports: [MdInputModule.forRoot(), FormsModule, ReactiveFormsModule],
14+
imports: [
15+
MdInputModule.forRoot(),
16+
MdPlatformModule.forRoot(),
17+
FormsModule,
18+
ReactiveFormsModule
19+
],
1620
declarations: [
1721
MdInputWrapperPlaceholderRequiredTestComponent,
1822
MdInputWrapperPlaceholderElementTestComponent,
@@ -47,28 +51,30 @@ describe('MdInputWrapper', function () {
4751
.toBe(true, 'Expected MdInputWrapper to default to having floating placeholders turned on');
4852
});
4953

50-
it('should not be treated as empty if type is date', () => {
51-
if (!(platform.TRIDENT || platform.FIREFOX)) {
52-
let fixture = TestBed.createComponent(MdInputWrapperDateTestController);
53-
fixture.detectChanges();
54+
it('should not be treated as empty if type is date',
55+
inject([MdPlatform], (platform: MdPlatform) => {
56+
if (!(platform.TRIDENT || platform.FIREFOX)) {
57+
let fixture = TestBed.createComponent(MdInputWrapperDateTestController);
58+
fixture.detectChanges();
5459

55-
let el = fixture.debugElement.query(By.css('label')).nativeElement;
56-
expect(el).not.toBeNull();
57-
expect(el.classList.contains('md-empty')).toBe(false);
58-
}
59-
});
60+
let el = fixture.debugElement.query(By.css('label')).nativeElement;
61+
expect(el).not.toBeNull();
62+
expect(el.classList.contains('md-empty')).toBe(false);
63+
}
64+
}));
6065

6166
// Firefox and IE don't support type="date" and fallback to type="text".
62-
it('should be treated as empty if type is date on Firefox and IE', () => {
63-
if (platform.TRIDENT || platform.FIREFOX) {
64-
let fixture = TestBed.createComponent(MdInputWrapperDateTestController);
65-
fixture.detectChanges();
66-
67-
let el = fixture.debugElement.query(By.css('label')).nativeElement;
68-
expect(el).not.toBeNull();
69-
expect(el.classList.contains('md-empty')).toBe(true);
70-
}
71-
});
67+
it('should be treated as empty if type is date on Firefox and IE',
68+
inject([MdPlatform], (platform: MdPlatform) => {
69+
if (platform.TRIDENT || platform.FIREFOX) {
70+
let fixture = TestBed.createComponent(MdInputWrapperDateTestController);
71+
fixture.detectChanges();
72+
73+
let el = fixture.debugElement.query(By.css('label')).nativeElement;
74+
expect(el).not.toBeNull();
75+
expect(el.classList.contains('md-empty')).toBe(true);
76+
}
77+
}));
7278

7379
it('should treat text input type as empty at init', () => {
7480
let fixture = TestBed.createComponent(MdInputWrapperTextTestController);

src/lib/input/input-wrapper.ts

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
} from '@angular/core';
1515
import {MdError, coerceBooleanProperty} from '../core';
1616
import {NgModel} from '@angular/forms';
17+
import {MdFeatureDetector} from '../core/platform/feature-detector';
1718

1819

1920
// Invalid input type. Using one of these will throw an MdInputWrapperUnsupportedTypeError.
@@ -31,22 +32,6 @@ const MD_INPUT_INVALID_TYPES = [
3132
];
3233

3334

34-
const MD_INPUT_NEVER_EMPTY_TYPES = (() => {
35-
let featureTestInput = document.createElement('input');
36-
return [
37-
'date',
38-
'datetime',
39-
'datetime-local',
40-
'month',
41-
'time',
42-
'week'
43-
].filter(value => {
44-
featureTestInput.setAttribute('type', value);
45-
return featureTestInput.type === value;
46-
});
47-
})();
48-
49-
5035
let nextUniqueId = 0;
5136

5237

@@ -152,7 +137,18 @@ export class MdInputDirective implements AfterContentInit {
152137
private get _uid() { return this._cachedUid = this._cachedUid || `md-input-${nextUniqueId++}`; }
153138
private _cachedUid: string;
154139

155-
constructor(private _elementRef: ElementRef, @Optional() private _ngModel: NgModel) {
140+
private _neverEmptyInputTypes = [
141+
'date',
142+
'datetime',
143+
'datetime-local',
144+
'month',
145+
'time',
146+
'week'
147+
].filter(t => this._featureDetector.supportedInputTypes.has(t));
148+
149+
constructor(private _featureDetector: MdFeatureDetector,
150+
private _elementRef: ElementRef,
151+
@Optional() private _ngModel: NgModel) {
156152
// Force setter to be called in case id was not specified.
157153
this.id = this.id;
158154

@@ -177,7 +173,7 @@ export class MdInputDirective implements AfterContentInit {
177173
}
178174
}
179175

180-
private _isNeverEmpty() { return MD_INPUT_NEVER_EMPTY_TYPES.indexOf(this._type) != -1; }
176+
private _isNeverEmpty() { return this._neverEmptyInputTypes.indexOf(this._type) != -1; }
181177

182178
private _onFocus() { this.focused = true; }
183179

src/lib/module.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import {MdToolbarModule} from './toolbar/index';
3232
import {MdTooltipModule} from './tooltip/index';
3333
import {MdMenuModule} from './menu/index';
3434
import {MdDialogModule} from './dialog/index';
35-
import {PlatformModule} from './core/platform/platform';
35+
import {MdPlatformModule} from './core/platform/index';
3636

3737

3838
const MATERIAL_MODULES = [
@@ -63,7 +63,7 @@ const MATERIAL_MODULES = [
6363
PortalModule,
6464
RtlModule,
6565
A11yModule,
66-
PlatformModule,
66+
MdPlatformModule,
6767
ProjectionModule,
6868
DefaultStyleCompatibilityModeModule,
6969
];
@@ -99,7 +99,7 @@ const MATERIAL_MODULES = [
9999
MdSlideToggleModule.forRoot(),
100100
MdSnackBarModule.forRoot(),
101101
MdTooltipModule.forRoot(),
102-
PlatformModule.forRoot(),
102+
MdPlatformModule.forRoot(),
103103
OverlayModule.forRoot(),
104104
DefaultStyleCompatibilityModeModule.forRoot(),
105105
],

src/lib/sidenav/sidenav.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {Component} from '@angular/core';
33
import {By} from '@angular/platform-browser';
44
import {MdSidenav, MdSidenavModule, MdSidenavToggleResult} from './sidenav';
55
import {A11yModule} from '../core/a11y/index';
6-
import {PlatformModule} from '../core/platform/platform';
6+
import {MdPlatformModule} from '../core/platform/index';
77

88

99
function endSidenavTransition(fixture: ComponentFixture<any>) {
@@ -19,7 +19,7 @@ function endSidenavTransition(fixture: ComponentFixture<any>) {
1919
describe('MdSidenav', () => {
2020
beforeEach(async(() => {
2121
TestBed.configureTestingModule({
22-
imports: [MdSidenavModule.forRoot(), A11yModule.forRoot(), PlatformModule.forRoot()],
22+
imports: [MdSidenavModule.forRoot(), A11yModule.forRoot(), MdPlatformModule.forRoot()],
2323
declarations: [
2424
BasicTestApp,
2525
SidenavLayoutTwoSidenavTestApp,

0 commit comments

Comments
 (0)