├── docs
├── CNAME
├── favicon.ico
├── index.html
└── dist
│ ├── vendor-manifest.json
│ └── 89889688147bd7575d6327160d64e760.svg
├── wwwroot
├── favicon.ico
└── index.html
├── README.md
├── ClientApp
├── app
│ ├── components
│ │ ├── app
│ │ │ ├── app.component.css
│ │ │ ├── app.component.ts
│ │ │ └── app.component.html
│ │ ├── navmenu
│ │ │ ├── navmenu.component.ts
│ │ │ ├── navmenu.component.css
│ │ │ └── navmenu.component.html
│ │ └── home
│ │ │ ├── home.component.html
│ │ │ ├── home.component.ts
│ │ │ └── home.component.spec.ts
│ ├── app.module.server.ts
│ ├── app.module.browser.ts
│ └── app.module.shared.ts
├── test
│ ├── karma.conf.js
│ └── boot-tests.ts
├── boot.browser.ts
└── boot.server.ts
├── tsconfig.json
├── package.json
├── webpack.config.js
├── webpack.config.vendor.js
└── .gitignore
/docs/CNAME:
--------------------------------------------------------------------------------
1 | decimalnow.com
--------------------------------------------------------------------------------
/docs/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RaphHaddad/DecimalNow/HEAD/docs/favicon.ico
--------------------------------------------------------------------------------
/wwwroot/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RaphHaddad/DecimalNow/HEAD/wwwroot/favicon.ico
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Decimal now
2 | Uses decimal time
3 |
4 | deployed [here](http://decimalnow.com)
5 |
6 | the meat of the code is in `home.component.ts` and `home.component.spec.ts`
--------------------------------------------------------------------------------
/ClientApp/app/components/app/app.component.css:
--------------------------------------------------------------------------------
1 | @media (max-width: 767px) {
2 | /* On small screens, the nav menu spans the full width of the screen. Leave a space for it. */
3 | .body-content {
4 | padding-top: 50px;
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/ClientApp/app/components/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app',
5 | templateUrl: './app.component.html',
6 | styleUrls: ['./app.component.css']
7 | })
8 | export class AppComponent {
9 | }
10 |
--------------------------------------------------------------------------------
/ClientApp/app/components/navmenu/navmenu.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'nav-menu',
5 | templateUrl: './navmenu.component.html',
6 | styleUrls: ['./navmenu.component.css']
7 | })
8 | export class NavMenuComponent {
9 | }
10 |
--------------------------------------------------------------------------------
/ClientApp/app/app.module.server.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { ServerModule } from '@angular/platform-server';
3 | import { AppModuleShared } from './app.module.shared';
4 | import { AppComponent } from './components/app/app.component';
5 |
6 | @NgModule({
7 | bootstrap: [ AppComponent ],
8 | imports: [
9 | ServerModule,
10 | AppModuleShared
11 | ]
12 | })
13 | export class AppModule {
14 | }
15 |
--------------------------------------------------------------------------------
/ClientApp/app/components/app/app.component.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "es2015",
4 | "moduleResolution": "node",
5 | "target": "es5",
6 | "sourceMap": true,
7 | "experimentalDecorators": true,
8 | "emitDecoratorMetadata": true,
9 | "skipDefaultLibCheck": true,
10 | "skipLibCheck": true, // Workaround for https://github.com/angular/angular/issues/17863. Remove this if you upgrade to a fixed version of Angular.
11 | "strict": true,
12 | "lib": [ "es6", "dom" ],
13 | "types": [ "webpack-env" ]
14 | },
15 | "exclude": [ "bin", "node_modules" ],
16 | "atom": { "rewriteTsconfig": false }
17 | }
18 |
--------------------------------------------------------------------------------
/wwwroot/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/ClientApp/app/app.module.browser.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { BrowserModule } from '@angular/platform-browser';
3 | import { AppModuleShared } from './app.module.shared';
4 | import { AppComponent } from './components/app/app.component';
5 |
6 | @NgModule({
7 | bootstrap: [ AppComponent ],
8 | imports: [
9 | BrowserModule,
10 | AppModuleShared
11 | ],
12 | providers: [
13 | { provide: 'BASE_URL', useFactory: getBaseUrl }
14 | ]
15 | })
16 | export class AppModule {
17 | }
18 |
19 | export function getBaseUrl() {
20 | return document.getElementsByTagName('base')[0].href;
21 | }
22 |
--------------------------------------------------------------------------------
/ClientApp/app/app.module.shared.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { FormsModule } from '@angular/forms';
4 | import { HttpModule } from '@angular/http';
5 | import { RouterModule } from '@angular/router';
6 |
7 | import { AppComponent } from './components/app/app.component';
8 | import { NavMenuComponent } from './components/navmenu/navmenu.component';
9 | import { HomeComponent } from './components/home/home.component';
10 |
11 | @NgModule({
12 | declarations: [
13 | AppComponent,
14 | NavMenuComponent,
15 | HomeComponent
16 | ],
17 | imports: [
18 | CommonModule,
19 | HttpModule,
20 | FormsModule,
21 | RouterModule.forRoot([
22 | { path: '', redirectTo: 'home', pathMatch: 'full' },
23 | { path: 'home', component: HomeComponent },
24 | { path: '**', redirectTo: 'home' }
25 | ])
26 | ]
27 | })
28 | export class AppModuleShared {
29 | }
30 |
--------------------------------------------------------------------------------
/ClientApp/test/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/0.13/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '.',
7 | frameworks: ['jasmine'],
8 | files: [
9 | '../../wwwroot/dist/vendor.js',
10 | './boot-tests.ts'
11 | ],
12 | preprocessors: {
13 | './boot-tests.ts': ['webpack']
14 | },
15 | reporters: ['progress'],
16 | port: 9876,
17 | colors: true,
18 | logLevel: config.LOG_INFO,
19 | autoWatch: true,
20 | browsers: ['Chrome'],
21 | mime: { 'application/javascript': ['ts','tsx'] },
22 | singleRun: false,
23 | webpack: require('../../webpack.config.js')().filter(config => config.target !== 'node'), // Test against client bundle, because tests run in a browser
24 | webpackMiddleware: { stats: 'errors-only' }
25 | });
26 | };
27 |
--------------------------------------------------------------------------------
/ClientApp/boot.browser.ts:
--------------------------------------------------------------------------------
1 | import 'babel-polyfill'
2 | import 'reflect-metadata';
3 | import 'zone.js';
4 | import 'bootstrap';
5 | import { enableProdMode } from '@angular/core';
6 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
7 | import { AppModule } from './app/app.module.browser';
8 |
9 | if (module.hot) {
10 | module.hot.accept();
11 | module.hot.dispose(() => {
12 | // Before restarting the app, we create a new root element and dispose the old one
13 | const oldRootElem = document.querySelector('app');
14 | const newRootElem = document.createElement('app');
15 | oldRootElem!.parentNode!.insertBefore(newRootElem, oldRootElem);
16 | modulePromise.then(appModule => appModule.destroy());
17 | });
18 | } else {
19 | enableProdMode();
20 | }
21 |
22 | // Note: @ng-tools/webpack looks for the following expression when performing production
23 | // builds. Don't change how this line looks, otherwise you may break tree-shaking.
24 | const modulePromise = platformBrowserDynamic().bootstrapModule(AppModule);
25 |
--------------------------------------------------------------------------------
/ClientApp/test/boot-tests.ts:
--------------------------------------------------------------------------------
1 | // Load required polyfills and testing libraries
2 | import 'reflect-metadata';
3 | import 'zone.js';
4 | import 'zone.js/dist/long-stack-trace-zone';
5 | import 'zone.js/dist/proxy.js';
6 | import 'zone.js/dist/sync-test';
7 | import 'zone.js/dist/jasmine-patch';
8 | import 'zone.js/dist/async-test';
9 | import 'zone.js/dist/fake-async-test';
10 | import * as testing from '@angular/core/testing';
11 | import * as testingBrowser from '@angular/platform-browser-dynamic/testing';
12 |
13 | // There's no typing for the `__karma__` variable. Just declare it as any
14 | declare var __karma__: any;
15 | declare var require: any;
16 |
17 | // Prevent Karma from running prematurely
18 | __karma__.loaded = function () {};
19 |
20 | // First, initialize the Angular testing environment
21 | testing.getTestBed().initTestEnvironment(
22 | testingBrowser.BrowserDynamicTestingModule,
23 | testingBrowser.platformBrowserDynamicTesting()
24 | );
25 |
26 | // Then we find all the tests
27 | const context = require.context('../', true, /\.spec\.ts$/);
28 |
29 | // And load the modules
30 | context.keys().map(context);
31 |
32 | // Finally, start Karma to run the tests
33 | __karma__.start();
34 |
--------------------------------------------------------------------------------
/docs/dist/vendor-manifest.json:
--------------------------------------------------------------------------------
1 | {"name":"vendor_0d8620b209dffc725fd8","content":{"./node_modules/jquery/dist/jquery.js":{"id":0,"meta":{}},"./node_modules/process/browser.js":{"id":1,"meta":{}},"./node_modules/webpack/buildin/global.js":{"id":2,"meta":{}},"./node_modules/bootstrap/dist/js/npm.js":{"id":3,"meta":{}},"./node_modules/es6-promise/dist/es6-promise.js":{"id":4,"meta":{}},"./node_modules/es6-shim/es6-shim.js":{"id":5,"meta":{}},"./node_modules/event-source-polyfill/eventsource.js":{"id":6,"meta":{}},"./node_modules/bootstrap/dist/css/bootstrap.css":{"id":7,"meta":{}},"./node_modules/bootstrap/js/affix.js":{"id":8,"meta":{}},"./node_modules/bootstrap/js/alert.js":{"id":9,"meta":{}},"./node_modules/bootstrap/js/button.js":{"id":10,"meta":{}},"./node_modules/bootstrap/js/carousel.js":{"id":11,"meta":{}},"./node_modules/bootstrap/js/collapse.js":{"id":12,"meta":{}},"./node_modules/bootstrap/js/dropdown.js":{"id":13,"meta":{}},"./node_modules/bootstrap/js/modal.js":{"id":14,"meta":{}},"./node_modules/bootstrap/js/popover.js":{"id":15,"meta":{}},"./node_modules/bootstrap/js/scrollspy.js":{"id":16,"meta":{}},"./node_modules/bootstrap/js/tab.js":{"id":17,"meta":{}},"./node_modules/bootstrap/js/tooltip.js":{"id":18,"meta":{}},"./node_modules/bootstrap/js/transition.js":{"id":19,"meta":{}}}}
--------------------------------------------------------------------------------
/ClientApp/app/components/home/home.component.html:
--------------------------------------------------------------------------------
1 | Decimal time NOW
2 | For those of us who want decimal time
3 | 10 hours in a day, each hour has 100 minutes, each minute has 100 seconds
4 | Decimal Time Now:
5 | {{ decimalNow }}
6 |
32 |
--------------------------------------------------------------------------------
/ClientApp/app/components/navmenu/navmenu.component.css:
--------------------------------------------------------------------------------
1 | li .glyphicon {
2 | margin-right: 10px;
3 | }
4 |
5 | /* Highlighting rules for nav menu items */
6 | li.link-active a,
7 | li.link-active a:hover,
8 | li.link-active a:focus {
9 | background-color: #4189C7;
10 | color: white;
11 | }
12 |
13 | /* Keep the nav menu independent of scrolling and on top of other items */
14 | .main-nav {
15 | position: fixed;
16 | top: 0;
17 | left: 0;
18 | right: 0;
19 | z-index: 1;
20 | }
21 |
22 | @media (min-width: 768px) {
23 | /* On small screens, convert the nav menu to a vertical sidebar */
24 | .main-nav {
25 | height: 100%;
26 | width: calc(25% - 20px);
27 | }
28 | .navbar {
29 | border-radius: 0px;
30 | border-width: 0px;
31 | height: 100%;
32 | }
33 | .navbar-header {
34 | float: none;
35 | }
36 | .navbar-collapse {
37 | border-top: 1px solid #444;
38 | padding: 0px;
39 | }
40 | .navbar ul {
41 | float: none;
42 | }
43 | .navbar li {
44 | float: none;
45 | font-size: 15px;
46 | margin: 6px;
47 | }
48 | .navbar li a {
49 | padding: 10px 16px;
50 | border-radius: 4px;
51 | }
52 | .navbar a {
53 | /* If a menu item's text is too long, truncate it */
54 | width: 100%;
55 | white-space: nowrap;
56 | overflow: hidden;
57 | text-overflow: ellipsis;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/ClientApp/app/components/navmenu/navmenu.component.html:
--------------------------------------------------------------------------------
1 |
34 |
--------------------------------------------------------------------------------
/ClientApp/boot.server.ts:
--------------------------------------------------------------------------------
1 | import 'reflect-metadata';
2 | import 'zone.js';
3 | import 'rxjs/add/operator/first';
4 | import { APP_BASE_HREF } from '@angular/common';
5 | import { enableProdMode, ApplicationRef, NgZone, ValueProvider } from '@angular/core';
6 | import { platformDynamicServer, PlatformState, INITIAL_CONFIG } from '@angular/platform-server';
7 | import { createServerRenderer, RenderResult } from 'aspnet-prerendering';
8 | import { AppModule } from './app/app.module.server';
9 |
10 | enableProdMode();
11 |
12 | export default createServerRenderer(params => {
13 | const providers = [
14 | { provide: INITIAL_CONFIG, useValue: { document: '', url: params.url } },
15 | { provide: APP_BASE_HREF, useValue: params.baseUrl },
16 | { provide: 'BASE_URL', useValue: params.origin + params.baseUrl },
17 | ];
18 |
19 | return platformDynamicServer(providers).bootstrapModule(AppModule).then(moduleRef => {
20 | const appRef: ApplicationRef = moduleRef.injector.get(ApplicationRef);
21 | const state = moduleRef.injector.get(PlatformState);
22 | const zone = moduleRef.injector.get(NgZone);
23 |
24 | return new Promise((resolve, reject) => {
25 | zone.onError.subscribe((errorInfo: any) => reject(errorInfo));
26 | appRef.isStable.first(isStable => isStable).subscribe(() => {
27 | // Because 'onStable' fires before 'onError', we have to delay slightly before
28 | // completing the request in case there's an error to report
29 | setImmediate(() => {
30 | resolve({
31 | html: state.renderToString()
32 | });
33 | moduleRef.destroy();
34 | });
35 | });
36 | });
37 | });
38 | });
39 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Raph_DecimalNow",
3 | "private": true,
4 | "version": "0.0.0",
5 | "scripts": {
6 | "test": "karma start ClientApp/test/karma.conf.js",
7 | "start": "node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js && node node_modules/webpack/bin/webpack.js && node ./node_modules/http-server/bin/http-server ./wwwroot",
8 | "publish": "npm install && node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js --env.prod && node node_modules/webpack/bin/webpack.js --env.prod"
9 | },
10 | "dependencies": {
11 | "@angular/animations": "4.2.5",
12 | "@angular/common": "4.2.5",
13 | "@angular/compiler": "4.2.5",
14 | "@angular/compiler-cli": "4.2.5",
15 | "@angular/core": "4.2.5",
16 | "@angular/forms": "4.2.5",
17 | "@angular/http": "4.2.5",
18 | "@angular/platform-browser": "4.2.5",
19 | "@angular/platform-browser-dynamic": "4.2.5",
20 | "@angular/platform-server": "4.2.5",
21 | "@angular/router": "4.2.5",
22 | "@ngtools/webpack": "1.5.0",
23 | "@types/webpack-env": "1.13.0",
24 | "angular2-template-loader": "0.6.2",
25 | "aspnet-prerendering": "^3.0.1",
26 | "aspnet-webpack": "^2.0.1",
27 | "awesome-typescript-loader": "3.2.1",
28 | "babel-polyfill": "^6.26.0",
29 | "bootstrap": "3.3.7",
30 | "css": "^2.2.3",
31 | "css-loader": "0.28.4",
32 | "es6-shim": "0.35.3",
33 | "event-source-polyfill": "0.0.9",
34 | "expose-loader": "0.7.3",
35 | "extract-text-webpack-plugin": "2.1.2",
36 | "file-loader": "0.11.2",
37 | "html-loader": "0.4.5",
38 | "http-server": "^0.11.1",
39 | "isomorphic-fetch": "2.2.1",
40 | "jquery": "3.2.1",
41 | "json-loader": "0.5.4",
42 | "preboot": "4.5.2",
43 | "raw-loader": "0.5.1",
44 | "reflect-metadata": "0.1.10",
45 | "rxjs": "5.4.2",
46 | "style-loader": "0.18.2",
47 | "to-string-loader": "1.1.5",
48 | "typescript": "2.4.1",
49 | "url-loader": "^1.0.1",
50 | "webpack": "2.5.1",
51 | "webpack-hot-middleware": "2.18.2",
52 | "webpack-merge": "4.1.0",
53 | "zone.js": "0.8.12"
54 | },
55 | "devDependencies": {
56 | "@types/chai": "4.0.1",
57 | "@types/jasmine": "2.5.53",
58 | "chai": "4.0.2",
59 | "jasmine-core": "2.6.4",
60 | "karma": "^2.0.5",
61 | "karma-chai": "0.1.0",
62 | "karma-chrome-launcher": "2.2.0",
63 | "karma-cli": "1.0.1",
64 | "karma-jasmine": "1.1.0",
65 | "karma-webpack": "^3.0.0"
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const merge = require('webpack-merge');
4 | const AotPlugin = require('@ngtools/webpack').AotPlugin;
5 | const CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin;
6 |
7 | module.exports = (env) => {
8 | // Configuration in common to both client-side and server-side bundles
9 | const isDevBuild = !(env && env.prod);
10 | const sharedConfig = {
11 | stats: { modules: false },
12 | context: __dirname,
13 | resolve: { extensions: [ '.js', '.ts' ] },
14 | output: {
15 | filename: '[name].js',
16 | publicPath: 'dist/' // Webpack dev middleware, if enabled, handles requests for this URL prefix
17 | },
18 | module: {
19 | rules: [
20 | { test: /\.ts$/, include: /ClientApp/, use: isDevBuild ? ['awesome-typescript-loader?silent=true', 'angular2-template-loader'] : '@ngtools/webpack' },
21 | { test: /\.html$/, use: 'html-loader?minimize=false' },
22 | { test: /\.css$/, use: [ 'to-string-loader', isDevBuild ? 'css-loader' : 'css-loader?minimize' ] },
23 | { test: /\.(png|jpg|jpeg|gif|svg)$/, use: 'url-loader?limit=25000' }
24 | ]
25 | },
26 | plugins: [new CheckerPlugin()]
27 | };
28 |
29 | // Configuration for client-side bundle suitable for running in browsers
30 | const clientBundleOutputDir = './wwwroot/dist';
31 | const clientBundleConfig = merge(sharedConfig, {
32 | entry: { 'main-client': './ClientApp/boot.browser.ts' },
33 | output: { path: path.join(__dirname, clientBundleOutputDir) },
34 | plugins: [
35 | new webpack.DllReferencePlugin({
36 | context: __dirname,
37 | manifest: require('./wwwroot/dist/vendor-manifest.json')
38 | })
39 | ].concat(isDevBuild ? [
40 | // Plugins that apply in development builds only
41 | new webpack.SourceMapDevToolPlugin({
42 | filename: '[file].map', // Remove this line if you prefer inline source maps
43 | moduleFilenameTemplate: path.relative(clientBundleOutputDir, '[resourcePath]') // Point sourcemap entries to the original file locations on disk
44 | })
45 | ] : [
46 | // Plugins that apply in production builds only
47 | new webpack.optimize.UglifyJsPlugin(),
48 | new AotPlugin({
49 | tsConfigPath: './tsconfig.json',
50 | entryModule: path.join(__dirname, 'ClientApp/app/app.module.browser#AppModule'),
51 | exclude: ['./**/*.server.ts']
52 | })
53 | ])
54 | });
55 |
56 | // Configuration for server-side (prerendering) bundle suitable for running in Node
57 | const serverBundleConfig = merge(sharedConfig, {
58 | resolve: { mainFields: ['main'] },
59 | entry: { 'main-server': './ClientApp/boot.server.ts' },
60 | plugins: [
61 | new webpack.DllReferencePlugin({
62 | context: __dirname,
63 | manifest: require('./ClientApp/dist/vendor-manifest.json'),
64 | sourceType: 'commonjs2',
65 | name: './vendor'
66 | })
67 | ].concat(isDevBuild ? [] : [
68 | // Plugins that apply in production builds only
69 | new AotPlugin({
70 | tsConfigPath: './tsconfig.json',
71 | entryModule: path.join(__dirname, 'ClientApp/app/app.module.server#AppModule'),
72 | exclude: ['./**/*.browser.ts']
73 | })
74 | ]),
75 | output: {
76 | libraryTarget: 'commonjs',
77 | path: path.join(__dirname, './ClientApp/dist')
78 | },
79 | target: 'node',
80 | devtool: 'inline-source-map'
81 | });
82 |
83 | return [clientBundleConfig, serverBundleConfig];
84 | };
85 |
--------------------------------------------------------------------------------
/webpack.config.vendor.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const ExtractTextPlugin = require('extract-text-webpack-plugin');
4 | const merge = require('webpack-merge');
5 | const treeShakableModules = [
6 | '@angular/animations',
7 | '@angular/common',
8 | '@angular/compiler',
9 | '@angular/core',
10 | '@angular/forms',
11 | '@angular/http',
12 | '@angular/platform-browser',
13 | '@angular/platform-browser-dynamic',
14 | '@angular/router',
15 | 'zone.js',
16 | ];
17 | const nonTreeShakableModules = [
18 | 'bootstrap',
19 | 'bootstrap/dist/css/bootstrap.css',
20 | 'es6-promise',
21 | 'es6-shim',
22 | 'event-source-polyfill',
23 | 'jquery',
24 | ];
25 | const allModules = treeShakableModules.concat(nonTreeShakableModules);
26 |
27 | module.exports = (env) => {
28 | const extractCSS = new ExtractTextPlugin('vendor.css');
29 | const isDevBuild = !(env && env.prod);
30 | const sharedConfig = {
31 | stats: { modules: false },
32 | resolve: { extensions: [ '.js' ] },
33 | module: {
34 | rules: [
35 | { test: /\.(png|woff|woff2|eot|ttf|svg)(\?|$)/, use: 'url-loader?limit=100000' }
36 | ]
37 | },
38 | output: {
39 | publicPath: 'dist/',
40 | filename: '[name].js',
41 | library: '[name]_[hash]'
42 | },
43 | plugins: [
44 | new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery' }), // Maps these identifiers to the jQuery package (because Bootstrap expects it to be a global variable)
45 | new webpack.ContextReplacementPlugin(/\@angular\b.*\b(bundles|linker)/, path.join(__dirname, './ClientApp')), // Workaround for https://github.com/angular/angular/issues/11580
46 | new webpack.ContextReplacementPlugin(/angular(\\|\/)core(\\|\/)@angular/, path.join(__dirname, './ClientApp')), // Workaround for https://github.com/angular/angular/issues/14898
47 | new webpack.IgnorePlugin(/^vertx$/) // Workaround for https://github.com/stefanpenner/es6-promise/issues/100
48 | ]
49 | };
50 |
51 | const clientBundleConfig = merge(sharedConfig, {
52 | entry: {
53 | // To keep development builds fast, include all vendor dependencies in the vendor bundle.
54 | // But for production builds, leave the tree-shakable ones out so the AOT compiler can produce a smaller bundle.
55 | vendor: isDevBuild ? allModules : nonTreeShakableModules
56 | },
57 | output: { path: path.join(__dirname, 'wwwroot', 'dist') },
58 | module: {
59 | rules: [
60 | { test: /\.css(\?|$)/, use: extractCSS.extract({ use: isDevBuild ? 'css-loader' : 'css-loader?minimize' }) }
61 | ]
62 | },
63 | plugins: [
64 | extractCSS,
65 | new webpack.DllPlugin({
66 | path: path.join(__dirname, 'wwwroot', 'dist', '[name]-manifest.json'),
67 | name: '[name]_[hash]'
68 | })
69 | ].concat(isDevBuild ? [] : [
70 | new webpack.optimize.UglifyJsPlugin()
71 | ])
72 | });
73 |
74 | const serverBundleConfig = merge(sharedConfig, {
75 | target: 'node',
76 | resolve: { mainFields: ['main'] },
77 | entry: { vendor: allModules.concat(['aspnet-prerendering']) },
78 | output: {
79 | path: path.join(__dirname, 'ClientApp', 'dist'),
80 | libraryTarget: 'commonjs2',
81 | },
82 | module: {
83 | rules: [ { test: /\.css(\?|$)/, use: ['to-string-loader', isDevBuild ? 'css-loader' : 'css-loader?minimize' ] } ]
84 | },
85 | plugins: [
86 | new webpack.DllPlugin({
87 | path: path.join(__dirname, 'ClientApp', 'dist', '[name]-manifest.json'),
88 | name: '[name]_[hash]'
89 | })
90 | ]
91 | });
92 |
93 | return [clientBundleConfig, serverBundleConfig];
94 | }
95 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /Properties/launchSettings.json
2 |
3 | ## Ignore Visual Studio temporary files, build results, and
4 | ## files generated by popular Visual Studio add-ons.
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 |
12 | # User-specific files (MonoDevelop/Xamarin Studio)
13 | *.userprefs
14 |
15 | # Build results
16 | [Dd]ebug/
17 | [Dd]ebugPublic/
18 | [Rr]elease/
19 | [Rr]eleases/
20 | x64/
21 | x86/
22 | build/
23 | bld/
24 | bin/
25 | Bin/
26 | obj/
27 | Obj/
28 |
29 | # Visual Studio 2015 cache/options directory
30 | .vs/
31 | /wwwroot/dist/
32 | /ClientApp/dist/
33 |
34 | # MSTest test Results
35 | [Tt]est[Rr]esult*/
36 | [Bb]uild[Ll]og.*
37 |
38 | # NUNIT
39 | *.VisualState.xml
40 | TestResult.xml
41 |
42 | # Build Results of an ATL Project
43 | [Dd]ebugPS/
44 | [Rr]eleasePS/
45 | dlldata.c
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 |
84 | # Visual Studio profiler
85 | *.psess
86 | *.vsp
87 | *.vspx
88 | *.sap
89 |
90 | # TFS 2012 Local Workspace
91 | $tf/
92 |
93 | # Guidance Automation Toolkit
94 | *.gpState
95 |
96 | # ReSharper is a .NET coding add-in
97 | _ReSharper*/
98 | *.[Rr]e[Ss]harper
99 | *.DotSettings.user
100 |
101 | # JustCode is a .NET coding add-in
102 | .JustCode
103 |
104 | # TeamCity is a build add-in
105 | _TeamCity*
106 |
107 | # DotCover is a Code Coverage Tool
108 | *.dotCover
109 |
110 | # NCrunch
111 | _NCrunch_*
112 | .*crunch*.local.xml
113 | nCrunchTemp_*
114 |
115 | # MightyMoose
116 | *.mm.*
117 | AutoTest.Net/
118 |
119 | # Web workbench (sass)
120 | .sass-cache/
121 |
122 | # Installshield output folder
123 | [Ee]xpress/
124 |
125 | # DocProject is a documentation generator add-in
126 | DocProject/buildhelp/
127 | DocProject/Help/*.HxT
128 | DocProject/Help/*.HxC
129 | DocProject/Help/*.hhc
130 | DocProject/Help/*.hhk
131 | DocProject/Help/*.hhp
132 | DocProject/Help/Html2
133 | DocProject/Help/html
134 |
135 | # Click-Once directory
136 | publish/
137 |
138 | # Publish Web Output
139 | *.[Pp]ublish.xml
140 | *.azurePubxml
141 | # TODO: Comment the next line if you want to checkin your web deploy settings
142 | # but database connection strings (with potential passwords) will be unencrypted
143 | *.pubxml
144 | *.publishproj
145 |
146 | # NuGet Packages
147 | *.nupkg
148 | # The packages folder can be ignored because of Package Restore
149 | **/packages/*
150 | # except build/, which is used as an MSBuild target.
151 | !**/packages/build/
152 | # Uncomment if necessary however generally it will be regenerated when needed
153 | #!**/packages/repositories.config
154 |
155 | # Microsoft Azure Build Output
156 | csx/
157 | *.build.csdef
158 |
159 | # Microsoft Azure Emulator
160 | ecf/
161 | rcf/
162 |
163 | # Microsoft Azure ApplicationInsights config file
164 | ApplicationInsights.config
165 |
166 | # Windows Store app package directory
167 | AppPackages/
168 | BundleArtifacts/
169 |
170 | # Visual Studio cache files
171 | # files ending in .cache can be ignored
172 | *.[Cc]ache
173 | # but keep track of directories ending in .cache
174 | !*.[Cc]ache/
175 |
176 | # Others
177 | ClientBin/
178 | ~$*
179 | *~
180 | *.dbmdl
181 | *.dbproj.schemaview
182 | *.pfx
183 | *.publishsettings
184 | orleans.codegen.cs
185 |
186 | /node_modules
187 |
188 | /yarn.lock
189 |
190 | # RIA/Silverlight projects
191 | Generated_Code/
192 |
193 | # Backup & report files from converting an old project file
194 | # to a newer Visual Studio version. Backup files are not needed,
195 | # because we have git ;-)
196 | _UpgradeReport_Files/
197 | Backup*/
198 | UpgradeLog*.XML
199 | UpgradeLog*.htm
200 |
201 | # SQL Server files
202 | *.mdf
203 | *.ldf
204 |
205 | # Business Intelligence projects
206 | *.rdl.data
207 | *.bim.layout
208 | *.bim_*.settings
209 |
210 | # Microsoft Fakes
211 | FakesAssemblies/
212 |
213 | # GhostDoc plugin setting file
214 | *.GhostDoc.xml
215 |
216 | # Node.js Tools for Visual Studio
217 | .ntvs_analysis.dat
218 |
219 | # Visual Studio 6 build log
220 | *.plg
221 |
222 | # Visual Studio 6 workspace options file
223 | *.opt
224 |
225 | # Visual Studio LightSwitch build output
226 | **/*.HTMLClient/GeneratedArtifacts
227 | **/*.DesktopClient/GeneratedArtifacts
228 | **/*.DesktopClient/ModelManifest.xml
229 | **/*.Server/GeneratedArtifacts
230 | **/*.Server/ModelManifest.xml
231 | _Pvt_Extensions
232 |
233 | # Paket dependency manager
234 | .paket/paket.exe
235 |
236 | # FAKE - F# Make
237 | .fake/
238 |
--------------------------------------------------------------------------------
/ClientApp/app/components/home/home.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Inject } from '@angular/core';
2 | import { PLATFORM_ID } from '@angular/core';
3 | import { isPlatformBrowser } from '@angular/common';
4 |
5 | @Component({
6 | selector: 'home',
7 | templateUrl: './home.component.html'
8 | })
9 |
10 | export class HomeComponent implements OnInit {
11 |
12 | public decimalNow: string;
13 | public inputOutputDecimalTime: string;
14 | public inputOutputTime: string;
15 | public errors: string[];
16 | private inputChanged: string;
17 | private inputChangedDecimal = "decimalTime";
18 | private inputChangedTime = "time";
19 |
20 | constructor( @Inject(PLATFORM_ID) private platformId: Object) { }
21 |
22 | ngOnInit(): void {
23 | if (isPlatformBrowser(this.platformId)) {
24 | setInterval(() => {
25 | this.setDecimalTimeNow();
26 | }, 0);
27 | }
28 | }
29 |
30 | public convert() {
31 | this.errors = [];
32 | if (this.inputChanged === this.inputChangedTime) {
33 | this.convertInputToDecimalTime();
34 | } else if (this.inputChanged === this.inputChangedDecimal) {
35 | this.convertInputToTime();
36 | } else {
37 | this.errors.push("you must change one of the inputs");
38 | }
39 | }
40 |
41 | public decimalTimeInputChanged() {
42 | this.inputChanged = this.inputChangedDecimal;
43 | }
44 |
45 | public timeInputChanged() {
46 | this.inputChanged = this.inputChangedTime;
47 | }
48 |
49 | private setDecimalTime(hours: number, minutes: number, seconds: number) {
50 | this.decimalNow = this.formatDecimalTime(this.getDecimalTime(hours, minutes, seconds));
51 | }
52 |
53 | private convertInputToDecimalTime() {
54 | if (this.inputOutputTime) {
55 | var timeArr = this.inputOutputTime.split(':');
56 | var hours = timeArr[0];
57 | var minutes = timeArr[1];
58 | var seconds = timeArr[2];
59 | if (!seconds) {
60 | seconds = "0";
61 | }
62 | this.inputOutputDecimalTime = this.formatDecimalTime(this.getDecimalTime(parseInt(hours), parseInt(minutes), parseInt(seconds)));
63 | } else {
64 | this.errors.push("Please input valid time");
65 | }
66 | }
67 |
68 | private formatDecimalTime(decimalTime: number) {
69 | if (decimalTime === 0) {
70 | return "00.00";
71 | }
72 | if (!decimalTime) {
73 | return "";
74 | }
75 | var decimalTimeAsString = decimalTime.toString();
76 | if (decimalTimeAsString.length === 3) {
77 | return decimalTimeAsString.slice(0, 1) + '.' +
78 | decimalTimeAsString.slice(2, 3) + '0';
79 | }
80 | if (decimalTimeAsString.length === 1) {
81 | return decimalTimeAsString + '.00';
82 | }
83 | var secondsStr = decimalTimeAsString.slice(4, 6);
84 | if (secondsStr.length < 2) {
85 | secondsStr += '0';
86 | }
87 | return decimalTimeAsString.slice(0, 1) + '.' +
88 | decimalTimeAsString.slice(2, 4) + '.' +
89 | secondsStr;
90 | }
91 |
92 | private getDecimalTime(hours: number, minutes: number, seconds: number) {
93 | var dhours = hours * 5 / 12;
94 | var dminutes = minutes * 1000 / 144 / 1000;
95 | var dseconds = seconds * 1000 / 864 / 10000;
96 | return dhours + dminutes + dseconds;
97 | }
98 |
99 | private getTime(dhours: number, dminutes: number, dseconds: number) {
100 | var hours = (dhours * 8640);
101 | var minutes = dminutes * (864/10);
102 | var seconds = (dseconds * 60) / 100;
103 | return new Date(2000, 1, 1, 0, 0, seconds + hours + minutes).toLocaleTimeString('en-GB');
104 | }
105 |
106 | private setDecimalTimeNow() {
107 | var now = new Date();
108 | this.setDecimalTime(now.getHours(), now.getMinutes(), now.getSeconds());
109 | }
110 |
111 | private convertInputToTime() {
112 | try {
113 | var splitedDecimalTime = this.inputOutputDecimalTime.split('.');
114 | var dhours = parseInt(splitedDecimalTime[0]);
115 | var dminutesStr = splitedDecimalTime[1];
116 | var dminutes = parseInt(dminutesStr);
117 | var dsecondsStr = splitedDecimalTime[2];
118 | var dseconds = parseInt(dsecondsStr);
119 | if (dminutesStr.length !== 2) {
120 | this.errors.push("Decimal minutes should be two digits. Example: '00', '05' or '20'");
121 | } else if (dminutes < 0 || dminutes >= 100) {
122 | this.errors.push("Decimal minutes should be between 0 and 99 inclussive");
123 | }
124 |
125 | if (dsecondsStr && (dsecondsStr.length !== 2)) {
126 | this.errors.push("Decimal seconds should be two digits. Example: '00', '05' or '20'");
127 | } else if (dsecondsStr && (dseconds < 0 || dseconds >= 100)) {
128 | this.errors.push("Decimal seconds should be between 0 and 99 inclussive");
129 | }
130 | else if (!dsecondsStr) {
131 | dseconds = 0;
132 | }
133 | if (dhours < 0 || dhours > 10) {
134 | this.errors.push("Decimal hours should be between 0 and 10 inclussive");
135 | }
136 | if (this.errors.length <= 0) {
137 | this.inputOutputTime = this.getTime(dhours, dminutes, dseconds);
138 | }
139 | } catch (error) {
140 | this.errors.push(
141 | "You must enter decimal time in a proper format. See example above (decimal seconds are optional)");
142 | }
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/ClientApp/app/components/home/home.component.spec.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import { assert } from 'chai';
3 | import { HomeComponent } from './home.component';
4 | import { TestBed, async, ComponentFixture } from '@angular/core/testing';
5 | import { FormsModule } from '@angular/forms';
6 | let fixture: ComponentFixture;
7 |
8 | // For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
9 |
10 | describe('Home component', () => {
11 | beforeEach(() => {
12 | TestBed.configureTestingModule({ imports: [FormsModule], declarations: [HomeComponent] });
13 | fixture = TestBed.createComponent(HomeComponent);
14 | fixture.detectChanges();
15 | });
16 |
17 | it('24 hours should return 1 decimal day (10 decimal hours)', async(() => {
18 | fixture.componentInstance.inputOutputTime = new Date(1980, 6, 6, 24, 0, 0).toLocaleTimeString();
19 | fixture.componentInstance.timeInputChanged();
20 |
21 | fixture.componentInstance.convert();
22 |
23 | expect(fixture.componentInstance.inputOutputDecimalTime).toEqual("00.00");
24 | }));
25 |
26 | it('12 hours should return 0.5 decimal day (5 decimal hours)', async(() => {
27 | fixture.componentInstance.inputOutputTime = new Date(1980, 6, 6, 12, 0, 0).toLocaleTimeString();
28 | fixture.componentInstance.timeInputChanged();
29 |
30 | fixture.componentInstance.convert();
31 |
32 | expect(fixture.componentInstance.inputOutputDecimalTime).toEqual("5.00");
33 | }));
34 |
35 | it('should convert inputed time AM', async(() => {
36 | fixture.componentInstance.inputOutputTime = '9:30';
37 | fixture.componentInstance.timeInputChanged();
38 | fixture.componentInstance.convert();
39 |
40 | expect(fixture.componentInstance.inputOutputDecimalTime).toEqual("3.95.83");
41 | }));
42 |
43 | it('should convert inputed time PM', async(() => {
44 | fixture.componentInstance.inputOutputTime = '17:30';
45 | fixture.componentInstance.timeInputChanged();
46 | fixture.componentInstance.convert();
47 |
48 | expect(fixture.componentInstance.inputOutputDecimalTime).toEqual("7.29.16");
49 | }));
50 |
51 | it('should convert inputed time with nice format', async(() => {
52 | fixture.componentInstance.inputOutputTime = '03:02';
53 | fixture.componentInstance.timeInputChanged();
54 | fixture.componentInstance.convert();
55 |
56 | expect(fixture.componentInstance.inputOutputDecimalTime).toEqual("1.26.38");
57 | }));
58 |
59 | it('should convert decimal time to time', async(() => {
60 | fixture.componentInstance.inputOutputDecimalTime = "10.00.00";
61 | fixture.componentInstance.decimalTimeInputChanged();
62 |
63 | fixture.componentInstance.convert();
64 |
65 | var expected = new Date(2000, 1, 1, 24, 0, 0);
66 | expect(fixture.componentInstance.inputOutputTime).toEqual(expected.toLocaleTimeString());
67 | }));
68 |
69 | it('should convert decimal time to time for half day', async(() => {
70 | fixture.componentInstance.inputOutputDecimalTime = "5.00.00";
71 | fixture.componentInstance.decimalTimeInputChanged();
72 |
73 | fixture.componentInstance.convert();
74 |
75 | var expected = new Date(2000, 1, 1, 12, 0, 0);
76 | expect(fixture.componentInstance.inputOutputTime).toEqual(expected.toLocaleTimeString());
77 | }));
78 |
79 | it('should convert decimal time to time for fifth of a day', async(() => {
80 | fixture.componentInstance.inputOutputDecimalTime = "2.00.00";
81 | fixture.componentInstance.decimalTimeInputChanged();
82 |
83 | fixture.componentInstance.convert();
84 |
85 | var expected = new Date(2000, 0, 0, 4, 48, 0);
86 | expect(fixture.componentInstance.inputOutputTime).toEqual(expected.toLocaleTimeString());
87 | }));
88 |
89 | it('should convert decimal time to time for fourth of a minute', async(() => {
90 | fixture.componentInstance.inputOutputDecimalTime = "0.00.25";
91 | fixture.componentInstance.decimalTimeInputChanged();
92 |
93 | fixture.componentInstance.convert();
94 |
95 | var expected = new Date(2000, 1, 1, 0, 0, 15);
96 | expect(fixture.componentInstance.inputOutputTime).toEqual(expected.toLocaleTimeString());
97 | }));
98 |
99 | it('should convert decimal time to time for 30 seconds', async(() => {
100 | fixture.componentInstance.inputOutputDecimalTime = "0.00.50";
101 | fixture.componentInstance.decimalTimeInputChanged();
102 |
103 | fixture.componentInstance.convert();
104 |
105 | var expected = new Date(2000, 1, 1, 0, 0, 30);
106 | expect(fixture.componentInstance.inputOutputTime).toEqual(expected.toLocaleTimeString());
107 | }));
108 |
109 | it('should convert decimal time to time for quarter day', async(() => {
110 | fixture.componentInstance.inputOutputDecimalTime = "2.50.00";
111 | fixture.componentInstance.decimalTimeInputChanged();
112 |
113 | fixture.componentInstance.convert();
114 |
115 | var expected = new Date(2000, 1, 1, 6, 0, 0);
116 | expect(fixture.componentInstance.inputOutputTime).toEqual(expected.toLocaleTimeString());
117 | }));
118 |
119 | it('should convert time to decimal time for quarter day', async(() => {
120 | fixture.componentInstance.inputOutputTime = new Date(2000, 1, 1, 6, 0, 0).toLocaleTimeString();
121 | fixture.componentInstance.timeInputChanged();
122 |
123 | fixture.componentInstance.convert();
124 |
125 | expect(fixture.componentInstance.inputOutputDecimalTime).toEqual("2.50");
126 | }));
127 |
128 | it('should convert decimal time to time for 4:30PM', async(() => {
129 | fixture.componentInstance.inputOutputDecimalTime = "6.88.00";
130 | fixture.componentInstance.decimalTimeInputChanged();
131 |
132 | fixture.componentInstance.convert();
133 |
134 | var expected = new Date(2000, 1, 1, 16, 30, 43);
135 | expect(fixture.componentInstance.inputOutputTime).toEqual(expected.toLocaleTimeString());
136 | }));
137 |
138 | it('should convert decimal time to time for 3:14PM', async(() => {
139 | fixture.componentInstance.inputOutputDecimalTime = "6.35.00";
140 | fixture.componentInstance.decimalTimeInputChanged();
141 |
142 | fixture.componentInstance.convert();
143 |
144 | var expected = new Date(2000, 1, 1, 15, 14, 24);
145 | expect(fixture.componentInstance.inputOutputTime).toEqual(expected.toLocaleTimeString());
146 | }));
147 |
148 | it('should convert decimal time to time for 7:09PM', async(() => {
149 | fixture.componentInstance.inputOutputDecimalTime = "7.98.00";
150 | fixture.componentInstance.decimalTimeInputChanged();
151 |
152 | fixture.componentInstance.convert();
153 |
154 | var expected = new Date(2000, 1, 1, 19, 9, 7);
155 | expect(fixture.componentInstance.inputOutputTime).toEqual(expected.toLocaleTimeString());
156 | }));
157 |
158 | it('should validate decimal time format when only minute', async(() => {
159 | fixture.componentInstance.inputOutputDecimalTime = "6.35";
160 | fixture.componentInstance.decimalTimeInputChanged();
161 |
162 | fixture.componentInstance.convert();
163 |
164 | var hasError = fixture.componentInstance.errors.length > 0;
165 | expect(hasError).toBe(false);
166 | }));
167 |
168 | it('should error decimal time format when hour greater than 10', async(() => {
169 | fixture.componentInstance.inputOutputDecimalTime = "11.35";
170 | fixture.componentInstance.decimalTimeInputChanged();
171 |
172 | fixture.componentInstance.convert();
173 |
174 | var hasError = fixture.componentInstance.errors.length > 0;
175 | expect(hasError).toBe(true);
176 | }));
177 |
178 | it('should error decimal time format when hour less than 0', async(() => {
179 | fixture.componentInstance.inputOutputDecimalTime = "-1.35";
180 | fixture.componentInstance.decimalTimeInputChanged();
181 |
182 | fixture.componentInstance.convert();
183 |
184 | var hasError = fixture.componentInstance.errors.length > 0;
185 | expect(hasError).toBe(true);
186 | }));
187 |
188 | it('should error decimal time format when minute is more than 99', async(() => {
189 | fixture.componentInstance.inputOutputDecimalTime = "1.100";
190 | fixture.componentInstance.decimalTimeInputChanged();
191 |
192 | fixture.componentInstance.convert();
193 |
194 | var hasError = fixture.componentInstance.errors.length > 0;
195 | expect(hasError).toBe(true);
196 | }));
197 |
198 | it('should error decimal time format when minute is less than 0', async(() => {
199 | fixture.componentInstance.inputOutputDecimalTime = "1.-1";
200 | fixture.componentInstance.decimalTimeInputChanged();
201 |
202 | fixture.componentInstance.convert();
203 |
204 | var hasError = fixture.componentInstance.errors.length > 0;
205 | expect(hasError).toBe(true);
206 | }));
207 |
208 | it('should error decimal time format when minute is not two digits', async(() => {
209 | fixture.componentInstance.inputOutputDecimalTime = "1.5";
210 | fixture.componentInstance.decimalTimeInputChanged();
211 |
212 | fixture.componentInstance.convert();
213 |
214 | var hasExplanation = fixture.componentInstance.errors.filter(x => x.includes("two digits")).length > 0;
215 | expect(hasExplanation).toBe(true);
216 | }));
217 |
218 |
219 | it('should error decimal time format when second is more than 99', async(() => {
220 | fixture.componentInstance.inputOutputDecimalTime = "1.10.100";
221 | fixture.componentInstance.decimalTimeInputChanged();
222 |
223 | fixture.componentInstance.convert();
224 |
225 | var hasError = fixture.componentInstance.errors.length > 0;
226 | expect(hasError).toBe(true);
227 | }));
228 |
229 | it('should error decimal time format when second is less than 0', async(() => {
230 | fixture.componentInstance.inputOutputDecimalTime = "1.10.-1";
231 | fixture.componentInstance.decimalTimeInputChanged();
232 |
233 | fixture.componentInstance.convert();
234 |
235 | var hasError = fixture.componentInstance.errors.length > 0;
236 | expect(hasError).toBe(true);
237 | }));
238 |
239 | it('should validate decimal time format when second is 00', async(() => {
240 | fixture.componentInstance.inputOutputDecimalTime = "1.10.00";
241 | fixture.componentInstance.decimalTimeInputChanged();
242 |
243 | fixture.componentInstance.convert();
244 |
245 | var hasError = fixture.componentInstance.errors.length > 0;
246 | expect(hasError).toBe(false);
247 | }));
248 |
249 | it('should error decimal time format when second is not two digits', async(() => {
250 | fixture.componentInstance.inputOutputDecimalTime = "1.50.4";
251 | fixture.componentInstance.decimalTimeInputChanged();
252 |
253 | fixture.componentInstance.convert();
254 |
255 | var hasExplanation = fixture.componentInstance.errors.filter(x => x.includes("two digits")).length > 0;
256 | expect(hasExplanation).toBe(true);
257 | }));
258 |
259 | it('should calculate valid date if only decimal hours and decimal minutes are given', async(() => {
260 | fixture.componentInstance.inputOutputDecimalTime = "7.98";
261 | fixture.componentInstance.decimalTimeInputChanged();
262 |
263 | fixture.componentInstance.convert();
264 |
265 | var expected = new Date(2000, 1, 1, 19, 9, 7);
266 | expect(fixture.componentInstance.inputOutputTime).toEqual(expected.toLocaleTimeString());
267 | }));
268 |
269 | it('should append zero to decimal seconds', async(() => {
270 | fixture.componentInstance.inputOutputTime = new Date(2000, 1, 1, 19, 30, 0).toLocaleTimeString();
271 | fixture.componentInstance.timeInputChanged();
272 |
273 | fixture.componentInstance.convert();
274 |
275 | expect(fixture.componentInstance.inputOutputDecimalTime).toEqual("8.12.50");
276 | }));
277 |
278 | it('should convert 2:24:00 AM to 1.00', async(() => {
279 | fixture.componentInstance.inputOutputTime = new Date(2000, 1, 1, 2, 24, 0).toLocaleTimeString();
280 | fixture.componentInstance.timeInputChanged();
281 |
282 | fixture.componentInstance.convert();
283 |
284 | expect(fixture.componentInstance.inputOutputDecimalTime).toEqual("1.00");
285 | }));
286 |
287 | it('should error on first attempt to convert', async(() => {
288 | fixture.componentInstance.convert();
289 |
290 | var hasExplanation = fixture.componentInstance.errors.filter(x => x.includes("must change one of the inputs")).length > 0;
291 | expect(hasExplanation).toBe(true);
292 | }));
293 | });
294 |
--------------------------------------------------------------------------------
/docs/dist/89889688147bd7575d6327160d64e760.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------