docs(upgrade): add ngUpgrade guide version 2
closes #1538 This is a major reorganization of the Upgrade guide. * Compatible with the new version of the AngularJS 1 PhoneCat tutorial. * No longer switching Angular 1 code to SystemJS for PhoneCat, to allow beginning Angular 2 migration with fewer preparation steps. SystemJS switch now happens simultaneously with upgrade. (This is based on input from @joeeames) * Testing moved to an appendix to make the main narrative shorter and easier to follow. * Use component methods to do phone filtering and ordering instead of introducing pipes to replace filterFilter and orderByFilter. * Cover issue with camelCase inputs on downgraded components. For authors: * All examples now fully integrated with the example boilerplate. Uses the same Angular 2 version as all other guides. E2E tests are executed along with all the others. * Reduced number of PhoneCat versions from five to three. * Each directory has a README explaining how to run it and what might be peculiar about it. Closes angular/angular#8622 Relates to angular/angular.js#14416 Relates to angular/angular-phonecat#326
|
@ -7,7 +7,8 @@ declare var angular:any;
|
|||
// #docregion register
|
||||
angular.module('heroApp', [])
|
||||
.service('heroes', HeroesService)
|
||||
.directive('heroDetail', upgradeAdapter.downgradeNg2Component(HeroDetailComponent));
|
||||
.directive('heroDetail',
|
||||
upgradeAdapter.downgradeNg2Component(HeroDetailComponent));
|
||||
|
||||
upgradeAdapter.upgradeNg1Provider('heroes');
|
||||
|
||||
|
|
|
@ -30,15 +30,15 @@
|
|||
<!-- #docregion usecomponent -->
|
||||
<div ng-controller="MainController as mainCtrl">
|
||||
<hero-detail [hero]="mainCtrl.hero"
|
||||
(deleted)="mainCtrl.onDelete($event)">
|
||||
(deleted)="mainCtrl.onDelete($event)">
|
||||
</hero-detail>
|
||||
</div>
|
||||
<!-- #enddocregion usecomponent -->
|
||||
<!-- #docregion userepeatedcomponent -->
|
||||
<div ng-controller="MainController as mainCtrl">
|
||||
<hero-detail [hero]="hero"
|
||||
(deleted)="mainCtrl.onDelete($event)"
|
||||
ng-repeat="hero in mainCtrl.heroes">
|
||||
(deleted)="mainCtrl.onDelete($event)"
|
||||
ng-repeat="hero in mainCtrl.heroes">
|
||||
</hero-detail>
|
||||
</div>
|
||||
<!-- #enddocregion userepeatedcomponent-->
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
</head>
|
||||
|
||||
<body ng-app="heroApp" ng-strict-di>
|
||||
<div id="message" ng-controller="MainCtrl as mainCtrl">{{ mainCtrl.message }}</div>
|
||||
<div id="message" ng-controller="MainCtrl as mainCtrl">
|
||||
{{ mainCtrl.message }}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
This is the Angular Phonecat application adjusted to fit our boilerplate project
|
||||
structure.
|
||||
|
||||
The following changes from vanilla Phonecat are applied:
|
||||
|
||||
* The TypeScript config file shown in the guide is `tsconfig.ng1.json` instead
|
||||
of the default, because we don't want to enable `noImplicitAny` for migration.
|
||||
* Karma config for unit tests is in karma.conf.ng1.js because the boilerplate
|
||||
Karma config is not compatible with the way Angular 1 tests need to be run.
|
||||
The shell script run-unit-tests.sh can be used to run the unit tests.
|
||||
* Instead of using Bower, Angular 1 and its dependencies are fetched from a CDN
|
||||
in index.html and karma.conf.ng1.js.
|
||||
* E2E tests have been moved to the parent directory, where `gulp run-e2e-tests` can
|
||||
discover and run them along with all the other examples.
|
||||
* Angular 1 typings (from DefinitelyTyped) are added to typings-ng1 so that
|
||||
TypeScript can recognize Angular 1 code. (typings.json comes from boilerplate
|
||||
so we can't add them there).
|
||||
* Most of the phone JSON and image data removed in the interest of keeping
|
||||
repo weight down. Keeping enough to retain testability of the app.
|
||||
|
||||
## Running the app
|
||||
|
||||
Start like any example
|
||||
|
||||
npm run start
|
||||
|
||||
You'll find the app under the /app path: http://localhost:3002/app/index.html
|
||||
|
||||
## Running unit tests
|
||||
|
||||
./run-unit-tests.sh
|
||||
|
||||
## Running E2E tests
|
||||
|
||||
Like for any example (at the project root):
|
||||
|
||||
gulp run-e2e-tests --filter=phonecat-1
|
|
@ -0,0 +1,104 @@
|
|||
'use strict';
|
||||
|
||||
// Angular E2E Testing Guide:
|
||||
// https://docs.angularjs.org/guide/e2e-testing
|
||||
|
||||
describe('PhoneCat Application', function() {
|
||||
|
||||
beforeAll(function() {
|
||||
browser.baseUrl = 'http://localhost:8080/app/';
|
||||
setProtractorToNg1Mode();
|
||||
});
|
||||
|
||||
it('should redirect `index.html` to `index.html#!/phones', function() {
|
||||
browser.get('index.html');
|
||||
expect(browser.getLocationAbsUrl()).toBe('/phones');
|
||||
});
|
||||
|
||||
describe('View: Phone list', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
browser.get('index.html#!/phones');
|
||||
});
|
||||
|
||||
it('should filter the phone list as a user types into the search box', function() {
|
||||
var phoneList = element.all(by.repeater('phone in $ctrl.phones'));
|
||||
var query = element(by.model('$ctrl.query'));
|
||||
|
||||
expect(phoneList.count()).toBe(20);
|
||||
|
||||
query.sendKeys('nexus');
|
||||
expect(phoneList.count()).toBe(1);
|
||||
|
||||
query.clear();
|
||||
query.sendKeys('motorola');
|
||||
expect(phoneList.count()).toBe(8);
|
||||
});
|
||||
|
||||
it('should be possible to control phone order via the drop-down menu', function() {
|
||||
var queryField = element(by.model('$ctrl.query'));
|
||||
var orderSelect = element(by.model('$ctrl.orderProp'));
|
||||
var nameOption = orderSelect.element(by.css('option[value="name"]'));
|
||||
var phoneNameColumn = element.all(by.repeater('phone in $ctrl.phones').column('phone.name'));
|
||||
|
||||
function getNames() {
|
||||
return phoneNameColumn.map(function(elem) {
|
||||
return elem.getText();
|
||||
});
|
||||
}
|
||||
|
||||
queryField.sendKeys('tablet'); // Let's narrow the dataset to make the assertions shorter
|
||||
|
||||
expect(getNames()).toEqual([
|
||||
'Motorola XOOM\u2122 with Wi-Fi',
|
||||
'MOTOROLA XOOM\u2122'
|
||||
]);
|
||||
|
||||
nameOption.click();
|
||||
|
||||
expect(getNames()).toEqual([
|
||||
'MOTOROLA XOOM\u2122',
|
||||
'Motorola XOOM\u2122 with Wi-Fi'
|
||||
]);
|
||||
});
|
||||
|
||||
it('should render phone specific links', function() {
|
||||
var query = element(by.model('$ctrl.query'));
|
||||
query.sendKeys('nexus');
|
||||
|
||||
element.all(by.css('.phones li a')).first().click();
|
||||
expect(browser.getLocationAbsUrl()).toBe('/phones/nexus-s');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('View: Phone detail', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
browser.get('index.html#!/phones/nexus-s');
|
||||
});
|
||||
|
||||
it('should display the `nexus-s` page', function() {
|
||||
expect(element(by.binding('$ctrl.phone.name')).getText()).toBe('Nexus S');
|
||||
});
|
||||
|
||||
it('should display the first phone image as the main phone image', function() {
|
||||
var mainImage = element(by.css('img.phone.selected'));
|
||||
|
||||
expect(mainImage.getAttribute('src')).toMatch(/img\/phones\/nexus-s.0.jpg/);
|
||||
});
|
||||
|
||||
it('should swap the main image when clicking on a thumbnail image', function() {
|
||||
var mainImage = element(by.css('img.phone.selected'));
|
||||
var thumbnails = element.all(by.css('.phone-thumbs img'));
|
||||
|
||||
thumbnails.get(2).click();
|
||||
expect(mainImage.getAttribute('src')).toMatch(/img\/phones\/nexus-s.2.jpg/);
|
||||
|
||||
thumbnails.get(0).click();
|
||||
expect(mainImage.getAttribute('src')).toMatch(/img\/phones\/nexus-s.0.jpg/);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,67 @@
|
|||
/* Animate `ngRepeat` in `phoneList` component */
|
||||
.phone-list-item.ng-enter,
|
||||
.phone-list-item.ng-leave,
|
||||
.phone-list-item.ng-move {
|
||||
overflow: hidden;
|
||||
transition: 0.5s linear all;
|
||||
}
|
||||
|
||||
.phone-list-item.ng-enter,
|
||||
.phone-list-item.ng-leave.ng-leave-active,
|
||||
.phone-list-item.ng-move {
|
||||
height: 0;
|
||||
margin-bottom: 0;
|
||||
opacity: 0;
|
||||
padding-bottom: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.phone-list-item.ng-enter.ng-enter-active,
|
||||
.phone-list-item.ng-leave,
|
||||
.phone-list-item.ng-move.ng-move-active {
|
||||
height: 120px;
|
||||
margin-bottom: 20px;
|
||||
opacity: 1;
|
||||
padding-bottom: 4px;
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
/* Animate view transitions with `ngView` */
|
||||
.view-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.view-frame {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.view-frame.ng-enter,
|
||||
.view-frame.ng-leave {
|
||||
background: white;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.view-frame.ng-enter {
|
||||
animation: 1s fade-in;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.view-frame.ng-leave {
|
||||
animation: 1s fade-out;
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
@keyframes fade-in {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes fade-out {
|
||||
from { opacity: 1; }
|
||||
to { opacity: 0; }
|
||||
}
|
||||
|
||||
/* Older browsers might need vendor-prefixes for keyframes and animation! */
|
|
@ -0,0 +1,43 @@
|
|||
'use strict';
|
||||
|
||||
angular.
|
||||
module('phonecatApp').
|
||||
animation('.phone', function phoneAnimationFactory() {
|
||||
return {
|
||||
addClass: animateIn,
|
||||
removeClass: animateOut
|
||||
};
|
||||
|
||||
function animateIn(element: JQuery, className: string, done: () => void) {
|
||||
if (className !== 'selected') return;
|
||||
|
||||
element.css({
|
||||
display: 'block',
|
||||
position: 'absolute',
|
||||
top: 500,
|
||||
left: 0
|
||||
}).animate({
|
||||
top: 0
|
||||
}, done);
|
||||
|
||||
return function animateInEnd(wasCanceled: boolean) {
|
||||
if (wasCanceled) element.stop();
|
||||
};
|
||||
}
|
||||
|
||||
function animateOut(element: JQuery, className: string, done: () => void) {
|
||||
if (className !== 'selected') return;
|
||||
|
||||
element.css({
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0
|
||||
}).animate({
|
||||
top: -500
|
||||
}, done);
|
||||
|
||||
return function animateOutEnd(wasCanceled: boolean) {
|
||||
if (wasCanceled) element.stop();
|
||||
};
|
||||
}
|
||||
});
|
|
@ -0,0 +1,18 @@
|
|||
// #docregion
|
||||
angular.
|
||||
module('phonecatApp').
|
||||
config(['$locationProvider' ,'$routeProvider',
|
||||
function config($locationProvider: angular.ILocationProvider,
|
||||
$routeProvider: angular.route.IRouteProvider) {
|
||||
$locationProvider.hashPrefix('!');
|
||||
|
||||
$routeProvider.
|
||||
when('/phones', {
|
||||
template: '<phone-list></phone-list>'
|
||||
}).
|
||||
when('/phones/:phoneId', {
|
||||
template: '<phone-detail></phone-detail>'
|
||||
}).
|
||||
otherwise('/phones');
|
||||
}
|
||||
]);
|
|
@ -1,99 +1,93 @@
|
|||
/* app css stylesheet */
|
||||
|
||||
body {
|
||||
padding-top: 20px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
|
||||
.phone-images {
|
||||
background-color: white;
|
||||
width: 450px;
|
||||
height: 450px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
float: left;
|
||||
h1 {
|
||||
border-bottom: 1px solid gray;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* View: Phone list */
|
||||
.phones {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.thumb {
|
||||
float: left;
|
||||
margin: -0.5em 1em 1.5em 0;
|
||||
padding-bottom: 1em;
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.phones li {
|
||||
clear: both;
|
||||
height: 115px;
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
/** Detail View **/
|
||||
img.phone {
|
||||
.thumb {
|
||||
float: left;
|
||||
margin-right: 3em;
|
||||
margin-bottom: 2em;
|
||||
background-color: white;
|
||||
padding: 2em;
|
||||
height: 400px;
|
||||
width: 400px;
|
||||
display: none;
|
||||
height: 100px;
|
||||
margin: -0.5em 1em 1.5em 0;
|
||||
padding-bottom: 1em;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
img.phone:first-child {
|
||||
/* View: Phone detail */
|
||||
.phone {
|
||||
background-color: white;
|
||||
display: none;
|
||||
float: left;
|
||||
height: 400px;
|
||||
margin-bottom: 2em;
|
||||
margin-right: 3em;
|
||||
padding: 2em;
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.phone:first-child {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
ul.phone-thumbs {
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
.phone-images {
|
||||
background-color: white;
|
||||
float: left;
|
||||
height: 450px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 450px;
|
||||
}
|
||||
|
||||
ul.phone-thumbs li {
|
||||
.phone-thumbs {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.phone-thumbs img {
|
||||
height: 100px;
|
||||
padding: 1em;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.phone-thumbs li {
|
||||
background-color: white;
|
||||
border: 1px solid black;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
margin: 1em;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
ul.phone-thumbs img {
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
ul.phone-thumbs img:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
ul.specs {
|
||||
.specs {
|
||||
clear: both;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
ul.specs > li{
|
||||
.specs dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.specs > li {
|
||||
display: inline-block;
|
||||
width: 200px;
|
||||
vertical-align: top;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
ul.specs > li > span{
|
||||
font-weight: bold;
|
||||
.specs > li > span {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
ul.specs dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h1 {
|
||||
border-bottom: 1px solid gray;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
'use strict';
|
||||
|
||||
// Define the `phonecatApp` module
|
||||
angular.module('phonecatApp', [
|
||||
'ngAnimate',
|
||||
'ngRoute',
|
||||
'core',
|
||||
'phoneDetail',
|
||||
'phoneList',
|
||||
]);
|
|
@ -0,0 +1,14 @@
|
|||
'use strict';
|
||||
|
||||
describe('checkmark', () => {
|
||||
|
||||
beforeEach(angular.mock.module('core'));
|
||||
|
||||
it('should convert boolean values to unicode checkmark or cross',
|
||||
inject(function(checkmarkFilter: (v: boolean) => string) {
|
||||
expect(checkmarkFilter(true)).toBe('\u2713');
|
||||
expect(checkmarkFilter(false)).toBe('\u2718');
|
||||
})
|
||||
);
|
||||
|
||||
});
|
|
@ -0,0 +1,9 @@
|
|||
// #docregion
|
||||
|
||||
angular.
|
||||
module('core').
|
||||
filter('checkmark', function() {
|
||||
return function(input: boolean) {
|
||||
return input ? '\u2713' : '\u2718';
|
||||
};
|
||||
});
|
|
@ -0,0 +1,4 @@
|
|||
'use strict';
|
||||
|
||||
// Define the `core` module
|
||||
angular.module('core', ['core.phone']);
|
|
@ -0,0 +1,4 @@
|
|||
'use strict';
|
||||
|
||||
// Define the `core.phone` module
|
||||
angular.module('core.phone', ['ngResource']);
|
|
@ -0,0 +1,43 @@
|
|||
'use strict';
|
||||
|
||||
describe('Phone', () => {
|
||||
let $httpBackend:angular.IHttpBackendService;
|
||||
let Phone:any;
|
||||
let phonesData = [
|
||||
{name: 'Phone X'},
|
||||
{name: 'Phone Y'},
|
||||
{name: 'Phone Z'}
|
||||
];
|
||||
|
||||
// Add a custom equality tester before each test
|
||||
beforeEach(function() {
|
||||
jasmine.addCustomEqualityTester(angular.equals);
|
||||
});
|
||||
|
||||
// Load the module that contains the `Phone` service before each test
|
||||
beforeEach(angular.mock.module('core.phone'));
|
||||
|
||||
// Instantiate the service and "train" `$httpBackend` before each test
|
||||
beforeEach(inject(function(_$httpBackend_:angular.IHttpBackendService, _Phone_:any) {
|
||||
$httpBackend = _$httpBackend_;
|
||||
$httpBackend.expectGET('phones/phones.json').respond(phonesData);
|
||||
|
||||
Phone = _Phone_;
|
||||
}));
|
||||
|
||||
// Verify that there are no outstanding expectations or requests after each test
|
||||
afterEach(() => {
|
||||
$httpBackend.verifyNoOutstandingExpectation();
|
||||
$httpBackend.verifyNoOutstandingRequest();
|
||||
});
|
||||
|
||||
it('should fetch the phones data from `/phones/phones.json`', () => {
|
||||
var phones = Phone.query();
|
||||
|
||||
expect(phones).toEqual([]);
|
||||
|
||||
$httpBackend.flush();
|
||||
expect(phones).toEqual(phonesData);
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
// #docregion
|
||||
angular.
|
||||
module('core.phone').
|
||||
factory('Phone', ['$resource',
|
||||
function($resource: angular.resource.IResourceService) {
|
||||
return $resource('phones/:phoneId.json', {}, {
|
||||
query: {
|
||||
method: 'GET',
|
||||
params: {phoneId: 'phones'},
|
||||
isArray: true
|
||||
}
|
||||
});
|
||||
}
|
||||
]);
|
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 21 KiB |
|
@ -0,0 +1,35 @@
|
|||
<!doctype html>
|
||||
<html lang="en" ng-app="phonecatApp">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Google Phone Gallery</title>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="app.css" />
|
||||
<link rel="stylesheet" href="app.animations.css" />
|
||||
|
||||
<script src="https://code.jquery.com/jquery-2.2.4.js"></script>
|
||||
<script src="https://code.angularjs.org/1.5.5/angular.js"></script>
|
||||
<script src="https://code.angularjs.org/1.5.5/angular-animate.js"></script>
|
||||
<script src="https://code.angularjs.org/1.5.5/angular-resource.js"></script>
|
||||
<script src="https://code.angularjs.org/1.5.5/angular-route.js"></script>
|
||||
|
||||
<script src="app.module.js"></script>
|
||||
<script src="app.config.js"></script>
|
||||
<script src="app.animations.js"></script>
|
||||
<script src="core/core.module.js"></script>
|
||||
<script src="core/checkmark/checkmark.filter.js"></script>
|
||||
<script src="core/phone/phone.module.js"></script>
|
||||
<script src="core/phone/phone.service.js"></script>
|
||||
<script src="phone-list/phone-list.module.js"></script>
|
||||
<script src="phone-list/phone-list.component.js"></script>
|
||||
<script src="phone-detail/phone-detail.module.js"></script>
|
||||
<script src="phone-detail/phone-detail.component.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="view-container">
|
||||
<div ng-view class="view-frame"></div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,38 @@
|
|||
// #docregion
|
||||
describe('phoneDetail', () => {
|
||||
|
||||
// Load the module that contains the `phoneDetail` component before each test
|
||||
beforeEach(angular.mock.module('phoneDetail'));
|
||||
|
||||
// Test the controller
|
||||
describe('PhoneDetailController', () => {
|
||||
let $httpBackend: angular.IHttpBackendService
|
||||
let ctrl: any;
|
||||
let xyzPhoneData = {
|
||||
name: 'phone xyz',
|
||||
images: ['image/url1.png', 'image/url2.png']
|
||||
};
|
||||
|
||||
beforeEach(inject(($componentController: any,
|
||||
_$httpBackend_: angular.IHttpBackendService,
|
||||
$routeParams: angular.route.IRouteParamsService) => {
|
||||
$httpBackend = _$httpBackend_;
|
||||
$httpBackend.expectGET('phones/xyz.json').respond(xyzPhoneData);
|
||||
|
||||
$routeParams['phoneId'] = 'xyz';
|
||||
|
||||
ctrl = $componentController('phoneDetail');
|
||||
}));
|
||||
|
||||
it('should fetch the phone details', () => {
|
||||
jasmine.addCustomEqualityTester(angular.equals);
|
||||
|
||||
expect(ctrl.phone).toEqual({});
|
||||
|
||||
$httpBackend.flush();
|
||||
expect(ctrl.phone).toEqual(xyzPhoneData);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,24 @@
|
|||
// #docregion
|
||||
class PhoneDetailController {
|
||||
phone: any;
|
||||
mainImageUrl: string;
|
||||
|
||||
static $inject = ['$routeParams', 'Phone'];
|
||||
constructor($routeParams: angular.route.IRouteParamsService, Phone: any) {
|
||||
let phoneId = $routeParams['phoneId'];
|
||||
this.phone = Phone.get({phoneId}, (phone: any) => {
|
||||
this.setImage(phone.images[0]);
|
||||
});
|
||||
}
|
||||
|
||||
setImage(imageUrl: string) {
|
||||
this.mainImageUrl = imageUrl;
|
||||
}
|
||||
}
|
||||
|
||||
angular.
|
||||
module('phoneDetail').
|
||||
component('phoneDetail', {
|
||||
templateUrl: 'phone-detail/phone-detail.template.html',
|
||||
controller: PhoneDetailController
|
||||
});
|
|
@ -0,0 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
// Define the `phoneDetail` module
|
||||
angular.module('phoneDetail', [
|
||||
'ngRoute',
|
||||
'core.phone'
|
||||
]);
|
|
@ -0,0 +1,117 @@
|
|||
<div class="phone-images">
|
||||
<img ng-src="{{img}}" class="phone"
|
||||
ng-class="{selected: img === $ctrl.mainImageUrl}"
|
||||
ng-repeat="img in $ctrl.phone.images" />
|
||||
</div>
|
||||
|
||||
<h1>{{$ctrl.phone.name}}</h1>
|
||||
|
||||
<p>{{$ctrl.phone.description}}</p>
|
||||
|
||||
<ul class="phone-thumbs">
|
||||
<li ng-repeat="img in $ctrl.phone.images">
|
||||
<img ng-src="{{img}}" ng-click="$ctrl.setImage(img)" />
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="specs">
|
||||
<li>
|
||||
<span>Availability and Networks</span>
|
||||
<dl>
|
||||
<dt>Availability</dt>
|
||||
<dd ng-repeat="availability in $ctrl.phone.availability">{{availability}}</dd>
|
||||
</dl>
|
||||
</li>
|
||||
<li>
|
||||
<span>Battery</span>
|
||||
<dl>
|
||||
<dt>Type</dt>
|
||||
<dd>{{$ctrl.phone.battery.type}}</dd>
|
||||
<dt>Talk Time</dt>
|
||||
<dd>{{$ctrl.phone.battery.talkTime}}</dd>
|
||||
<dt>Standby time (max)</dt>
|
||||
<dd>{{$ctrl.phone.battery.standbyTime}}</dd>
|
||||
</dl>
|
||||
</li>
|
||||
<li>
|
||||
<span>Storage and Memory</span>
|
||||
<dl>
|
||||
<dt>RAM</dt>
|
||||
<dd>{{$ctrl.phone.storage.ram}}</dd>
|
||||
<dt>Internal Storage</dt>
|
||||
<dd>{{$ctrl.phone.storage.flash}}</dd>
|
||||
</dl>
|
||||
</li>
|
||||
<li>
|
||||
<span>Connectivity</span>
|
||||
<dl>
|
||||
<dt>Network Support</dt>
|
||||
<dd>{{$ctrl.phone.connectivity.cell}}</dd>
|
||||
<dt>WiFi</dt>
|
||||
<dd>{{$ctrl.phone.connectivity.wifi}}</dd>
|
||||
<dt>Bluetooth</dt>
|
||||
<dd>{{$ctrl.phone.connectivity.bluetooth}}</dd>
|
||||
<dt>Infrared</dt>
|
||||
<dd>{{$ctrl.phone.connectivity.infrared | checkmark}}</dd>
|
||||
<dt>GPS</dt>
|
||||
<dd>{{$ctrl.phone.connectivity.gps | checkmark}}</dd>
|
||||
</dl>
|
||||
</li>
|
||||
<li>
|
||||
<span>Android</span>
|
||||
<dl>
|
||||
<dt>OS Version</dt>
|
||||
<dd>{{$ctrl.phone.android.os}}</dd>
|
||||
<dt>UI</dt>
|
||||
<dd>{{$ctrl.phone.android.ui}}</dd>
|
||||
</dl>
|
||||
</li>
|
||||
<li>
|
||||
<span>Size and Weight</span>
|
||||
<dl>
|
||||
<dt>Dimensions</dt>
|
||||
<dd ng-repeat="dim in $ctrl.phone.sizeAndWeight.dimensions">{{dim}}</dd>
|
||||
<dt>Weight</dt>
|
||||
<dd>{{$ctrl.phone.sizeAndWeight.weight}}</dd>
|
||||
</dl>
|
||||
</li>
|
||||
<li>
|
||||
<span>Display</span>
|
||||
<dl>
|
||||
<dt>Screen size</dt>
|
||||
<dd>{{$ctrl.phone.display.screenSize}}</dd>
|
||||
<dt>Screen resolution</dt>
|
||||
<dd>{{$ctrl.phone.display.screenResolution}}</dd>
|
||||
<dt>Touch screen</dt>
|
||||
<dd>{{$ctrl.phone.display.touchScreen | checkmark}}</dd>
|
||||
</dl>
|
||||
</li>
|
||||
<li>
|
||||
<span>Hardware</span>
|
||||
<dl>
|
||||
<dt>CPU</dt>
|
||||
<dd>{{$ctrl.phone.hardware.cpu}}</dd>
|
||||
<dt>USB</dt>
|
||||
<dd>{{$ctrl.phone.hardware.usb}}</dd>
|
||||
<dt>Audio / headphone jack</dt>
|
||||
<dd>{{$ctrl.phone.hardware.audioJack}}</dd>
|
||||
<dt>FM Radio</dt>
|
||||
<dd>{{$ctrl.phone.hardware.fmRadio | checkmark}}</dd>
|
||||
<dt>Accelerometer</dt>
|
||||
<dd>{{$ctrl.phone.hardware.accelerometer | checkmark}}</dd>
|
||||
</dl>
|
||||
</li>
|
||||
<li>
|
||||
<span>Camera</span>
|
||||
<dl>
|
||||
<dt>Primary</dt>
|
||||
<dd>{{$ctrl.phone.camera.primary}}</dd>
|
||||
<dt>Features</dt>
|
||||
<dd>{{$ctrl.phone.camera.features.join(', ')}}</dd>
|
||||
</dl>
|
||||
</li>
|
||||
<li>
|
||||
<span>Additional Features</span>
|
||||
<dd>{{$ctrl.phone.additionalFeatures}}</dd>
|
||||
</li>
|
||||
</ul>
|
|
@ -0,0 +1,36 @@
|
|||
'use strict';
|
||||
|
||||
describe('phoneList', () => {
|
||||
|
||||
// Load the module that contains the `phoneList` component before each test
|
||||
beforeEach(angular.mock.module('phoneList'));
|
||||
|
||||
// Test the controller
|
||||
describe('PhoneListController', () => {
|
||||
let $httpBackend: angular.IHttpBackendService;
|
||||
let ctrl: any;
|
||||
|
||||
beforeEach(inject(($componentController: any, _$httpBackend_: angular.IHttpBackendService) => {
|
||||
$httpBackend = _$httpBackend_;
|
||||
$httpBackend.expectGET('phones/phones.json')
|
||||
.respond([{name: 'Nexus S'}, {name: 'Motorola DROID'}]);
|
||||
|
||||
ctrl = $componentController('phoneList');
|
||||
}));
|
||||
|
||||
it('should create a `phones` property with 2 phones fetched with `$http`', () => {
|
||||
jasmine.addCustomEqualityTester(angular.equals);
|
||||
|
||||
expect(ctrl.phones).toEqual([]);
|
||||
|
||||
$httpBackend.flush();
|
||||
expect(ctrl.phones).toEqual([{name: 'Nexus S'}, {name: 'Motorola DROID'}]);
|
||||
});
|
||||
|
||||
it('should set a default value for the `orderProp` property', () => {
|
||||
expect(ctrl.orderProp).toBe('age');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,20 @@
|
|||
// #docregion
|
||||
class PhoneListController {
|
||||
phones: any[];
|
||||
orderProp: string;
|
||||
query: string;
|
||||
|
||||
static $inject = ['Phone'];
|
||||
constructor(Phone: any) {
|
||||
this.phones = Phone.query();
|
||||
this.orderProp = 'age';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
angular.
|
||||
module('phoneList').
|
||||
component('phoneList', {
|
||||
templateUrl: 'phone-list/phone-list.template.html',
|
||||
controller: PhoneListController
|
||||
});
|
|
@ -0,0 +1,4 @@
|
|||
'use strict';
|
||||
|
||||
// Define the `phoneList` module
|
||||
angular.module('phoneList', ['core.phone']);
|
|
@ -0,0 +1,36 @@
|
|||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<!--Sidebar content-->
|
||||
|
||||
<p>
|
||||
Search:
|
||||
<input ng-model="$ctrl.query" />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Sort by:
|
||||
<select ng-model="$ctrl.orderProp">
|
||||
<option value="name">Alphabetical</option>
|
||||
<option value="age">Newest</option>
|
||||
</select>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
<div class="col-md-10">
|
||||
<!--Body content-->
|
||||
|
||||
<ul class="phones">
|
||||
<li ng-repeat="phone in $ctrl.phones | filter:$ctrl.query | orderBy:$ctrl.orderProp"
|
||||
class="thumbnail phone-list-item">
|
||||
<a href="#!/phones/{{phone.id}}" class="thumb">
|
||||
<img ng-src="{{phone.imageUrl}}" alt="{{phone.name}}" />
|
||||
</a>
|
||||
<a href="#!/phones/{{phone.id}}">{{phone.name}}</a>
|
||||
<p>{{phone.snippet}}</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,64 @@
|
|||
{
|
||||
"additionalFeatures": "Front Facing 1.3MP Camera",
|
||||
"android": {
|
||||
"os": "Android 2.2",
|
||||
"ui": "Dell Stage"
|
||||
},
|
||||
"availability": [
|
||||
"T-Mobile"
|
||||
],
|
||||
"battery": {
|
||||
"standbyTime": "",
|
||||
"talkTime": "",
|
||||
"type": "Lithium Ion (Li-Ion) (2780 mAH)"
|
||||
},
|
||||
"camera": {
|
||||
"features": [
|
||||
"Flash",
|
||||
"Video"
|
||||
],
|
||||
"primary": "5.0 megapixels"
|
||||
},
|
||||
"connectivity": {
|
||||
"bluetooth": "Bluetooth 2.1",
|
||||
"cell": "T-mobile HSPA+ @ 2100/1900/AWS/850 MHz",
|
||||
"gps": true,
|
||||
"infrared": false,
|
||||
"wifi": "802.11 b/g"
|
||||
},
|
||||
"description": "Introducing Dell\u2122 Streak 7. Share photos, videos and movies together. It\u2019s small enough to carry around, big enough to gather around. Android\u2122 2.2-based tablet with over-the-air upgrade capability for future OS releases. A vibrant 7-inch, multitouch display with full Adobe\u00ae Flash 10.1 pre-installed. Includes a 1.3 MP front-facing camera for face-to-face chats on popular services such as Qik or Skype. 16 GB of internal storage, plus Wi-Fi, Bluetooth and built-in GPS keeps you in touch with the world around you. Connect on your terms. Save with 2-year contract or flexibility with prepaid pay-as-you-go plans",
|
||||
"display": {
|
||||
"screenResolution": "WVGA (800 x 480)",
|
||||
"screenSize": "7.0 inches",
|
||||
"touchScreen": true
|
||||
},
|
||||
"hardware": {
|
||||
"accelerometer": true,
|
||||
"audioJack": "3.5mm",
|
||||
"cpu": "nVidia Tegra T20",
|
||||
"fmRadio": false,
|
||||
"physicalKeyboard": false,
|
||||
"usb": "USB 2.0"
|
||||
},
|
||||
"id": "dell-streak-7",
|
||||
"images": [
|
||||
"img/phones/dell-streak-7.0.jpg",
|
||||
"img/phones/dell-streak-7.1.jpg",
|
||||
"img/phones/dell-streak-7.2.jpg",
|
||||
"img/phones/dell-streak-7.3.jpg",
|
||||
"img/phones/dell-streak-7.4.jpg"
|
||||
],
|
||||
"name": "Dell Streak 7",
|
||||
"sizeAndWeight": {
|
||||
"dimensions": [
|
||||
"199.9 mm (w)",
|
||||
"119.8 mm (h)",
|
||||
"12.4 mm (d)"
|
||||
],
|
||||
"weight": "450.0 grams"
|
||||
},
|
||||
"storage": {
|
||||
"flash": "16000MB",
|
||||
"ram": "512MB"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"additionalFeatures": "",
|
||||
"android": {
|
||||
"os": "Android 2.2",
|
||||
"ui": "MOTOBLUR"
|
||||
},
|
||||
"availability": [
|
||||
"AT&T"
|
||||
],
|
||||
"battery": {
|
||||
"standbyTime": "400 hours",
|
||||
"talkTime": "5 hours",
|
||||
"type": "Lithium Ion (Li-Ion) (1930 mAH)"
|
||||
},
|
||||
"camera": {
|
||||
"features": [
|
||||
""
|
||||
],
|
||||
"primary": ""
|
||||
},
|
||||
"connectivity": {
|
||||
"bluetooth": "Bluetooth 2.1",
|
||||
"cell": "WCDMA 850/1900/2100, GSM 850/900/1800/1900, HSDPA 14Mbps (Category 10) Edge Class 12, GPRS Class 12, eCompass, AGPS",
|
||||
"gps": true,
|
||||
"infrared": false,
|
||||
"wifi": "802.11 a/b/g/n"
|
||||
},
|
||||
"description": "MOTOROLA ATRIX 4G gives you dual-core processing power and the revolutionary webtop application. With webtop and a compatible Motorola docking station, sold separately, you can surf the Internet with a full Firefox browser, create and edit docs, or access multimedia on a large screen almost anywhere.",
|
||||
"display": {
|
||||
"screenResolution": "QHD (960 x 540)",
|
||||
"screenSize": "4.0 inches",
|
||||
"touchScreen": true
|
||||
},
|
||||
"hardware": {
|
||||
"accelerometer": true,
|
||||
"audioJack": "3.5mm",
|
||||
"cpu": "1 GHz Dual Core",
|
||||
"fmRadio": false,
|
||||
"physicalKeyboard": false,
|
||||
"usb": "USB 2.0"
|
||||
},
|
||||
"id": "motorola-atrix-4g",
|
||||
"images": [
|
||||
"img/phones/motorola-atrix-4g.0.jpg",
|
||||
"img/phones/motorola-atrix-4g.1.jpg",
|
||||
"img/phones/motorola-atrix-4g.2.jpg",
|
||||
"img/phones/motorola-atrix-4g.3.jpg"
|
||||
],
|
||||
"name": "MOTOROLA ATRIX\u2122 4G",
|
||||
"sizeAndWeight": {
|
||||
"dimensions": [
|
||||
"63.5 mm (w)",
|
||||
"117.75 mm (h)",
|
||||
"10.95 mm (d)"
|
||||
],
|
||||
"weight": "135.0 grams"
|
||||
},
|
||||
"storage": {
|
||||
"flash": "",
|
||||
"ram": ""
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
{
|
||||
"additionalFeatures": "Sensors: proximity, ambient light, barometer, gyroscope",
|
||||
"android": {
|
||||
"os": "Android 3.0",
|
||||
"ui": "Honeycomb"
|
||||
},
|
||||
"availability": [
|
||||
""
|
||||
],
|
||||
"battery": {
|
||||
"standbyTime": "336 hours",
|
||||
"talkTime": "24 hours",
|
||||
"type": "Other ( mAH)"
|
||||
},
|
||||
"camera": {
|
||||
"features": [
|
||||
"Flash",
|
||||
"Video"
|
||||
],
|
||||
"primary": "5.0 megapixels"
|
||||
},
|
||||
"connectivity": {
|
||||
"bluetooth": "Bluetooth 2.1",
|
||||
"cell": "",
|
||||
"gps": true,
|
||||
"infrared": false,
|
||||
"wifi": "802.11 b/g/n"
|
||||
},
|
||||
"description": "Motorola XOOM with Wi-Fi has a super-powerful dual-core processor and Android\u2122 3.0 (Honeycomb) \u2014 the Android platform designed specifically for tablets. With its 10.1-inch HD widescreen display, you\u2019ll enjoy HD video in a thin, light, powerful and upgradeable tablet.",
|
||||
"display": {
|
||||
"screenResolution": "WXGA (1200 x 800)",
|
||||
"screenSize": "10.1 inches",
|
||||
"touchScreen": true
|
||||
},
|
||||
"hardware": {
|
||||
"accelerometer": true,
|
||||
"audioJack": "3.5mm",
|
||||
"cpu": "1 GHz Dual Core Tegra 2",
|
||||
"fmRadio": false,
|
||||
"physicalKeyboard": false,
|
||||
"usb": "USB 2.0"
|
||||
},
|
||||
"id": "motorola-xoom-with-wi-fi",
|
||||
"images": [
|
||||
"img/phones/motorola-xoom-with-wi-fi.0.jpg",
|
||||
"img/phones/motorola-xoom-with-wi-fi.1.jpg",
|
||||
"img/phones/motorola-xoom-with-wi-fi.2.jpg",
|
||||
"img/phones/motorola-xoom-with-wi-fi.3.jpg",
|
||||
"img/phones/motorola-xoom-with-wi-fi.4.jpg",
|
||||
"img/phones/motorola-xoom-with-wi-fi.5.jpg"
|
||||
],
|
||||
"name": "Motorola XOOM\u2122 with Wi-Fi",
|
||||
"sizeAndWeight": {
|
||||
"dimensions": [
|
||||
"249.1 mm (w)",
|
||||
"167.8 mm (h)",
|
||||
"12.9 mm (d)"
|
||||
],
|
||||
"weight": "708.0 grams"
|
||||
},
|
||||
"storage": {
|
||||
"flash": "32000MB",
|
||||
"ram": "1000MB"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"additionalFeatures": "Front-facing camera. Sensors: proximity, ambient light, barometer, gyroscope.",
|
||||
"android": {
|
||||
"os": "Android 3.0",
|
||||
"ui": "Android"
|
||||
},
|
||||
"availability": [
|
||||
"Verizon"
|
||||
],
|
||||
"battery": {
|
||||
"standbyTime": "336 hours",
|
||||
"talkTime": "24 hours",
|
||||
"type": "Other (3250 mAH)"
|
||||
},
|
||||
"camera": {
|
||||
"features": [
|
||||
"Flash",
|
||||
"Video"
|
||||
],
|
||||
"primary": "5.0 megapixels"
|
||||
},
|
||||
"connectivity": {
|
||||
"bluetooth": "Bluetooth 2.1",
|
||||
"cell": "CDMA 800 /1900 LTE 700, Rx diversity in all bands",
|
||||
"gps": true,
|
||||
"infrared": false,
|
||||
"wifi": "802.11 a/b/g/n"
|
||||
},
|
||||
"description": "MOTOROLA XOOM has a super-powerful dual-core processor and Android\u2122 3.0 (Honeycomb) \u2014 the Android platform designed specifically for tablets. With its 10.1-inch HD widescreen display, you\u2019ll enjoy HD video in a thin, light, powerful and upgradeable tablet.",
|
||||
"display": {
|
||||
"screenResolution": "WXGA (1200 x 800)",
|
||||
"screenSize": "10.1 inches",
|
||||
"touchScreen": true
|
||||
},
|
||||
"hardware": {
|
||||
"accelerometer": true,
|
||||
"audioJack": "3.5mm",
|
||||
"cpu": "1 GHz Dual Core Tegra 2",
|
||||
"fmRadio": false,
|
||||
"physicalKeyboard": false,
|
||||
"usb": "USB 2.0"
|
||||
},
|
||||
"id": "motorola-xoom",
|
||||
"images": [
|
||||
"img/phones/motorola-xoom.0.jpg",
|
||||
"img/phones/motorola-xoom.1.jpg",
|
||||
"img/phones/motorola-xoom.2.jpg"
|
||||
],
|
||||
"name": "MOTOROLA XOOM\u2122",
|
||||
"sizeAndWeight": {
|
||||
"dimensions": [
|
||||
"249.0 mm (w)",
|
||||
"168.0 mm (h)",
|
||||
"12.7 mm (d)"
|
||||
],
|
||||
"weight": "726.0 grams"
|
||||
},
|
||||
"storage": {
|
||||
"flash": "32000MB",
|
||||
"ram": "1000MB"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
{
|
||||
"additionalFeatures": "Contour Display, Near Field Communications (NFC), Three-axis gyroscope, Anti-fingerprint display coating, Internet Calling support (VoIP/SIP)",
|
||||
"android": {
|
||||
"os": "Android 2.3",
|
||||
"ui": "Android"
|
||||
},
|
||||
"availability": [
|
||||
"M1,",
|
||||
"O2,",
|
||||
"Orange,",
|
||||
"Singtel,",
|
||||
"StarHub,",
|
||||
"T-Mobile,",
|
||||
"Vodafone"
|
||||
],
|
||||
"battery": {
|
||||
"standbyTime": "428 hours",
|
||||
"talkTime": "6 hours",
|
||||
"type": "Lithium Ion (Li-Ion) (1500 mAH)"
|
||||
},
|
||||
"camera": {
|
||||
"features": [
|
||||
"Flash",
|
||||
"Video"
|
||||
],
|
||||
"primary": "5.0 megapixels"
|
||||
},
|
||||
"connectivity": {
|
||||
"bluetooth": "Bluetooth 2.1",
|
||||
"cell": "Quad-band GSM: 850, 900, 1800, 1900\r\nTri-band HSPA: 900, 2100, 1700\r\nHSPA type: HSDPA (7.2Mbps) HSUPA (5.76Mbps)",
|
||||
"gps": true,
|
||||
"infrared": false,
|
||||
"wifi": "802.11 b/g/n"
|
||||
},
|
||||
"description": "Nexus S is the next generation of Nexus devices, co-developed by Google and Samsung. The latest Android platform (Gingerbread), paired with a 1 GHz Hummingbird processor and 16GB of memory, makes Nexus S one of the fastest phones on the market. It comes pre-installed with the best of Google apps and enabled with new and popular features like true multi-tasking, Wi-Fi hotspot, Internet Calling, NFC support, and full web browsing. With this device, users will also be the first to receive software upgrades and new Google mobile apps as soon as they become available. For more details, visit http://www.google.com/nexus.",
|
||||
"display": {
|
||||
"screenResolution": "WVGA (800 x 480)",
|
||||
"screenSize": "4.0 inches",
|
||||
"touchScreen": true
|
||||
},
|
||||
"hardware": {
|
||||
"accelerometer": true,
|
||||
"audioJack": "3.5mm",
|
||||
"cpu": "1GHz Cortex A8 (Hummingbird) processor",
|
||||
"fmRadio": false,
|
||||
"physicalKeyboard": false,
|
||||
"usb": "USB 2.0"
|
||||
},
|
||||
"id": "nexus-s",
|
||||
"images": [
|
||||
"img/phones/nexus-s.0.jpg",
|
||||
"img/phones/nexus-s.1.jpg",
|
||||
"img/phones/nexus-s.2.jpg",
|
||||
"img/phones/nexus-s.3.jpg"
|
||||
],
|
||||
"name": "Nexus S",
|
||||
"sizeAndWeight": {
|
||||
"dimensions": [
|
||||
"63.0 mm (w)",
|
||||
"123.9 mm (h)",
|
||||
"10.88 mm (d)"
|
||||
],
|
||||
"weight": "129.0 grams"
|
||||
},
|
||||
"storage": {
|
||||
"flash": "16384MB",
|
||||
"ram": "512MB"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
[
|
||||
{
|
||||
"age": 0,
|
||||
"id": "motorola-xoom-with-wi-fi",
|
||||
"imageUrl": "img/phones/motorola-xoom-with-wi-fi.0.jpg",
|
||||
"name": "Motorola XOOM\u2122 with Wi-Fi",
|
||||
"snippet": "The Next, Next Generation\r\n\r\nExperience the future with Motorola XOOM with Wi-Fi, the world's first tablet powered by Android 3.0 (Honeycomb)."
|
||||
},
|
||||
{
|
||||
"age": 1,
|
||||
"id": "motorola-xoom",
|
||||
"imageUrl": "img/phones/motorola-xoom.0.jpg",
|
||||
"name": "MOTOROLA XOOM\u2122",
|
||||
"snippet": "The Next, Next Generation\n\nExperience the future with MOTOROLA XOOM, the world's first tablet powered by Android 3.0 (Honeycomb)."
|
||||
},
|
||||
{
|
||||
"age": 2,
|
||||
"carrier": "AT&T",
|
||||
"id": "motorola-atrix-4g",
|
||||
"imageUrl": "img/phones/motorola-atrix-4g.0.jpg",
|
||||
"name": "MOTOROLA ATRIX\u2122 4G",
|
||||
"snippet": "MOTOROLA ATRIX 4G the world's most powerful smartphone."
|
||||
},
|
||||
{
|
||||
"age": 3,
|
||||
"id": "dell-streak-7",
|
||||
"imageUrl": "img/phones/dell-streak-7.0.jpg",
|
||||
"name": "Dell Streak 7",
|
||||
"snippet": "Introducing Dell\u2122 Streak 7. Share photos, videos and movies together. It\u2019s small enough to carry around, big enough to gather around."
|
||||
},
|
||||
{
|
||||
"age": 4,
|
||||
"carrier": "Cellular South",
|
||||
"id": "samsung-gem",
|
||||
"imageUrl": "img/phones/samsung-gem.0.jpg",
|
||||
"name": "Samsung Gem\u2122",
|
||||
"snippet": "The Samsung Gem\u2122 brings you everything that you would expect and more from a touch display smart phone \u2013 more apps, more features and a more affordable price."
|
||||
},
|
||||
{
|
||||
"age": 5,
|
||||
"carrier": "Dell",
|
||||
"id": "dell-venue",
|
||||
"imageUrl": "img/phones/dell-venue.0.jpg",
|
||||
"name": "Dell Venue",
|
||||
"snippet": "The Dell Venue; Your Personal Express Lane to Everything"
|
||||
},
|
||||
{
|
||||
"age": 6,
|
||||
"carrier": "Best Buy",
|
||||
"id": "nexus-s",
|
||||
"imageUrl": "img/phones/nexus-s.0.jpg",
|
||||
"name": "Nexus S",
|
||||
"snippet": "Fast just got faster with Nexus S. A pure Google experience, Nexus S is the first phone to run Gingerbread (Android 2.3), the fastest version of Android yet."
|
||||
},
|
||||
{
|
||||
"age": 7,
|
||||
"carrier": "Cellular South",
|
||||
"id": "lg-axis",
|
||||
"imageUrl": "img/phones/lg-axis.0.jpg",
|
||||
"name": "LG Axis",
|
||||
"snippet": "Android Powered, Google Maps Navigation, 5 Customizable Home Screens"
|
||||
},
|
||||
{
|
||||
"age": 8,
|
||||
"id": "samsung-galaxy-tab",
|
||||
"imageUrl": "img/phones/samsung-galaxy-tab.0.jpg",
|
||||
"name": "Samsung Galaxy Tab\u2122",
|
||||
"snippet": "Feel Free to Tab\u2122. The Samsung Galaxy Tab\u2122 brings you an ultra-mobile entertainment experience through its 7\u201d display, high-power processor and Adobe\u00ae Flash\u00ae Player compatibility."
|
||||
},
|
||||
{
|
||||
"age": 9,
|
||||
"carrier": "Cellular South",
|
||||
"id": "samsung-showcase-a-galaxy-s-phone",
|
||||
"imageUrl": "img/phones/samsung-showcase-a-galaxy-s-phone.0.jpg",
|
||||
"name": "Samsung Showcase\u2122 a Galaxy S\u2122 phone",
|
||||
"snippet": "The Samsung Showcase\u2122 delivers a cinema quality experience like you\u2019ve never seen before. Its innovative 4\u201d touch display technology provides rich picture brilliance, even outdoors"
|
||||
},
|
||||
{
|
||||
"age": 10,
|
||||
"carrier": "Verizon",
|
||||
"id": "droid-2-global-by-motorola",
|
||||
"imageUrl": "img/phones/droid-2-global-by-motorola.0.jpg",
|
||||
"name": "DROID\u2122 2 Global by Motorola",
|
||||
"snippet": "The first smartphone with a 1.2 GHz processor and global capabilities."
|
||||
},
|
||||
{
|
||||
"age": 11,
|
||||
"carrier": "Verizon",
|
||||
"id": "droid-pro-by-motorola",
|
||||
"imageUrl": "img/phones/droid-pro-by-motorola.0.jpg",
|
||||
"name": "DROID\u2122 Pro by Motorola",
|
||||
"snippet": "The next generation of DOES."
|
||||
},
|
||||
{
|
||||
"age": 12,
|
||||
"carrier": "AT&T",
|
||||
"id": "motorola-bravo-with-motoblur",
|
||||
"imageUrl": "img/phones/motorola-bravo-with-motoblur.0.jpg",
|
||||
"name": "MOTOROLA BRAVO\u2122 with MOTOBLUR\u2122",
|
||||
"snippet": "An experience to cheer about."
|
||||
},
|
||||
{
|
||||
"age": 13,
|
||||
"carrier": "T-Mobile",
|
||||
"id": "motorola-defy-with-motoblur",
|
||||
"imageUrl": "img/phones/motorola-defy-with-motoblur.0.jpg",
|
||||
"name": "Motorola DEFY\u2122 with MOTOBLUR\u2122",
|
||||
"snippet": "Are you ready for everything life throws your way?"
|
||||
},
|
||||
{
|
||||
"age": 14,
|
||||
"carrier": "T-Mobile",
|
||||
"id": "t-mobile-mytouch-4g",
|
||||
"imageUrl": "img/phones/t-mobile-mytouch-4g.0.jpg",
|
||||
"name": "T-Mobile myTouch 4G",
|
||||
"snippet": "The T-Mobile myTouch 4G is a premium smartphone designed to deliver blazing fast 4G speeds so that you can video chat from practically anywhere, with or without Wi-Fi."
|
||||
},
|
||||
{
|
||||
"age": 15,
|
||||
"carrier": "US Cellular",
|
||||
"id": "samsung-mesmerize-a-galaxy-s-phone",
|
||||
"imageUrl": "img/phones/samsung-mesmerize-a-galaxy-s-phone.0.jpg",
|
||||
"name": "Samsung Mesmerize\u2122 a Galaxy S\u2122 phone",
|
||||
"snippet": "The Samsung Mesmerize\u2122 delivers a cinema quality experience like you\u2019ve never seen before. Its innovative 4\u201d touch display technology provides rich picture brilliance,even outdoors"
|
||||
},
|
||||
{
|
||||
"age": 16,
|
||||
"carrier": "Sprint",
|
||||
"id": "sanyo-zio",
|
||||
"imageUrl": "img/phones/sanyo-zio.0.jpg",
|
||||
"name": "SANYO ZIO",
|
||||
"snippet": "The Sanyo Zio by Kyocera is an Android smartphone with a combination of ultra-sleek styling, strong performance and unprecedented value."
|
||||
},
|
||||
{
|
||||
"age": 17,
|
||||
"id": "samsung-transform",
|
||||
"imageUrl": "img/phones/samsung-transform.0.jpg",
|
||||
"name": "Samsung Transform\u2122",
|
||||
"snippet": "The Samsung Transform\u2122 brings you a fun way to customize your Android powered touch screen phone to just the way you like it through your favorite themed \u201cSprint ID Service Pack\u201d."
|
||||
},
|
||||
{
|
||||
"age": 18,
|
||||
"id": "t-mobile-g2",
|
||||
"imageUrl": "img/phones/t-mobile-g2.0.jpg",
|
||||
"name": "T-Mobile G2",
|
||||
"snippet": "The T-Mobile G2 with Google is the first smartphone built for 4G speeds on T-Mobile's new network. Get the information you need, faster than you ever thought possible."
|
||||
},
|
||||
{
|
||||
"age": 19,
|
||||
"id": "motorola-charm-with-motoblur",
|
||||
"imageUrl": "img/phones/motorola-charm-with-motoblur.0.jpg",
|
||||
"name": "Motorola CHARM\u2122 with MOTOBLUR\u2122",
|
||||
"snippet": "Motorola CHARM fits easily in your pocket or palm. Includes MOTOBLUR service."
|
||||
}
|
||||
]
|
|
@ -0,0 +1,32 @@
|
|||
//jshint strict: false
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
|
||||
basePath: './app',
|
||||
|
||||
files: [
|
||||
'https://code.angularjs.org/1.5.5/angular.js',
|
||||
'https://code.angularjs.org/1.5.5/angular-animate.js',
|
||||
'https://code.angularjs.org/1.5.5/angular-resource.js',
|
||||
'https://code.angularjs.org/1.5.5/angular-route.js',
|
||||
'https://code.angularjs.org/1.5.5/angular-mocks.js',
|
||||
'**/*.module.js',
|
||||
'*!(.module|.spec).js',
|
||||
'!(bower_components)/**/*!(.module|.spec).js',
|
||||
'**/*.spec.js'
|
||||
],
|
||||
|
||||
autoWatch: true,
|
||||
|
||||
frameworks: ['jasmine'],
|
||||
|
||||
browsers: ['Chrome', 'Firefox'],
|
||||
|
||||
plugins: [
|
||||
'karma-chrome-launcher',
|
||||
'karma-firefox-launcher',
|
||||
'karma-jasmine'
|
||||
]
|
||||
|
||||
});
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
## The boilerplate Karma configuration won't work with Angular 1 tests since
|
||||
## a specific loading configuration is needed for them.
|
||||
## We keep one in karma.conf.ng1.js. This scripts runs the ng1 tests with
|
||||
## that config.
|
||||
|
||||
PATH=$(npm bin):$PATH
|
||||
tsc && karma start karma.conf.ng1.js
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"removeComments": false,
|
||||
"noImplicitAny": false,
|
||||
"suppressImplicitAnyIndexErrors": true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,294 @@
|
|||
// Generated by typings
|
||||
// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/f0b2681b481397d0c03557ac2ac4d70c1c61c464/angularjs/angular-animate.d.ts
|
||||
declare module "angular-animate" {
|
||||
var _: string;
|
||||
export = _;
|
||||
}
|
||||
|
||||
/**
|
||||
* ngAnimate module (angular-animate.js)
|
||||
*/
|
||||
declare namespace angular.animate {
|
||||
interface IAnimateFactory {
|
||||
(...args: any[]): IAnimateCallbackObject;
|
||||
}
|
||||
|
||||
interface IAnimateCallbackObject {
|
||||
eventFn?: (element: IAugmentedJQuery, doneFunction: Function, options: IAnimationOptions) => any;
|
||||
setClass?: (element: IAugmentedJQuery, addedClasses: string, removedClasses: string, doneFunction: Function, options: IAnimationOptions) => any;
|
||||
addClass?: (element: IAugmentedJQuery, addedClasses: string, doneFunction: Function, options: IAnimationOptions) => any;
|
||||
removeClass?: (element: IAugmentedJQuery, removedClasses: string, doneFunction: Function, options: IAnimationOptions) => any;
|
||||
enter?: (element: IAugmentedJQuery, doneFunction: Function, options: IAnimationOptions) => any;
|
||||
leave?: (element: IAugmentedJQuery, doneFunction: Function, options: IAnimationOptions) => any;
|
||||
move?: (element: IAugmentedJQuery, doneFunction: Function, options: IAnimationOptions) => any;
|
||||
animate?: (element: IAugmentedJQuery, fromStyles: string, toStyles: string, doneFunction: Function, options: IAnimationOptions) => any;
|
||||
}
|
||||
|
||||
interface IAnimationPromise extends IPromise<void> {}
|
||||
|
||||
/**
|
||||
* AnimateService
|
||||
* see http://docs.angularjs.org/api/ngAnimate/service/$animate
|
||||
*/
|
||||
interface IAnimateService {
|
||||
/**
|
||||
* Sets up an event listener to fire whenever the animation event has fired on the given element or among any of its children.
|
||||
*
|
||||
* @param event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...)
|
||||
* @param container the container element that will capture each of the animation events that are fired on itself as well as among its children
|
||||
* @param callback the callback function that will be fired when the listener is triggered
|
||||
*/
|
||||
on(event: string, container: JQuery, callback: Function): void;
|
||||
|
||||
/**
|
||||
* Deregisters an event listener based on the event which has been associated with the provided element.
|
||||
*
|
||||
* @param event the animation event (e.g. enter, leave, move, addClass, removeClass, etc...)
|
||||
* @param container the container element the event listener was placed on
|
||||
* @param callback the callback function that was registered as the listener
|
||||
*/
|
||||
off(event: string, container?: JQuery, callback?: Function): void;
|
||||
|
||||
/**
|
||||
* Associates the provided element with a host parent element to allow the element to be animated even if it exists outside of the DOM structure of the Angular application.
|
||||
*
|
||||
* @param element the external element that will be pinned
|
||||
* @param parentElement the host parent element that will be associated with the external element
|
||||
*/
|
||||
pin(element: JQuery, parentElement: JQuery): void;
|
||||
|
||||
/**
|
||||
* Globally enables / disables animations.
|
||||
*
|
||||
* @param element If provided then the element will be used to represent the enable/disable operation.
|
||||
* @param value If provided then set the animation on or off.
|
||||
* @returns current animation state
|
||||
*/
|
||||
enabled(element: JQuery, value?: boolean): boolean;
|
||||
enabled(value: boolean): boolean;
|
||||
|
||||
/**
|
||||
* Cancels the provided animation.
|
||||
*/
|
||||
cancel(animationPromise: IAnimationPromise): void;
|
||||
|
||||
/**
|
||||
* Performs an inline animation on the element.
|
||||
*
|
||||
* @param element the element that will be the focus of the animation
|
||||
* @param from a collection of CSS styles that will be applied to the element at the start of the animation
|
||||
* @param to a collection of CSS styles that the element will animate towards
|
||||
* @param className an optional CSS class that will be added to the element for the duration of the animation (the default class is 'ng-inline-animate')
|
||||
* @param options an optional collection of styles that will be picked up by the CSS transition/animation
|
||||
* @returns the animation callback promise
|
||||
*/
|
||||
animate(element: JQuery, from: any, to: any, className?: string, options?: IAnimationOptions): IAnimationPromise;
|
||||
|
||||
/**
|
||||
* Appends the element to the parentElement element that resides in the document and then runs the enter animation.
|
||||
*
|
||||
* @param element the element that will be the focus of the enter animation
|
||||
* @param parentElement the parent element of the element that will be the focus of the enter animation
|
||||
* @param afterElement the sibling element (which is the previous element) of the element that will be the focus of the enter animation
|
||||
* @param options an optional collection of styles that will be picked up by the CSS transition/animation
|
||||
* @returns the animation callback promise
|
||||
*/
|
||||
enter(element: JQuery, parentElement: JQuery, afterElement?: JQuery, options?: IAnimationOptions): IAnimationPromise;
|
||||
|
||||
/**
|
||||
* Runs the leave animation operation and, upon completion, removes the element from the DOM.
|
||||
*
|
||||
* @param element the element that will be the focus of the leave animation
|
||||
* @param options an optional collection of styles that will be picked up by the CSS transition/animation
|
||||
* @returns the animation callback promise
|
||||
*/
|
||||
leave(element: JQuery, options?: IAnimationOptions): IAnimationPromise;
|
||||
|
||||
/**
|
||||
* Fires the move DOM operation. Just before the animation starts, the animate service will either append
|
||||
* it into the parentElement container or add the element directly after the afterElement element if present.
|
||||
* Then the move animation will be run.
|
||||
*
|
||||
* @param element the element that will be the focus of the move animation
|
||||
* @param parentElement the parent element of the element that will be the focus of the move animation
|
||||
* @param afterElement the sibling element (which is the previous element) of the element that will be the focus of the move animation
|
||||
* @returns the animation callback promise
|
||||
*/
|
||||
move(element: JQuery, parentElement: JQuery, afterElement?: JQuery): IAnimationPromise;
|
||||
|
||||
/**
|
||||
* Triggers a custom animation event based off the className variable and then attaches the className
|
||||
* value to the element as a CSS class.
|
||||
*
|
||||
* @param element the element that will be animated
|
||||
* @param className the CSS class that will be added to the element and then animated
|
||||
* @param options an optional collection of styles that will be picked up by the CSS transition/animation
|
||||
* @returns the animation callback promise
|
||||
*/
|
||||
addClass(element: JQuery, className: string, options?: IAnimationOptions): IAnimationPromise;
|
||||
|
||||
/**
|
||||
* Triggers a custom animation event based off the className variable and then removes the CSS class
|
||||
* provided by the className value from the element.
|
||||
*
|
||||
* @param element the element that will be animated
|
||||
* @param className the CSS class that will be animated and then removed from the element
|
||||
* @param options an optional collection of styles that will be picked up by the CSS transition/animation
|
||||
* @returns the animation callback promise
|
||||
*/
|
||||
removeClass(element: JQuery, className: string, options?: IAnimationOptions): IAnimationPromise;
|
||||
|
||||
/**
|
||||
* Adds and/or removes the given CSS classes to and from the element. Once complete, the done() callback
|
||||
* will be fired (if provided).
|
||||
*
|
||||
* @param element the element which will have its CSS classes changed removed from it
|
||||
* @param add the CSS classes which will be added to the element
|
||||
* @param remove the CSS class which will be removed from the element CSS classes have been set on the element
|
||||
* @param options an optional collection of styles that will be picked up by the CSS transition/animation
|
||||
* @returns the animation callback promise
|
||||
*/
|
||||
setClass(element: JQuery, add: string, remove: string, options?: IAnimationOptions): IAnimationPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* AnimateProvider
|
||||
* see http://docs.angularjs.org/api/ngAnimate/provider/$animateProvider
|
||||
*/
|
||||
interface IAnimateProvider {
|
||||
/**
|
||||
* Registers a new injectable animation factory function.
|
||||
*
|
||||
* @param name The name of the animation.
|
||||
* @param factory The factory function that will be executed to return the animation object.
|
||||
*/
|
||||
register(name: string, factory: IAnimateFactory): void;
|
||||
|
||||
/**
|
||||
* Gets and/or sets the CSS class expression that is checked when performing an animation.
|
||||
*
|
||||
* @param expression The className expression which will be checked against all animations.
|
||||
* @returns The current CSS className expression value. If null then there is no expression value.
|
||||
*/
|
||||
classNameFilter(expression?: RegExp): RegExp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Angular Animation Options
|
||||
* see https://docs.angularjs.org/api/ngAnimate/#applying-directive-specific-styles-to-an-animation
|
||||
*/
|
||||
interface IAnimationOptions {
|
||||
/**
|
||||
* The ending CSS styles (a key/value object) that will be applied across the animation via a CSS transition.
|
||||
*/
|
||||
to?: Object;
|
||||
|
||||
/**
|
||||
* The starting CSS styles (a key/value object) that will be applied at the start of the animation.
|
||||
*/
|
||||
from?: Object;
|
||||
|
||||
/**
|
||||
* The DOM event (e.g. enter, leave, move). When used, a generated CSS class of ng-EVENT and
|
||||
* ng-EVENT-active will be applied to the element during the animation. Multiple events can be provided when
|
||||
* spaces are used as a separator. (Note that this will not perform any DOM operation.)
|
||||
*/
|
||||
event?: string;
|
||||
|
||||
/**
|
||||
* The CSS easing value that will be applied to the transition or keyframe animation (or both).
|
||||
*/
|
||||
easing?: string;
|
||||
|
||||
/**
|
||||
* The raw CSS transition style that will be used (e.g. 1s linear all).
|
||||
*/
|
||||
transition?: string;
|
||||
|
||||
/**
|
||||
* The raw CSS keyframe animation style that will be used (e.g. 1s my_animation linear).
|
||||
*/
|
||||
keyframe?: string;
|
||||
|
||||
/**
|
||||
* A space separated list of CSS classes that will be added to the element and spread across the animation.
|
||||
*/
|
||||
addClass?: string;
|
||||
|
||||
/**
|
||||
* A space separated list of CSS classes that will be removed from the element and spread across
|
||||
* the animation.
|
||||
*/
|
||||
removeClass?: string;
|
||||
|
||||
/**
|
||||
* A number value representing the total duration of the transition and/or keyframe (note that a value
|
||||
* of 1 is 1000ms). If a value of 0 is provided then the animation will be skipped entirely.
|
||||
*/
|
||||
duration?: number;
|
||||
|
||||
/**
|
||||
* A number value representing the total delay of the transition and/or keyframe (note that a value of
|
||||
* 1 is 1000ms). If a value of true is used then whatever delay value is detected from the CSS classes will be
|
||||
* mirrored on the elements styles (e.g. by setting delay true then the style value of the element will be
|
||||
* transition-delay: DETECTED_VALUE). Using true is useful when you want the CSS classes and inline styles to
|
||||
* all share the same CSS delay value.
|
||||
*/
|
||||
delay?: number;
|
||||
|
||||
/**
|
||||
* A numeric time value representing the delay between successively animated elements (Click here to
|
||||
* learn how CSS-based staggering works in ngAnimate.)
|
||||
*/
|
||||
stagger?: number;
|
||||
|
||||
/**
|
||||
* The numeric index representing the stagger item (e.g. a value of 5 is equal to the sixth item
|
||||
* in the stagger; therefore when a stagger option value of 0.1 is used then there will be a stagger delay of 600ms)
|
||||
* applyClassesEarly - Whether or not the classes being added or removed will be used when detecting the animation.
|
||||
* This is set by $animate when enter/leave/move animations are fired to ensure that the CSS classes are resolved in time.
|
||||
* (Note that this will prevent any transitions from occuring on the classes being added and removed.)
|
||||
*/
|
||||
staggerIndex?: number;
|
||||
}
|
||||
|
||||
interface IAnimateCssRunner {
|
||||
/**
|
||||
* Starts the animation
|
||||
*
|
||||
* @returns The animation runner with a done function for supplying a callback.
|
||||
*/
|
||||
start(): IAnimateCssRunnerStart;
|
||||
|
||||
/**
|
||||
* Ends (aborts) the animation
|
||||
*/
|
||||
end(): void;
|
||||
}
|
||||
|
||||
interface IAnimateCssRunnerStart extends IPromise<void> {
|
||||
/**
|
||||
* Allows you to add done callbacks to the running animation
|
||||
*
|
||||
* @param callbackFn: the callback function to be run
|
||||
*/
|
||||
done(callbackFn: (animationFinished: boolean) => void): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* AnimateCssService
|
||||
* see http://docs.angularjs.org/api/ngAnimate/service/$animateCss
|
||||
*/
|
||||
interface IAnimateCssService {
|
||||
(element: JQuery, animateCssOptions: IAnimationOptions): IAnimateCssRunner;
|
||||
}
|
||||
}
|
||||
|
||||
declare module angular {
|
||||
interface IModule {
|
||||
animation(name: string, animationFactory: angular.animate.IAnimateFactory): IModule;
|
||||
animation(name: string, inlineAnnotatedFunction: any[]): IModule;
|
||||
animation(object: Object): IModule;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"resolution": "main",
|
||||
"tree": {
|
||||
"src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/f0b2681b481397d0c03557ac2ac4d70c1c61c464/angularjs/angular-animate.d.ts",
|
||||
"raw": "registry:dt/angular-animate#1.5.0+20160407085121",
|
||||
"typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/f0b2681b481397d0c03557ac2ac4d70c1c61c464/angularjs/angular-animate.d.ts"
|
||||
}
|
||||
}
|
339
public/docs/_examples/upgrade-phonecat-1-typescript/ts/typings-ng1/globals/angular-mocks/index.d.ts
vendored
Normal file
|
@ -0,0 +1,339 @@
|
|||
// Generated by typings
|
||||
// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/5b7257019ae959533341a715b339d2562bbf9b85/angularjs/angular-mocks.d.ts
|
||||
declare module "angular-mocks/ngMock" {
|
||||
var _: string;
|
||||
export = _;
|
||||
}
|
||||
|
||||
declare module "angular-mocks/ngMockE2E" {
|
||||
var _: string;
|
||||
export = _;
|
||||
}
|
||||
|
||||
declare module "angular-mocks/ngAnimateMock" {
|
||||
var _: string;
|
||||
export = _;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// ngMock module (angular-mocks.js)
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
declare namespace angular {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// AngularStatic
|
||||
// We reopen it to add the MockStatic definition
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
interface IAngularStatic {
|
||||
mock: IMockStatic;
|
||||
}
|
||||
|
||||
// see https://docs.angularjs.org/api/ngMock/function/angular.mock.inject
|
||||
interface IInjectStatic {
|
||||
(...fns: Function[]): any;
|
||||
(...inlineAnnotatedConstructor: any[]): any; // this overload is undocumented, but works
|
||||
strictDi(val?: boolean): void;
|
||||
}
|
||||
|
||||
interface IMockStatic {
|
||||
// see https://docs.angularjs.org/api/ngMock/function/angular.mock.dump
|
||||
dump(obj: any): string;
|
||||
|
||||
inject: IInjectStatic
|
||||
|
||||
// see https://docs.angularjs.org/api/ngMock/function/angular.mock.module
|
||||
module: {
|
||||
(...modules: any[]): any;
|
||||
sharedInjector(): void;
|
||||
}
|
||||
|
||||
// see https://docs.angularjs.org/api/ngMock/type/angular.mock.TzDate
|
||||
TzDate(offset: number, timestamp: number): Date;
|
||||
TzDate(offset: number, timestamp: string): Date;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// ExceptionHandlerService
|
||||
// see https://docs.angularjs.org/api/ngMock/service/$exceptionHandler
|
||||
// see https://docs.angularjs.org/api/ngMock/provider/$exceptionHandlerProvider
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
interface IExceptionHandlerProvider extends IServiceProvider {
|
||||
mode(mode: string): void;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// TimeoutService
|
||||
// see https://docs.angularjs.org/api/ngMock/service/$timeout
|
||||
// Augments the original service
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
interface ITimeoutService {
|
||||
flush(delay?: number): void;
|
||||
flushNext(expectedDelay?: number): void;
|
||||
verifyNoPendingTasks(): void;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// IntervalService
|
||||
// see https://docs.angularjs.org/api/ngMock/service/$interval
|
||||
// Augments the original service
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
interface IIntervalService {
|
||||
flush(millis?: number): number;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// LogService
|
||||
// see https://docs.angularjs.org/api/ngMock/service/$log
|
||||
// Augments the original service
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
interface ILogService {
|
||||
assertEmpty(): void;
|
||||
reset(): void;
|
||||
}
|
||||
|
||||
interface ILogCall {
|
||||
logs: string[];
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// ControllerService mock
|
||||
// see https://docs.angularjs.org/api/ngMock/service/$controller
|
||||
// This interface extends http://docs.angularjs.org/api/ng.$controller
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
interface IControllerService {
|
||||
// Although the documentation doesn't state this, locals are optional
|
||||
<T>(controllerConstructor: new (...args: any[]) => T, locals?: any, bindings?: any): T;
|
||||
<T>(controllerConstructor: Function, locals?: any, bindings?: any): T;
|
||||
<T>(controllerName: string, locals?: any, bindings?: any): T;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// ComponentControllerService
|
||||
// see https://docs.angularjs.org/api/ngMock/service/$componentController
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
interface IComponentControllerService {
|
||||
// TBinding is an interface exposed by a component as per John Papa's style guide
|
||||
// https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#accessible-members-up-top
|
||||
<T, TBinding>(componentName: string, locals: { $scope: IScope, [key: string]: any }, bindings?: TBinding, ident?: string): T;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// HttpBackendService
|
||||
// see https://docs.angularjs.org/api/ngMock/service/$httpBackend
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
interface IHttpBackendService {
|
||||
/**
|
||||
* Flushes all pending requests using the trained responses.
|
||||
* @param count Number of responses to flush (in the order they arrived). If undefined, all pending requests will be flushed.
|
||||
*/
|
||||
flush(count?: number): void;
|
||||
|
||||
/**
|
||||
* Resets all request expectations, but preserves all backend definitions.
|
||||
*/
|
||||
resetExpectations(): void;
|
||||
|
||||
/**
|
||||
* Verifies that all of the requests defined via the expect api were made. If any of the requests were not made, verifyNoOutstandingExpectation throws an exception.
|
||||
*/
|
||||
verifyNoOutstandingExpectation(): void;
|
||||
|
||||
/**
|
||||
* Verifies that there are no outstanding requests that need to be flushed.
|
||||
*/
|
||||
verifyNoOutstandingRequest(): void;
|
||||
|
||||
/**
|
||||
* Creates a new request expectation.
|
||||
* Throws a preformatted error if expectation(s) don't match supplied string, regular expression, object, or if function returns false.
|
||||
* Returns an object with respond method that controls how a matched request is handled.
|
||||
* @param method HTTP method.
|
||||
* @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
|
||||
* @param data HTTP request body string, json object, regular expression or function that receives the data and returns true if the data matches the current expectation.
|
||||
* @param headers HTTP headers object or function that receives the headers and returns true if the headers match the current expectation.
|
||||
*/
|
||||
expect(method: string, url: string | RegExp | ((url: string) => boolean), data?: string | RegExp | Object | ((data: string) => boolean), headers?: Object | ((object: Object) => boolean)) :mock.IRequestHandler;
|
||||
|
||||
/**
|
||||
* Creates a new request expectation for DELETE requests.
|
||||
* Throws a preformatted error if expectation(s) don't match supplied string, regular expression, object, or if function returns false.
|
||||
* Returns an object with respond method that controls how a matched request is handled.
|
||||
* @param url HTTP url string, regular expression or function that receives a url and returns true if the url is as expected.
|
||||
* @param headers HTTP headers object to be compared with the HTTP headers in the request.
|
||||
*/
|
||||
expectDELETE(url: string | RegExp | ((url: string) => boolean), headers?: Object): mock.IRequestHandler;
|
||||
|
||||
/**
|
||||
* Creates a new request expectation for GET requests.
|
||||
* Throws a preformatted error if expectation(s) don't match supplied string, regular expression, object, or if function returns false.
|
||||
* Returns an object with respond method that controls how a matched request is handled.
|
||||
* @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
|
||||
* @param headers HTTP headers object to be compared with the HTTP headers in the request.
|
||||
*/
|
||||
expectGET(url: string | RegExp | ((url: string) => boolean), headers?: Object): mock.IRequestHandler;
|
||||
|
||||
/**
|
||||
* Creates a new request expectation for HEAD requests.
|
||||
* Throws a preformatted error if expectation(s) don't match supplied string, regular expression, object, or if function returns false.
|
||||
* Returns an object with respond method that controls how a matched request is handled.
|
||||
* @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
|
||||
* @param headers HTTP headers object to be compared with the HTTP headers in the request.
|
||||
*/
|
||||
expectHEAD(url: string | RegExp | ((url: string) => boolean), headers?: Object): mock.IRequestHandler;
|
||||
|
||||
/**
|
||||
* Creates a new request expectation for JSONP requests.
|
||||
* Throws a preformatted error if expectation(s) don't match supplied string, regular expression, or if function returns false.
|
||||
* Returns an object with respond method that controls how a matched request is handled.
|
||||
* @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
|
||||
*/
|
||||
expectJSONP(url: string | RegExp | ((url: string) => boolean)): mock.IRequestHandler;
|
||||
|
||||
/**
|
||||
* Creates a new request expectation for PATCH requests.
|
||||
* Throws a preformatted error if expectation(s) don't match supplied string, regular expression, object, or if function returns false.
|
||||
* Returns an object with respond method that controls how a matched request is handled.
|
||||
* @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
|
||||
* @param data HTTP request body string, json object, regular expression or function that receives the data and returns true if the data matches the current expectation.
|
||||
* @param headers HTTP headers object or function that receives the headers and returns true if the headers match the current expectation.
|
||||
*/
|
||||
expectPATCH(url: string | RegExp | ((url: string) => boolean), data?: string | RegExp | Object | ((data: string) => boolean), headers?: Object): mock.IRequestHandler;
|
||||
|
||||
/**
|
||||
* Creates a new request expectation for POST requests.
|
||||
* Throws a preformatted error if expectation(s) don't match supplied string, regular expression, object, or if function returns false.
|
||||
* Returns an object with respond method that controls how a matched request is handled.
|
||||
* @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
|
||||
* @param data HTTP request body string, json object, regular expression or function that receives the data and returns true if the data matches the current expectation.
|
||||
* @param headers HTTP headers object or function that receives the headers and returns true if the headers match the current expectation.
|
||||
*/
|
||||
expectPOST(url: string | RegExp | ((url: string) => boolean), data?: string | RegExp | Object | ((data: string) => boolean), headers?: Object): mock.IRequestHandler;
|
||||
|
||||
/**
|
||||
* Creates a new request expectation for PUT requests.
|
||||
* Throws a preformatted error if expectation(s) don't match supplied string, regular expression, object, or if function returns false.
|
||||
* Returns an object with respond method that controls how a matched request is handled.
|
||||
* @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
|
||||
* @param data HTTP request body string, json object, regular expression or function that receives the data and returns true if the data matches the current expectation.
|
||||
* @param headers HTTP headers object or function that receives the headers and returns true if the headers match the current expectation.
|
||||
*/
|
||||
expectPUT(url: string | RegExp | ((url: string) => boolean), data?: string | RegExp | Object | ((data: string) => boolean), headers?: Object): mock.IRequestHandler;
|
||||
|
||||
/**
|
||||
* Creates a new backend definition.
|
||||
* Returns an object with respond method that controls how a matched request is handled.
|
||||
* @param method HTTP method.
|
||||
* @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
|
||||
* @param data HTTP request body string, json object, regular expression or function that receives the data and returns true if the data matches the current expectation.
|
||||
* @param headers HTTP headers object or function that receives the headers and returns true if the headers match the current expectation.
|
||||
*/
|
||||
when(method: string, url: string | RegExp | ((url: string) => boolean), data?: string | RegExp | Object | ((data: string) => boolean), headers?: Object | ((object: Object) => boolean)): mock.IRequestHandler;
|
||||
|
||||
/**
|
||||
* Creates a new backend definition for DELETE requests.
|
||||
* Returns an object with respond method that controls how a matched request is handled.
|
||||
* @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
|
||||
* @param headers HTTP headers object or function that receives the headers and returns true if the headers match the current expectation.
|
||||
*/
|
||||
whenDELETE(url: string | RegExp | ((url: string) => boolean), headers?: Object | ((object: Object) => boolean)): mock.IRequestHandler;
|
||||
|
||||
/**
|
||||
* Creates a new backend definition for GET requests.
|
||||
* Returns an object with respond method that controls how a matched request is handled.
|
||||
* @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
|
||||
* @param headers HTTP headers object or function that receives the headers and returns true if the headers match the current expectation.
|
||||
*/
|
||||
whenGET(url: string | RegExp | ((url: string) => boolean), headers?: Object | ((object: Object) => boolean)): mock.IRequestHandler;
|
||||
|
||||
/**
|
||||
* Creates a new backend definition for HEAD requests.
|
||||
* Returns an object with respond method that controls how a matched request is handled.
|
||||
* @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
|
||||
* @param headers HTTP headers object or function that receives the headers and returns true if the headers match the current expectation.
|
||||
*/
|
||||
whenHEAD(url: string | RegExp | ((url: string) => boolean), headers?: Object | ((object: Object) => boolean)): mock.IRequestHandler;
|
||||
|
||||
/**
|
||||
* Creates a new backend definition for JSONP requests.
|
||||
* Returns an object with respond method that controls how a matched request is handled.
|
||||
* @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
|
||||
* @param headers HTTP headers object or function that receives the headers and returns true if the headers match the current expectation.
|
||||
*/
|
||||
whenJSONP(url: string | RegExp | ((url: string) => boolean)): mock.IRequestHandler;
|
||||
|
||||
/**
|
||||
* Creates a new backend definition for PATCH requests.
|
||||
* Returns an object with respond method that controls how a matched request is handled.
|
||||
* @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
|
||||
* @param data HTTP request body string, json object, regular expression or function that receives the data and returns true if the data matches the current expectation.
|
||||
* @param headers HTTP headers object or function that receives the headers and returns true if the headers match the current expectation.
|
||||
*/
|
||||
whenPATCH(url: string | RegExp | ((url: string) => boolean), data?: string | RegExp | Object | ((data: string) => boolean), headers?: Object | ((object: Object) => boolean)): mock.IRequestHandler;
|
||||
|
||||
/**
|
||||
* Creates a new backend definition for POST requests.
|
||||
* Returns an object with respond method that controls how a matched request is handled.
|
||||
* @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
|
||||
* @param data HTTP request body string, json object, regular expression or function that receives the data and returns true if the data matches the current expectation.
|
||||
* @param headers HTTP headers object or function that receives the headers and returns true if the headers match the current expectation.
|
||||
*/
|
||||
whenPOST(url: string | RegExp | ((url: string) => boolean), data?: string | RegExp | Object | ((data: string) => boolean), headers?: Object | ((object: Object) => boolean)): mock.IRequestHandler;
|
||||
|
||||
/**
|
||||
* Creates a new backend definition for PUT requests.
|
||||
* Returns an object with respond method that controls how a matched request is handled.
|
||||
* @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
|
||||
* @param data HTTP request body string, json object, regular expression or function that receives the data and returns true if the data matches the current expectation.
|
||||
* @param headers HTTP headers object or function that receives the headers and returns true if the headers match the current expectation.
|
||||
*/
|
||||
whenPUT(url: string | RegExp | ((url: string) => boolean), data?: string | RegExp | Object | ((data: string) => boolean), headers?: Object | ((object: Object) => boolean)): mock.IRequestHandler;
|
||||
}
|
||||
|
||||
export module mock {
|
||||
// returned interface by the the mocked HttpBackendService expect/when methods
|
||||
interface IRequestHandler {
|
||||
|
||||
/**
|
||||
* Controls the response for a matched request using a function to construct the response.
|
||||
* Returns the RequestHandler object for possible overrides.
|
||||
* @param func Function that receives the request HTTP method, url, data, and headers and returns an array containing response status (number), data, headers, and status text.
|
||||
*/
|
||||
respond(func: ((method: string, url: string, data: string | Object, headers: Object) => [number, string | Object, Object, string])): IRequestHandler;
|
||||
|
||||
/**
|
||||
* Controls the response for a matched request using supplied static data to construct the response.
|
||||
* Returns the RequestHandler object for possible overrides.
|
||||
* @param status HTTP status code to add to the response.
|
||||
* @param data Data to add to the response.
|
||||
* @param headers Headers object to add to the response.
|
||||
* @param responseText Response text to add to the response.
|
||||
*/
|
||||
respond(status: number, data: string | Object, headers?: Object, responseText?: string): IRequestHandler;
|
||||
|
||||
/**
|
||||
* Controls the response for a matched request using the HTTP status code 200 and supplied static data to construct the response.
|
||||
* Returns the RequestHandler object for possible overrides.
|
||||
* @param data Data to add to the response.
|
||||
* @param headers Headers object to add to the response.
|
||||
* @param responseText Response text to add to the response.
|
||||
*/
|
||||
respond(data: string | Object, headers?: Object, responseText?: string): IRequestHandler;
|
||||
|
||||
// Available when ngMockE2E is loaded
|
||||
/**
|
||||
* Any request matching a backend definition or expectation with passThrough handler will be passed through to the real backend (an XHR request will be made to the server.)
|
||||
*/
|
||||
passThrough(): IRequestHandler;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// functions attached to global object (window)
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//Use `angular.mock.module` instead of `module`, as `module` conflicts with commonjs.
|
||||
//declare var module: (...modules: any[]) => any;
|
||||
declare var inject: angular.IInjectStatic;
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"resolution": "main",
|
||||
"tree": {
|
||||
"src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/5b7257019ae959533341a715b339d2562bbf9b85/angularjs/angular-mocks.d.ts",
|
||||
"raw": "registry:dt/angular-mocks#1.3.0+20160425155016",
|
||||
"typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/5b7257019ae959533341a715b339d2562bbf9b85/angularjs/angular-mocks.d.ts"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
// Generated by typings
|
||||
// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/e684481e0cd360db62fd6213ca7248245315e8a2/angularjs/angular-resource.d.ts
|
||||
declare module 'angular-resource' {
|
||||
var _: string;
|
||||
export = _;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// ngResource module (angular-resource.js)
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
declare namespace angular.resource {
|
||||
|
||||
/**
|
||||
* Currently supported options for the $resource factory options argument.
|
||||
*/
|
||||
interface IResourceOptions {
|
||||
/**
|
||||
* If true then the trailing slashes from any calculated URL will be stripped (defaults to true)
|
||||
*/
|
||||
stripTrailingSlashes?: boolean;
|
||||
/**
|
||||
* If true, the request made by a "non-instance" call will be cancelled (if not already completed) by calling
|
||||
* $cancelRequest() on the call's return value. This can be overwritten per action. (Defaults to false.)
|
||||
*/
|
||||
cancellable?: boolean;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// ResourceService
|
||||
// see http://docs.angularjs.org/api/ngResource.$resource
|
||||
// Most part of the following definitions were achieved by analyzing the
|
||||
// actual implementation, since the documentation doesn't seem to cover
|
||||
// that deeply.
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
interface IResourceService {
|
||||
(url: string, paramDefaults?: any,
|
||||
/** example: {update: { method: 'PUT' }, delete: deleteDescriptor }
|
||||
where deleteDescriptor : IActionDescriptor */
|
||||
actions?: any, options?: IResourceOptions): IResourceClass<IResource<any>>;
|
||||
<T, U>(url: string, paramDefaults?: any,
|
||||
/** example: {update: { method: 'PUT' }, delete: deleteDescriptor }
|
||||
where deleteDescriptor : IActionDescriptor */
|
||||
actions?: any, options?: IResourceOptions): U;
|
||||
<T>(url: string, paramDefaults?: any,
|
||||
/** example: {update: { method: 'PUT' }, delete: deleteDescriptor }
|
||||
where deleteDescriptor : IActionDescriptor */
|
||||
actions?: any, options?: IResourceOptions): IResourceClass<T>;
|
||||
}
|
||||
|
||||
// Just a reference to facilitate describing new actions
|
||||
interface IActionDescriptor {
|
||||
method: string;
|
||||
params?: any;
|
||||
url?: string;
|
||||
isArray?: boolean;
|
||||
transformRequest?: angular.IHttpRequestTransformer | angular.IHttpRequestTransformer[];
|
||||
transformResponse?: angular.IHttpResponseTransformer | angular.IHttpResponseTransformer[];
|
||||
headers?: any;
|
||||
cache?: boolean | angular.ICacheObject;
|
||||
/**
|
||||
* Note: In contrast to $http.config, promises are not supported in $resource, because the same value
|
||||
* would be used for multiple requests. If you are looking for a way to cancel requests, you should
|
||||
* use the cancellable option.
|
||||
*/
|
||||
timeout?: number
|
||||
cancellable?: boolean;
|
||||
withCredentials?: boolean;
|
||||
responseType?: string;
|
||||
interceptor?: IHttpInterceptor;
|
||||
}
|
||||
|
||||
// Allow specify more resource methods
|
||||
// No need to add duplicates for all four overloads.
|
||||
interface IResourceMethod<T> {
|
||||
(): T;
|
||||
(params: Object): T;
|
||||
(success: Function, error?: Function): T;
|
||||
(params: Object, success: Function, error?: Function): T;
|
||||
(params: Object, data: Object, success?: Function, error?: Function): T;
|
||||
}
|
||||
|
||||
// Allow specify resource moethod which returns the array
|
||||
// No need to add duplicates for all four overloads.
|
||||
interface IResourceArrayMethod<T> {
|
||||
(): IResourceArray<T>;
|
||||
(params: Object): IResourceArray<T>;
|
||||
(success: Function, error?: Function): IResourceArray<T>;
|
||||
(params: Object, success: Function, error?: Function): IResourceArray<T>;
|
||||
(params: Object, data: Object, success?: Function, error?: Function): IResourceArray<T>;
|
||||
}
|
||||
|
||||
// Baseclass for everyresource with default actions.
|
||||
// If you define your new actions for the resource, you will need
|
||||
// to extend this interface and typecast the ResourceClass to it.
|
||||
//
|
||||
// In case of passing the first argument as anything but a function,
|
||||
// it's gonna be considered data if the action method is POST, PUT or
|
||||
// PATCH (in other words, methods with body). Otherwise, it's going
|
||||
// to be considered as parameters to the request.
|
||||
// https://github.com/angular/angular.js/blob/v1.2.0/src/ngResource/resource.js#L461-L465
|
||||
//
|
||||
// Only those methods with an HTTP body do have 'data' as first parameter:
|
||||
// https://github.com/angular/angular.js/blob/v1.2.0/src/ngResource/resource.js#L463
|
||||
// More specifically, those methods are POST, PUT and PATCH:
|
||||
// https://github.com/angular/angular.js/blob/v1.2.0/src/ngResource/resource.js#L432
|
||||
//
|
||||
// Also, static calls always return the IResource (or IResourceArray) retrieved
|
||||
// https://github.com/angular/angular.js/blob/v1.2.0/src/ngResource/resource.js#L538-L549
|
||||
interface IResourceClass<T> {
|
||||
new(dataOrParams? : any) : T;
|
||||
get: IResourceMethod<T>;
|
||||
|
||||
query: IResourceArrayMethod<T>;
|
||||
|
||||
save: IResourceMethod<T>;
|
||||
|
||||
remove: IResourceMethod<T>;
|
||||
|
||||
delete: IResourceMethod<T>;
|
||||
}
|
||||
|
||||
// Instance calls always return the the promise of the request which retrieved the object
|
||||
// https://github.com/angular/angular.js/blob/v1.2.0/src/ngResource/resource.js#L538-L546
|
||||
interface IResource<T> {
|
||||
$get(): angular.IPromise<T>;
|
||||
$get(params?: Object, success?: Function, error?: Function): angular.IPromise<T>;
|
||||
$get(success: Function, error?: Function): angular.IPromise<T>;
|
||||
|
||||
$query(): angular.IPromise<IResourceArray<T>>;
|
||||
$query(params?: Object, success?: Function, error?: Function): angular.IPromise<IResourceArray<T>>;
|
||||
$query(success: Function, error?: Function): angular.IPromise<IResourceArray<T>>;
|
||||
|
||||
$save(): angular.IPromise<T>;
|
||||
$save(params?: Object, success?: Function, error?: Function): angular.IPromise<T>;
|
||||
$save(success: Function, error?: Function): angular.IPromise<T>;
|
||||
|
||||
$remove(): angular.IPromise<T>;
|
||||
$remove(params?: Object, success?: Function, error?: Function): angular.IPromise<T>;
|
||||
$remove(success: Function, error?: Function): angular.IPromise<T>;
|
||||
|
||||
$delete(): angular.IPromise<T>;
|
||||
$delete(params?: Object, success?: Function, error?: Function): angular.IPromise<T>;
|
||||
$delete(success: Function, error?: Function): angular.IPromise<T>;
|
||||
|
||||
$cancelRequest(): void;
|
||||
|
||||
/** the promise of the original server interaction that created this instance. **/
|
||||
$promise : angular.IPromise<T>;
|
||||
$resolved : boolean;
|
||||
toJSON(): T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Really just a regular Array object with $promise and $resolve attached to it
|
||||
*/
|
||||
interface IResourceArray<T> extends Array<T & IResource<T>> {
|
||||
/** the promise of the original server interaction that created this collection. **/
|
||||
$promise : angular.IPromise<IResourceArray<T>>;
|
||||
$resolved : boolean;
|
||||
}
|
||||
|
||||
/** when creating a resource factory via IModule.factory */
|
||||
interface IResourceServiceFactoryFunction<T> {
|
||||
($resource: angular.resource.IResourceService): IResourceClass<T>;
|
||||
<U extends IResourceClass<T>>($resource: angular.resource.IResourceService): U;
|
||||
}
|
||||
|
||||
// IResourceServiceProvider used to configure global settings
|
||||
interface IResourceServiceProvider extends angular.IServiceProvider {
|
||||
|
||||
defaults: IResourceOptions;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** extensions to base ng based on using angular-resource */
|
||||
declare namespace angular {
|
||||
|
||||
interface IModule {
|
||||
/** creating a resource service factory */
|
||||
factory(name: string, resourceServiceFactoryFunction: angular.resource.IResourceServiceFactoryFunction<any>): IModule;
|
||||
}
|
||||
}
|
||||
|
||||
interface Array<T>
|
||||
{
|
||||
/** the promise of the original server interaction that created this collection. **/
|
||||
$promise : angular.IPromise<Array<T>>;
|
||||
$resolved : boolean;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"resolution": "main",
|
||||
"tree": {
|
||||
"src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/e684481e0cd360db62fd6213ca7248245315e8a2/angularjs/angular-resource.d.ts",
|
||||
"raw": "registry:dt/angular-resource#1.5.0+20160412142209",
|
||||
"typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/e684481e0cd360db62fd6213ca7248245315e8a2/angularjs/angular-resource.d.ts"
|
||||
}
|
||||
}
|
154
public/docs/_examples/upgrade-phonecat-1-typescript/ts/typings-ng1/globals/angular-route/index.d.ts
vendored
Normal file
|
@ -0,0 +1,154 @@
|
|||
// Generated by typings
|
||||
// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/7de6c3dd94feaeb21f20054b9f30d5dabc5efabd/angularjs/angular-route.d.ts
|
||||
declare module "angular-route" {
|
||||
var _: string;
|
||||
export = _;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// ngRoute module (angular-route.js)
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
declare namespace angular.route {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// RouteParamsService
|
||||
// see http://docs.angularjs.org/api/ngRoute.$routeParams
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
interface IRouteParamsService {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// RouteService
|
||||
// see http://docs.angularjs.org/api/ngRoute.$route
|
||||
// see http://docs.angularjs.org/api/ngRoute.$routeProvider
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
interface IRouteService {
|
||||
reload(): void;
|
||||
routes: any;
|
||||
|
||||
// May not always be available. For instance, current will not be available
|
||||
// to a controller that was not initialized as a result of a route maching.
|
||||
current?: ICurrentRoute;
|
||||
|
||||
/**
|
||||
* Causes $route service to update the current URL, replacing current route parameters with those specified in newParams.
|
||||
* Provided property names that match the route's path segment definitions will be interpolated into the
|
||||
* location's path, while remaining properties will be treated as query params.
|
||||
*
|
||||
* @param newParams Object.<string, string> mapping of URL parameter names to values
|
||||
*/
|
||||
updateParams(newParams:{[key:string]:string}): void;
|
||||
|
||||
}
|
||||
|
||||
type InlineAnnotatedFunction = Function|Array<string|Function>
|
||||
|
||||
/**
|
||||
* see http://docs.angularjs.org/api/ngRoute/provider/$routeProvider#when for API documentation
|
||||
*/
|
||||
interface IRoute {
|
||||
/**
|
||||
* {(string|function()=}
|
||||
* Controller fn that should be associated with newly created scope or the name of a registered controller if passed as a string.
|
||||
*/
|
||||
controller?: string|InlineAnnotatedFunction;
|
||||
/**
|
||||
* A controller alias name. If present the controller will be published to scope under the controllerAs name.
|
||||
*/
|
||||
controllerAs?: string;
|
||||
/**
|
||||
* Undocumented?
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* {string=|function()=}
|
||||
* Html template as a string or a function that returns an html template as a string which should be used by ngView or ngInclude directives. This property takes precedence over templateUrl.
|
||||
*
|
||||
* If template is a function, it will be called with the following parameters:
|
||||
*
|
||||
* {Array.<Object>} - route parameters extracted from the current $location.path() by applying the current route
|
||||
*/
|
||||
template?: string|{($routeParams?: angular.route.IRouteParamsService) : string;}
|
||||
/**
|
||||
* {string=|function()=}
|
||||
* Path or function that returns a path to an html template that should be used by ngView.
|
||||
*
|
||||
* If templateUrl is a function, it will be called with the following parameters:
|
||||
*
|
||||
* {Array.<Object>} - route parameters extracted from the current $location.path() by applying the current route
|
||||
*/
|
||||
templateUrl?: string|{ ($routeParams?: angular.route.IRouteParamsService): string; }
|
||||
/**
|
||||
* {Object.<string, function>=} - An optional map of dependencies which should be injected into the controller. If any of these dependencies are promises, the router will wait for them all to be resolved or one to be rejected before the controller is instantiated. If all the promises are resolved successfully, the values of the resolved promises are injected and $routeChangeSuccess event is fired. If any of the promises are rejected the $routeChangeError event is fired. The map object is:
|
||||
*
|
||||
* - key - {string}: a name of a dependency to be injected into the controller.
|
||||
* - factory - {string|function}: If string then it is an alias for a service. Otherwise if function, then it is injected and the return value is treated as the dependency. If the result is a promise, it is resolved before its value is injected into the controller. Be aware that ngRoute.$routeParams will still refer to the previous route within these resolve functions. Use $route.current.params to access the new route parameters, instead.
|
||||
*/
|
||||
resolve?: {[key: string]: any};
|
||||
/**
|
||||
* {(string|function())=}
|
||||
* Value to update $location path with and trigger route redirection.
|
||||
*
|
||||
* If redirectTo is a function, it will be called with the following parameters:
|
||||
*
|
||||
* - {Object.<string>} - route parameters extracted from the current $location.path() by applying the current route templateUrl.
|
||||
* - {string} - current $location.path()
|
||||
* - {Object} - current $location.search()
|
||||
* - The custom redirectTo function is expected to return a string which will be used to update $location.path() and $location.search().
|
||||
*/
|
||||
redirectTo?: string|{($routeParams?: angular.route.IRouteParamsService, $locationPath?: string, $locationSearch?: any) : string};
|
||||
/**
|
||||
* Reload route when only $location.search() or $location.hash() changes.
|
||||
*
|
||||
* This option defaults to true. If the option is set to false and url in the browser changes, then $routeUpdate event is broadcasted on the root scope.
|
||||
*/
|
||||
reloadOnSearch?: boolean;
|
||||
/**
|
||||
* Match routes without being case sensitive
|
||||
*
|
||||
* This option defaults to false. If the option is set to true, then the particular route can be matched without being case sensitive
|
||||
*/
|
||||
caseInsensitiveMatch?: boolean;
|
||||
}
|
||||
|
||||
// see http://docs.angularjs.org/api/ng.$route#current
|
||||
interface ICurrentRoute extends IRoute {
|
||||
locals: {
|
||||
[index: string]: any;
|
||||
$scope: IScope;
|
||||
$template: string;
|
||||
};
|
||||
|
||||
params: any;
|
||||
}
|
||||
|
||||
interface IRouteProvider extends IServiceProvider {
|
||||
/**
|
||||
* Match routes without being case sensitive
|
||||
*
|
||||
* This option defaults to false. If the option is set to true, then the particular route can be matched without being case sensitive
|
||||
*/
|
||||
caseInsensitiveMatch?: boolean;
|
||||
/**
|
||||
* Sets route definition that will be used on route change when no other route definition is matched.
|
||||
*
|
||||
* @params Mapping information to be assigned to $route.current.
|
||||
*/
|
||||
otherwise(params: IRoute): IRouteProvider;
|
||||
/**
|
||||
* Adds a new route definition to the $route service.
|
||||
*
|
||||
* @param path Route path (matched against $location.path). If $location.path contains redundant trailing slash or is missing one, the route will still match and the $location.path will be updated to add or drop the trailing slash to exactly match the route definition.
|
||||
*
|
||||
* - path can contain named groups starting with a colon: e.g. :name. All characters up to the next slash are matched and stored in $routeParams under the given name when the route matches.
|
||||
* - path can contain named groups starting with a colon and ending with a star: e.g.:name*. All characters are eagerly stored in $routeParams under the given name when the route matches.
|
||||
* - path can contain optional named groups with a question mark: e.g.:name?.
|
||||
*
|
||||
* For example, routes like /color/:color/largecode/:largecode*\/edit will match /color/brown/largecode/code/with/slashes/edit and extract: color: brown and largecode: code/with/slashes.
|
||||
*
|
||||
* @param route Mapping information to be assigned to $route.current on route match.
|
||||
*/
|
||||
when(path: string, route: IRoute): IRouteProvider;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"resolution": "main",
|
||||
"tree": {
|
||||
"src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/7de6c3dd94feaeb21f20054b9f30d5dabc5efabd/angularjs/angular-route.d.ts",
|
||||
"raw": "registry:dt/angular-route#1.3.0+20160317120654",
|
||||
"typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/7de6c3dd94feaeb21f20054b9f30d5dabc5efabd/angularjs/angular-route.d.ts"
|
||||
}
|
||||
}
|
1953
public/docs/_examples/upgrade-phonecat-1-typescript/ts/typings-ng1/globals/angular/index.d.ts
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"resolution": "main",
|
||||
"tree": {
|
||||
"src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/b76dd8a7f95b71696982befbb7589940d27e940b/angularjs/angular.d.ts",
|
||||
"raw": "registry:dt/angular#1.5.0+20160517064839",
|
||||
"typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/b76dd8a7f95b71696982befbb7589940d27e940b/angularjs/angular.d.ts"
|
||||
}
|
||||
}
|
3199
public/docs/_examples/upgrade-phonecat-1-typescript/ts/typings-ng1/globals/jquery/index.d.ts
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"resolution": "main",
|
||||
"tree": {
|
||||
"src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/4cdfbe96b666eec5e1defbb519a62abf04e96764/jquery/jquery.d.ts",
|
||||
"raw": "registry:dt/jquery#1.10.0+20160417213236",
|
||||
"typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/4cdfbe96b666eec5e1defbb519a62abf04e96764/jquery/jquery.d.ts"
|
||||
}
|
||||
}
|
6
public/docs/_examples/upgrade-phonecat-1-typescript/ts/typings-ng1/index.d.ts
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
/// <reference path="globals/angular-animate/index.d.ts" />
|
||||
/// <reference path="globals/angular-mocks/index.d.ts" />
|
||||
/// <reference path="globals/angular-resource/index.d.ts" />
|
||||
/// <reference path="globals/angular-route/index.d.ts" />
|
||||
/// <reference path="globals/angular/index.d.ts" />
|
||||
/// <reference path="globals/jquery/index.d.ts" />
|
|
@ -0,0 +1,37 @@
|
|||
This is the Angular Phonecat application adjusted to fit our boilerplate project
|
||||
structure.
|
||||
|
||||
The following changes from vanilla Phonecat are applied:
|
||||
|
||||
* Karma config for unit tests is in karma.conf.ng1.js because the boilerplate
|
||||
Karma config is not compatible with the way Angular 1 tests need to be run.
|
||||
The shell script run-unit-tests.sh can be used to run the unit tests.
|
||||
* There's a `package.ng1.json`, which is not used to run anything but only to
|
||||
show an example of changing the PhoneCat http-server root path.
|
||||
* Also for the Karma shim, there is a `karma-test-shim.1.js` file which isn't
|
||||
used but is shown in the test appendix.
|
||||
* Instead of using Bower, Angular 1 and its dependencies are fetched from a CDN
|
||||
in index.html and karma.conf.ng1.js.
|
||||
* E2E tests have been moved to the parent directory, where `run-e2e-tests` can
|
||||
discover and run them along with all the other examples.
|
||||
* Angular 1 typings (from DefinitelyTyped) are added to typings-ng1 so that
|
||||
TypeScript can recognize Angular 1 code. (typings.json comes from boilerplate
|
||||
so we can't add them there).
|
||||
* Most of the phone JSON and image data removed in the interest of keeping
|
||||
repo weight down. Keeping enough to retain testability of the app.
|
||||
|
||||
## Running the app
|
||||
|
||||
Start like any example
|
||||
|
||||
npm run start
|
||||
|
||||
## Running unit tests
|
||||
|
||||
./run-unit-tests.sh
|
||||
|
||||
## Running E2E tests
|
||||
|
||||
Like for any example (at the project root):
|
||||
|
||||
gulp run-e2e-tests --filter=phonecat-2
|
|
@ -0,0 +1,104 @@
|
|||
'use strict';
|
||||
|
||||
// Angular E2E Testing Guide:
|
||||
// https://docs.angularjs.org/guide/e2e-testing
|
||||
|
||||
describe('PhoneCat Application', function() {
|
||||
|
||||
beforeAll(function() {
|
||||
setProtractorToNg1Mode();
|
||||
});
|
||||
|
||||
it('should redirect `index.html` to `index.html#!/phones', function() {
|
||||
browser.get('index.html');
|
||||
expect(browser.getLocationAbsUrl()).toBe('/phones');
|
||||
});
|
||||
|
||||
describe('View: Phone list', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
browser.get('index.html#!/phones');
|
||||
});
|
||||
|
||||
it('should filter the phone list as a user types into the search box', function() {
|
||||
var phoneList = element.all(by.css('.phones li'));
|
||||
var query = element(by.css('input'));
|
||||
|
||||
expect(phoneList.count()).toBe(20);
|
||||
|
||||
sendKeys(query, 'nexus');
|
||||
expect(phoneList.count()).toBe(1);
|
||||
|
||||
query.clear();
|
||||
sendKeys(query, 'motorola');
|
||||
expect(phoneList.count()).toBe(8);
|
||||
});
|
||||
|
||||
it('should be possible to control phone order via the drop-down menu', function() {
|
||||
var queryField = element(by.css('input'));
|
||||
var orderSelect = element(by.css('select'));
|
||||
var nameOption = orderSelect.element(by.css('option[value="name"]'));
|
||||
var phoneNameColumn = element.all(by.css('.phones .name'));
|
||||
|
||||
function getNames() {
|
||||
return phoneNameColumn.map(function(elem) {
|
||||
return elem.getText();
|
||||
});
|
||||
}
|
||||
|
||||
sendKeys(queryField, 'tablet'); // Let's narrow the dataset to make the assertions shorter
|
||||
|
||||
expect(getNames()).toEqual([
|
||||
'Motorola XOOM\u2122 with Wi-Fi',
|
||||
'MOTOROLA XOOM\u2122'
|
||||
]);
|
||||
|
||||
nameOption.click();
|
||||
|
||||
expect(getNames()).toEqual([
|
||||
'MOTOROLA XOOM\u2122',
|
||||
'Motorola XOOM\u2122 with Wi-Fi'
|
||||
]);
|
||||
});
|
||||
|
||||
it('should render phone specific links', function() {
|
||||
var query = element(by.css('input'));
|
||||
sendKeys(query, 'nexus');
|
||||
|
||||
element.all(by.css('.phones li a')).first().click();
|
||||
browser.refresh(); // Not sure why this is needed but it is. The route change works fine.
|
||||
expect(browser.getLocationAbsUrl()).toBe('/phones/nexus-s');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('View: Phone detail', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
browser.get('index.html#!/phones/nexus-s');
|
||||
});
|
||||
|
||||
it('should display the `nexus-s` page', function() {
|
||||
expect(element(by.css('h1')).getText()).toBe('Nexus S');
|
||||
});
|
||||
|
||||
it('should display the first phone image as the main phone image', function() {
|
||||
var mainImage = element(by.css('img.phone.selected'));
|
||||
|
||||
expect(mainImage.getAttribute('src')).toMatch(/img\/phones\/nexus-s.0.jpg/);
|
||||
});
|
||||
|
||||
it('should swap the main image when clicking on a thumbnail image', function() {
|
||||
var mainImage = element(by.css('img.phone.selected'));
|
||||
var thumbnails = element.all(by.css('.phone-thumbs img'));
|
||||
|
||||
thumbnails.get(2).click();
|
||||
expect(mainImage.getAttribute('src')).toMatch(/img\/phones\/nexus-s.2.jpg/);
|
||||
|
||||
thumbnails.get(0).click();
|
||||
expect(mainImage.getAttribute('src')).toMatch(/img\/phones\/nexus-s.0.jpg/);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,67 @@
|
|||
/* Animate `ngRepeat` in `phoneList` component */
|
||||
.phone-list-item.ng-enter,
|
||||
.phone-list-item.ng-leave,
|
||||
.phone-list-item.ng-move {
|
||||
overflow: hidden;
|
||||
transition: 0.5s linear all;
|
||||
}
|
||||
|
||||
.phone-list-item.ng-enter,
|
||||
.phone-list-item.ng-leave.ng-leave-active,
|
||||
.phone-list-item.ng-move {
|
||||
height: 0;
|
||||
margin-bottom: 0;
|
||||
opacity: 0;
|
||||
padding-bottom: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.phone-list-item.ng-enter.ng-enter-active,
|
||||
.phone-list-item.ng-leave,
|
||||
.phone-list-item.ng-move.ng-move-active {
|
||||
height: 120px;
|
||||
margin-bottom: 20px;
|
||||
opacity: 1;
|
||||
padding-bottom: 4px;
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
/* Animate view transitions with `ngView` */
|
||||
.view-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.view-frame {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.view-frame.ng-enter,
|
||||
.view-frame.ng-leave {
|
||||
background: white;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.view-frame.ng-enter {
|
||||
animation: 1s fade-in;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.view-frame.ng-leave {
|
||||
animation: 1s fade-out;
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
@keyframes fade-in {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes fade-out {
|
||||
from { opacity: 1; }
|
||||
to { opacity: 0; }
|
||||
}
|
||||
|
||||
/* Older browsers might need vendor-prefixes for keyframes and animation! */
|
|
@ -0,0 +1,43 @@
|
|||
'use strict';
|
||||
|
||||
angular.
|
||||
module('phonecatApp').
|
||||
animation('.phone', function phoneAnimationFactory() {
|
||||
return {
|
||||
addClass: animateIn,
|
||||
removeClass: animateOut
|
||||
};
|
||||
|
||||
function animateIn(element: JQuery, className: string, done: () => void) {
|
||||
if (className !== 'selected') return;
|
||||
|
||||
element.css({
|
||||
display: 'block',
|
||||
position: 'absolute',
|
||||
top: 500,
|
||||
left: 0
|
||||
}).animate({
|
||||
top: 0
|
||||
}, done);
|
||||
|
||||
return function animateInEnd(wasCanceled: boolean) {
|
||||
if (wasCanceled) element.stop();
|
||||
};
|
||||
}
|
||||
|
||||
function animateOut(element: JQuery, className: string, done: () => void) {
|
||||
if (className !== 'selected') return;
|
||||
|
||||
element.css({
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0
|
||||
}).animate({
|
||||
top: -500
|
||||
}, done);
|
||||
|
||||
return function animateOutEnd(wasCanceled: boolean) {
|
||||
if (wasCanceled) element.stop();
|
||||
};
|
||||
}
|
||||
});
|
|
@ -0,0 +1,19 @@
|
|||
'use strict';
|
||||
|
||||
angular.
|
||||
module('phonecatApp').
|
||||
config(['$locationProvider' ,'$routeProvider',
|
||||
function config($locationProvider:angular.ILocationProvider,
|
||||
$routeProvider: angular.route.IRouteProvider) {
|
||||
$locationProvider.hashPrefix('!');
|
||||
|
||||
$routeProvider.
|
||||
when('/phones', {
|
||||
template: '<phone-list></phone-list>'
|
||||
}).
|
||||
when('/phones/:phoneId', {
|
||||
template: '<phone-detail></phone-detail>'
|
||||
}).
|
||||
otherwise('/phones');
|
||||
}
|
||||
]);
|
|
@ -1,99 +1,93 @@
|
|||
/* app css stylesheet */
|
||||
|
||||
body {
|
||||
padding-top: 20px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
|
||||
.phone-images {
|
||||
background-color: white;
|
||||
width: 450px;
|
||||
height: 450px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
float: left;
|
||||
h1 {
|
||||
border-bottom: 1px solid gray;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* View: Phone list */
|
||||
.phones {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.thumb {
|
||||
float: left;
|
||||
margin: -0.5em 1em 1.5em 0;
|
||||
padding-bottom: 1em;
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.phones li {
|
||||
clear: both;
|
||||
height: 115px;
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
/** Detail View **/
|
||||
img.phone {
|
||||
.thumb {
|
||||
float: left;
|
||||
margin-right: 3em;
|
||||
margin-bottom: 2em;
|
||||
background-color: white;
|
||||
padding: 2em;
|
||||
height: 400px;
|
||||
width: 400px;
|
||||
display: none;
|
||||
height: 100px;
|
||||
margin: -0.5em 1em 1.5em 0;
|
||||
padding-bottom: 1em;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
img.phone:first-child {
|
||||
/* View: Phone detail */
|
||||
.phone {
|
||||
background-color: white;
|
||||
display: none;
|
||||
float: left;
|
||||
height: 400px;
|
||||
margin-bottom: 2em;
|
||||
margin-right: 3em;
|
||||
padding: 2em;
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.phone:first-child {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
ul.phone-thumbs {
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
.phone-images {
|
||||
background-color: white;
|
||||
float: left;
|
||||
height: 450px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 450px;
|
||||
}
|
||||
|
||||
ul.phone-thumbs li {
|
||||
.phone-thumbs {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.phone-thumbs img {
|
||||
height: 100px;
|
||||
padding: 1em;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.phone-thumbs li {
|
||||
background-color: white;
|
||||
border: 1px solid black;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
margin: 1em;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
ul.phone-thumbs img {
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
ul.phone-thumbs img:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
ul.specs {
|
||||
.specs {
|
||||
clear: both;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
ul.specs > li{
|
||||
.specs dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.specs > li {
|
||||
display: inline-block;
|
||||
width: 200px;
|
||||
vertical-align: top;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
ul.specs > li > span{
|
||||
font-weight: bold;
|
||||
.specs > li > span {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
ul.specs dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h1 {
|
||||
border-bottom: 1px solid gray;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
'use strict';
|
||||
|
||||
// Define the `phonecatApp` module
|
||||
angular.module('phonecatApp', [
|
||||
'ngAnimate',
|
||||
'ngRoute',
|
||||
'core',
|
||||
'phoneDetail',
|
||||
'phoneList',
|
||||
]);
|
|
@ -0,0 +1,22 @@
|
|||
// #docregion
|
||||
import {
|
||||
describe,
|
||||
beforeEachProviders,
|
||||
it,
|
||||
inject,
|
||||
expect
|
||||
} from '@angular/core/testing';
|
||||
import { CheckmarkPipe } from './checkmark.pipe';
|
||||
|
||||
describe('CheckmarkPipe', function() {
|
||||
|
||||
beforeEachProviders(() => [CheckmarkPipe]);
|
||||
|
||||
it('should convert boolean values to unicode checkmark or cross',
|
||||
inject([CheckmarkPipe], function(checkmarkPipe: CheckmarkPipe) {
|
||||
expect(checkmarkPipe.transform(true)).toBe('\u2713');
|
||||
expect(checkmarkPipe.transform(false)).toBe('\u2718');
|
||||
})
|
||||
);
|
||||
|
||||
});
|
|
@ -0,0 +1,11 @@
|
|||
// #docregion
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
@Pipe({name: 'checkmark'})
|
||||
export class CheckmarkPipe implements PipeTransform {
|
||||
|
||||
transform(input: boolean) {
|
||||
return input ? '\u2713' : '\u2718';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
'use strict';
|
||||
|
||||
// Define the `core` module
|
||||
angular.module('core', ['core.phone']);
|
|
@ -0,0 +1,4 @@
|
|||
'use strict';
|
||||
|
||||
// Define the `core.phone` module
|
||||
angular.module('core.phone', ['ngResource']);
|
|
@ -0,0 +1,58 @@
|
|||
// #docregion
|
||||
import { provide } from '@angular/core';
|
||||
import {
|
||||
describe,
|
||||
beforeEach,
|
||||
beforeEachProviders,
|
||||
it,
|
||||
inject
|
||||
} from '@angular/core/testing';
|
||||
import {
|
||||
Http,
|
||||
BaseRequestOptions,
|
||||
ResponseOptions,
|
||||
Response
|
||||
} from '@angular/http';
|
||||
import { MockBackend, MockConnection } from '@angular/http/testing';
|
||||
import { Phone, PhoneData } from './phone.service';
|
||||
|
||||
describe('Phone', function() {
|
||||
let phone:Phone;
|
||||
let phonesData:PhoneData[] = [
|
||||
{name: 'Phone X', snippet: '', images: []},
|
||||
{name: 'Phone Y', snippet: '', images: []},
|
||||
{name: 'Phone Z', snippet: '', images: []}
|
||||
];
|
||||
let mockBackend:MockBackend;
|
||||
|
||||
beforeEachProviders(() => [
|
||||
Phone,
|
||||
MockBackend,
|
||||
BaseRequestOptions,
|
||||
provide(Http, {
|
||||
useFactory: (backend: MockBackend, options: BaseRequestOptions) =>
|
||||
new Http(backend, options),
|
||||
deps: [MockBackend, BaseRequestOptions]
|
||||
})
|
||||
]);
|
||||
|
||||
beforeEach(inject([MockBackend, Phone],
|
||||
(_mockBackend_:MockBackend, _phone_:Phone) => {
|
||||
mockBackend = _mockBackend_;
|
||||
phone = _phone_;
|
||||
}));
|
||||
|
||||
it('should fetch the phones data from `/phones/phones.json`',
|
||||
(done: () => void) => {
|
||||
mockBackend.connections.subscribe((conn: MockConnection) => {
|
||||
conn.mockRespond(new Response(new ResponseOptions({
|
||||
body: JSON.stringify(phonesData)
|
||||
})));
|
||||
});
|
||||
phone.query().subscribe(result => {
|
||||
expect(result).toEqual(phonesData);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
|
@ -1,38 +1,33 @@
|
|||
// #docregion full
|
||||
// #docregion
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Http, Response } from '@angular/http';
|
||||
import { Observable } from 'rxjs/Rx';
|
||||
|
||||
import 'rxjs/add/operator/map';
|
||||
|
||||
// #docregion phone-interface
|
||||
export interface Phone {
|
||||
// #docregion phonedata-interface
|
||||
export interface PhoneData {
|
||||
name: string;
|
||||
snippet: string;
|
||||
images: string[];
|
||||
}
|
||||
// #enddocregion phone-interface
|
||||
// #enddocregion phonedata-interface
|
||||
|
||||
// #docregion fullclass
|
||||
// #docregion class
|
||||
// #docregion classdef
|
||||
@Injectable()
|
||||
export class Phones {
|
||||
// #enddocregion class
|
||||
|
||||
export class Phone {
|
||||
// #enddocregion classdef
|
||||
constructor(private http: Http) { }
|
||||
|
||||
query():Observable<Phone[]> {
|
||||
query(): Observable<PhoneData[]> {
|
||||
return this.http.get(`phones/phones.json`)
|
||||
.map((res:Response) => res.json());
|
||||
.map((res: Response) => res.json());
|
||||
}
|
||||
|
||||
get(id: string):Observable<Phone> {
|
||||
get(id: string): Observable<PhoneData> {
|
||||
return this.http.get(`phones/${id}.json`)
|
||||
.map((res:Response) => res.json());
|
||||
.map((res: Response) => res.json());
|
||||
}
|
||||
|
||||
// #docregion class
|
||||
// #docregion classdef
|
||||
}
|
||||
// #enddocregion class
|
||||
// #enddocregion classdef
|
||||
// #enddocregion fullclass
|
||||
// #docregion full
|
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 14 KiB |