adding angular-cli support (causes major refactoring)
This commit is contained in:
parent
e7cb6311a9
commit
8b9f287a88
58
.angular-cli.json
Normal file
58
.angular-cli.json
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||||
|
"project": {
|
||||||
|
"name": "pigallery2"
|
||||||
|
},
|
||||||
|
"apps": [
|
||||||
|
{
|
||||||
|
"root": "frontend",
|
||||||
|
"outDir": "dist",
|
||||||
|
"assets": [
|
||||||
|
"assets",
|
||||||
|
"favicon.ico",
|
||||||
|
"config_inject.ejs"
|
||||||
|
],
|
||||||
|
"index": "index.html",
|
||||||
|
"main": "main.ts",
|
||||||
|
"polyfills": "polyfills.ts",
|
||||||
|
"test": "test.ts",
|
||||||
|
"tsconfig": "tsconfig.app.json",
|
||||||
|
"testTsconfig": "tsconfig.spec.json",
|
||||||
|
"prefix": "app",
|
||||||
|
"styles": [
|
||||||
|
"styles.css"
|
||||||
|
],
|
||||||
|
"scripts": [],
|
||||||
|
"environmentSource": "environments/environment.ts",
|
||||||
|
"environments": {
|
||||||
|
"dev": "environments/environment.ts",
|
||||||
|
"prod": "environments/environment.prod.ts"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"e2e": {
|
||||||
|
"protractor": {
|
||||||
|
"config": "./protractor.conf.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint": [
|
||||||
|
{
|
||||||
|
"project": "src/tsconfig.app.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"project": "src/tsconfig.spec.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"project": "test/e2e/tsconfig.e2e.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"test": {
|
||||||
|
"karma": {
|
||||||
|
"config": "./karma.conf.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaults": {
|
||||||
|
"styleExt": "css",
|
||||||
|
"component": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
.editorconfig
Normal file
13
.editorconfig
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Editor configuration, see http://editorconfig.org
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
max_line_length = off
|
||||||
|
trim_trailing_whitespace = false
|
||||||
9
.gitignore
vendored
9
.gitignore
vendored
@ -2,10 +2,8 @@
|
|||||||
PiGallery2.iml
|
PiGallery2.iml
|
||||||
node_modules/
|
node_modules/
|
||||||
pigallery2.zip
|
pigallery2.zip
|
||||||
frontend/app/**/*.js
|
frontend/**/*.js
|
||||||
frontend/app/**/*.js.map
|
frontend/**/*.js.map
|
||||||
frontend/main.js
|
|
||||||
frontend/main.js.map
|
|
||||||
frontend/dist
|
frontend/dist
|
||||||
backend/**/*.js
|
backend/**/*.js
|
||||||
backend/**/*.js.map
|
backend/**/*.js.map
|
||||||
@ -14,6 +12,9 @@ common/**/*.js.map
|
|||||||
test/coverage
|
test/coverage
|
||||||
test/backend/**/*.js
|
test/backend/**/*.js
|
||||||
test/backend/**/*.js.map
|
test/backend/**/*.js.map
|
||||||
|
test/e2e/**/*.js
|
||||||
|
test/e2e/**/*.js.map
|
||||||
demo/TEMP/
|
demo/TEMP/
|
||||||
config.json
|
config.json
|
||||||
users.db
|
users.db
|
||||||
|
dist/
|
||||||
|
|||||||
@ -21,4 +21,4 @@ class ProjectPathClass {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export let ProjectPath = new ProjectPathClass();
|
export const ProjectPath = new ProjectPathClass();
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import {ContentWrapper} from "../../../common/entities/ConentWrapper";
|
|||||||
import {DirectoryDTO} from "../../../common/entities/DirectoryDTO";
|
import {DirectoryDTO} from "../../../common/entities/DirectoryDTO";
|
||||||
import {ProjectPath} from "../../ProjectPath";
|
import {ProjectPath} from "../../ProjectPath";
|
||||||
import {PhotoDTO} from "../../../common/entities/PhotoDTO";
|
import {PhotoDTO} from "../../../common/entities/PhotoDTO";
|
||||||
import {hardwareRenderer, softwareRenderer} from "./THRenderers";
|
import {hardwareRenderer, RendererInput, softwareRenderer} from "./THRenderers";
|
||||||
import {Config} from "../../../common/config/private/Config";
|
import {Config} from "../../../common/config/private/Config";
|
||||||
|
|
||||||
|
|
||||||
@ -139,20 +139,35 @@ export class ThumbnailGeneratorMWs {
|
|||||||
|
|
||||||
this.initPools();
|
this.initPools();
|
||||||
//run on other thread
|
//run on other thread
|
||||||
pool.send({
|
|
||||||
|
let input = <RendererInput>{
|
||||||
imagePath: imagePath,
|
imagePath: imagePath,
|
||||||
size: size,
|
size: size,
|
||||||
thPath: thPath,
|
thPath: thPath,
|
||||||
makeSquare: makeSquare,
|
makeSquare: makeSquare,
|
||||||
qualityPriority: Config.Server.thumbnail.qualityPriority,
|
qualityPriority: Config.Server.thumbnail.qualityPriority,
|
||||||
__dirname: __dirname,
|
__dirname: __dirname,
|
||||||
})
|
};
|
||||||
|
if (Config.Server.enableThreading == true) {
|
||||||
|
pool.send(imagePath)
|
||||||
.on('done', (out) => {
|
.on('done', (out) => {
|
||||||
return next(out);
|
return next(out);
|
||||||
}).on('error', (error) => {
|
}).on('error', (error) => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
return next(new Error(ErrorCodes.THUMBNAIL_GENERATION_ERROR, error));
|
return next(new Error(ErrorCodes.THUMBNAIL_GENERATION_ERROR, error));
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
if (Config.Server.thumbnail.hardwareAcceleration == true) {
|
||||||
|
hardwareRenderer(input, out => next(out));
|
||||||
|
} else {
|
||||||
|
softwareRenderer(input, out => next(out));
|
||||||
|
}
|
||||||
|
}catch (error){
|
||||||
|
console.log(error);
|
||||||
|
return next(new Error(ErrorCodes.THUMBNAIL_GENERATION_ERROR, error));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static generateThumbnailName(imagePath: string, size: number): string {
|
private static generateThumbnailName(imagePath: string, size: number): string {
|
||||||
|
|||||||
@ -1,197 +1,18 @@
|
|||||||
///<reference path="exif.d.ts"/>
|
///<reference path="exif.d.ts"/>
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
import {DirectoryDTO} from "../../common/entities/DirectoryDTO";
|
import {DirectoryDTO} from "../../common/entities/DirectoryDTO";
|
||||||
import {
|
|
||||||
CameraMetadata,
|
|
||||||
GPSMetadata,
|
|
||||||
ImageSize,
|
|
||||||
PhotoDTO,
|
|
||||||
PhotoMetadata,
|
|
||||||
PositionMetaData
|
|
||||||
} from "../../common/entities/PhotoDTO";
|
|
||||||
import {ProjectPath} from "../ProjectPath";
|
import {ProjectPath} from "../ProjectPath";
|
||||||
import {Logger} from "../Logger";
|
import {Logger} from "../Logger";
|
||||||
|
import {diskManagerTask, DiskManagerTask} from "./DiskMangerTask";
|
||||||
|
import {Config} from "../../common/config/private/Config";
|
||||||
|
|
||||||
const Pool = require('threads').Pool;
|
const Pool = require('threads').Pool;
|
||||||
const pool = new Pool();
|
const pool = new Pool();
|
||||||
|
|
||||||
const LOG_TAG = "[DiskManager]";
|
const LOG_TAG = "[DiskManager]";
|
||||||
|
|
||||||
interface PoolInput {
|
|
||||||
relativeDirectoryName: string;
|
|
||||||
directoryName: string;
|
|
||||||
directoryParent: string;
|
|
||||||
absoluteDirectoryName: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
pool.run(
|
pool.run(diskManagerTask);
|
||||||
(input: PoolInput, done) => {
|
|
||||||
const fs = require("fs");
|
|
||||||
const path = require("path");
|
|
||||||
const mime = require("mime");
|
|
||||||
const iptc = require("node-iptc");
|
|
||||||
const exif_parser = require("exif-parser");
|
|
||||||
|
|
||||||
|
|
||||||
let isImage = (fullPath: string) => {
|
|
||||||
let imageMimeTypes = [
|
|
||||||
'image/bmp',
|
|
||||||
'image/gif',
|
|
||||||
'image/jpeg',
|
|
||||||
'image/png',
|
|
||||||
'image/pjpeg',
|
|
||||||
'image/tiff',
|
|
||||||
'image/webp',
|
|
||||||
'image/x-tiff',
|
|
||||||
'image/x-windows-bmp'
|
|
||||||
];
|
|
||||||
|
|
||||||
let extension = mime.lookup(fullPath);
|
|
||||||
|
|
||||||
return imageMimeTypes.indexOf(extension) !== -1;
|
|
||||||
};
|
|
||||||
|
|
||||||
let loadPhotoMetadata = (fullPath: string): Promise<PhotoMetadata> => {
|
|
||||||
return new Promise<PhotoMetadata>((resolve: (metadata: PhotoMetadata) => void, reject) => {
|
|
||||||
fs.readFile(fullPath, function (err, data) {
|
|
||||||
if (err) {
|
|
||||||
return reject({file: fullPath, error: err});
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
|
|
||||||
const exif = exif_parser.create(data).parse();
|
|
||||||
const iptcData = iptc(data);
|
|
||||||
|
|
||||||
const imageSize: ImageSize = {width: exif.imageSize.width, height: exif.imageSize.height};
|
|
||||||
const cameraData: CameraMetadata = {
|
|
||||||
ISO: exif.tags.ISO,
|
|
||||||
model: exif.tags.Modeol,
|
|
||||||
maker: exif.tags.Make,
|
|
||||||
fStop: exif.tags.FNumber,
|
|
||||||
exposure: exif.tags.ExposureTime,
|
|
||||||
focalLength: exif.tags.FocalLength,
|
|
||||||
lens: exif.tags.LensModel,
|
|
||||||
};
|
|
||||||
const GPS: GPSMetadata = {
|
|
||||||
latitude: exif.tags.GPSLatitude,
|
|
||||||
longitude: exif.tags.GPSLongitude,
|
|
||||||
altitude: exif.tags.GPSAltitude
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const positionData: PositionMetaData = {
|
|
||||||
GPSData: GPS,
|
|
||||||
country: iptcData.country_or_primary_location_name,
|
|
||||||
state: iptcData.province_or_state,
|
|
||||||
city: iptcData.city
|
|
||||||
};
|
|
||||||
|
|
||||||
//Decode characters to UTF8
|
|
||||||
const decode = (s: any) => {
|
|
||||||
for (let a, b, i = -1, l = (s = s.split("")).length, o = String.fromCharCode, c = "charCodeAt"; ++i < l;
|
|
||||||
((a = s[i][c](0)) & 0x80) &&
|
|
||||||
(s[i] = (a & 0xfc) == 0xc0 && ((b = s[i + 1][c](0)) & 0xc0) == 0x80 ?
|
|
||||||
o(((a & 0x03) << 6) + (b & 0x3f)) : o(128), s[++i] = "")
|
|
||||||
);
|
|
||||||
return s.join("");
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const keywords: string[] = (iptcData.keywords || []).map((s: string) => decode(s));
|
|
||||||
const creationDate: number = iptcData.date_time ? iptcData.date_time.getTime() : 0;
|
|
||||||
|
|
||||||
|
|
||||||
const metadata: PhotoMetadata = <PhotoMetadata>{
|
|
||||||
keywords: keywords,
|
|
||||||
cameraData: cameraData,
|
|
||||||
positionData: positionData,
|
|
||||||
size: imageSize,
|
|
||||||
creationDate: creationDate
|
|
||||||
};
|
|
||||||
return resolve(metadata);
|
|
||||||
} catch (err) {
|
|
||||||
return reject({file: fullPath, error: err});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
let parseDir = (directoryInfo: {
|
|
||||||
relativeDirectoryName: string,
|
|
||||||
directoryName: string,
|
|
||||||
directoryParent: string,
|
|
||||||
absoluteDirectoryName: string
|
|
||||||
}, maxPhotos: number = null, photosOnly: boolean = false): Promise<DirectoryDTO> => {
|
|
||||||
return new Promise<DirectoryDTO>((resolve, reject) => {
|
|
||||||
let promises: Array<Promise<any>> = [];
|
|
||||||
let directory = <DirectoryDTO>{
|
|
||||||
name: directoryInfo.directoryName,
|
|
||||||
path: directoryInfo.directoryParent,
|
|
||||||
lastUpdate: Date.now(),
|
|
||||||
directories: [],
|
|
||||||
photos: []
|
|
||||||
};
|
|
||||||
fs.readdir(directoryInfo.absoluteDirectoryName, (err, list) => {
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
return reject(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
for (let i = 0; i < list.length; i++) {
|
|
||||||
let file = list[i];
|
|
||||||
let fullFilePath = path.normalize(path.resolve(directoryInfo.absoluteDirectoryName, file));
|
|
||||||
if (photosOnly == false && fs.statSync(fullFilePath).isDirectory()) {
|
|
||||||
let promise = parseDir({
|
|
||||||
relativeDirectoryName: path.join(directoryInfo.relativeDirectoryName, path.sep),
|
|
||||||
directoryName: file,
|
|
||||||
directoryParent: path.join(directoryInfo.relativeDirectoryName, path.sep),
|
|
||||||
absoluteDirectoryName: fullFilePath
|
|
||||||
},
|
|
||||||
5, true
|
|
||||||
).then((dir) => {
|
|
||||||
directory.directories.push(dir);
|
|
||||||
});
|
|
||||||
promises.push(promise);
|
|
||||||
} else if (isImage(fullFilePath)) {
|
|
||||||
|
|
||||||
let promise = loadPhotoMetadata(fullFilePath).then((photoMetadata) => {
|
|
||||||
directory.photos.push(<PhotoDTO>{
|
|
||||||
name: file,
|
|
||||||
directory: null,
|
|
||||||
metadata: photoMetadata
|
|
||||||
});
|
|
||||||
});
|
|
||||||
promises.push(promise);
|
|
||||||
|
|
||||||
if (maxPhotos != null && promises.length > maxPhotos) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Promise.all(promises).then(() => {
|
|
||||||
return resolve(directory);
|
|
||||||
}).catch((err) => {
|
|
||||||
return reject({directoryInfo: directoryInfo, error: err});
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
return reject({directoryInfo: directoryInfo, error: err});
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
parseDir(input).then((dir) => {
|
|
||||||
done(null, dir);
|
|
||||||
}).catch((err) => {
|
|
||||||
done(err, null);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
export class DiskManager {
|
export class DiskManager {
|
||||||
public static scanDirectory(relativeDirectoryName: string, cb: (error: any, result: DirectoryDTO) => void) {
|
public static scanDirectory(relativeDirectoryName: string, cb: (error: any, result: DirectoryDTO) => void) {
|
||||||
@ -200,12 +21,14 @@ export class DiskManager {
|
|||||||
let directoryParent = path.join(path.dirname(relativeDirectoryName), path.sep);
|
let directoryParent = path.join(path.dirname(relativeDirectoryName), path.sep);
|
||||||
let absoluteDirectoryName = path.join(ProjectPath.ImageFolder, relativeDirectoryName);
|
let absoluteDirectoryName = path.join(ProjectPath.ImageFolder, relativeDirectoryName);
|
||||||
|
|
||||||
pool.send({
|
let input = <DiskManagerTask.PoolInput>{
|
||||||
relativeDirectoryName,
|
relativeDirectoryName,
|
||||||
directoryName,
|
directoryName,
|
||||||
directoryParent,
|
directoryParent,
|
||||||
absoluteDirectoryName
|
absoluteDirectoryName
|
||||||
}).on('done', (error: any, result: DirectoryDTO) => {
|
};
|
||||||
|
|
||||||
|
let done = (error: any, result: DirectoryDTO) => {
|
||||||
if (error || !result) {
|
if (error || !result) {
|
||||||
return cb(error, result);
|
return cb(error, result);
|
||||||
}
|
}
|
||||||
@ -220,9 +43,22 @@ export class DiskManager {
|
|||||||
};
|
};
|
||||||
addDirs(result);
|
addDirs(result);
|
||||||
return cb(error, result);
|
return cb(error, result);
|
||||||
}).on('error', (error) => {
|
};
|
||||||
|
|
||||||
|
let error = (error) => {
|
||||||
return cb(error, null);
|
return cb(error, null);
|
||||||
});
|
};
|
||||||
|
|
||||||
|
|
||||||
|
if (Config.Server.enableThreading == true) {
|
||||||
|
pool.send(input).on('done', done).on('error', error);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
diskManagerTask(input, done);
|
||||||
|
} catch (err) {
|
||||||
|
error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
192
backend/model/DiskMangerTask.ts
Normal file
192
backend/model/DiskMangerTask.ts
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
///<reference path="exif.d.ts"/>
|
||||||
|
import {DirectoryDTO} from "../../common/entities/DirectoryDTO";
|
||||||
|
import {
|
||||||
|
CameraMetadata,
|
||||||
|
GPSMetadata,
|
||||||
|
ImageSize,
|
||||||
|
PhotoDTO,
|
||||||
|
PhotoMetadata,
|
||||||
|
PositionMetaData
|
||||||
|
} from "../../common/entities/PhotoDTO";
|
||||||
|
|
||||||
|
const LOG_TAG = "[DiskManagerTask]";
|
||||||
|
|
||||||
|
export const diskManagerTask = (input: DiskManagerTask.PoolInput, done) => {
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
const mime = require("mime");
|
||||||
|
const iptc = require("node-iptc");
|
||||||
|
const exif_parser = require("exif-parser");
|
||||||
|
|
||||||
|
|
||||||
|
let isImage = (fullPath: string) => {
|
||||||
|
let imageMimeTypes = [
|
||||||
|
'image/bmp',
|
||||||
|
'image/gif',
|
||||||
|
'image/jpeg',
|
||||||
|
'image/png',
|
||||||
|
'image/pjpeg',
|
||||||
|
'image/tiff',
|
||||||
|
'image/webp',
|
||||||
|
'image/x-tiff',
|
||||||
|
'image/x-windows-bmp'
|
||||||
|
];
|
||||||
|
|
||||||
|
let extension = mime.lookup(fullPath);
|
||||||
|
|
||||||
|
return imageMimeTypes.indexOf(extension) !== -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
let loadPhotoMetadata = (fullPath: string): Promise<PhotoMetadata> => {
|
||||||
|
return new Promise<PhotoMetadata>((resolve: (metadata: PhotoMetadata) => void, reject) => {
|
||||||
|
fs.readFile(fullPath, function (err, data) {
|
||||||
|
if (err) {
|
||||||
|
return reject({file: fullPath, error: err});
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
|
||||||
|
const exif = exif_parser.create(data).parse();
|
||||||
|
const iptcData = iptc(data);
|
||||||
|
|
||||||
|
const imageSize: ImageSize = {width: exif.imageSize.width, height: exif.imageSize.height};
|
||||||
|
const cameraData: CameraMetadata = {
|
||||||
|
ISO: exif.tags.ISO,
|
||||||
|
model: exif.tags.Modeol,
|
||||||
|
maker: exif.tags.Make,
|
||||||
|
fStop: exif.tags.FNumber,
|
||||||
|
exposure: exif.tags.ExposureTime,
|
||||||
|
focalLength: exif.tags.FocalLength,
|
||||||
|
lens: exif.tags.LensModel,
|
||||||
|
};
|
||||||
|
const GPS: GPSMetadata = {
|
||||||
|
latitude: exif.tags.GPSLatitude,
|
||||||
|
longitude: exif.tags.GPSLongitude,
|
||||||
|
altitude: exif.tags.GPSAltitude
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const positionData: PositionMetaData = {
|
||||||
|
GPSData: GPS,
|
||||||
|
country: iptcData.country_or_primary_location_name,
|
||||||
|
state: iptcData.province_or_state,
|
||||||
|
city: iptcData.city
|
||||||
|
};
|
||||||
|
|
||||||
|
//Decode characters to UTF8
|
||||||
|
const decode = (s: any) => {
|
||||||
|
for (let a, b, i = -1, l = (s = s.split("")).length, o = String.fromCharCode, c = "charCodeAt"; ++i < l;
|
||||||
|
((a = s[i][c](0)) & 0x80) &&
|
||||||
|
(s[i] = (a & 0xfc) == 0xc0 && ((b = s[i + 1][c](0)) & 0xc0) == 0x80 ?
|
||||||
|
o(((a & 0x03) << 6) + (b & 0x3f)) : o(128), s[++i] = "")
|
||||||
|
);
|
||||||
|
return s.join("");
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const keywords: string[] = (iptcData.keywords || []).map((s: string) => decode(s));
|
||||||
|
const creationDate: number = iptcData.date_time ? iptcData.date_time.getTime() : 0;
|
||||||
|
|
||||||
|
|
||||||
|
const metadata: PhotoMetadata = <PhotoMetadata>{
|
||||||
|
keywords: keywords,
|
||||||
|
cameraData: cameraData,
|
||||||
|
positionData: positionData,
|
||||||
|
size: imageSize,
|
||||||
|
creationDate: creationDate
|
||||||
|
};
|
||||||
|
return resolve(metadata);
|
||||||
|
} catch (err) {
|
||||||
|
return reject({file: fullPath, error: err});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
let parseDir = (directoryInfo: {
|
||||||
|
relativeDirectoryName: string,
|
||||||
|
directoryName: string,
|
||||||
|
directoryParent: string,
|
||||||
|
absoluteDirectoryName: string
|
||||||
|
}, maxPhotos: number = null, photosOnly: boolean = false): Promise<DirectoryDTO> => {
|
||||||
|
return new Promise<DirectoryDTO>((resolve, reject) => {
|
||||||
|
let promises: Array<Promise<any>> = [];
|
||||||
|
let directory = <DirectoryDTO>{
|
||||||
|
name: directoryInfo.directoryName,
|
||||||
|
path: directoryInfo.directoryParent,
|
||||||
|
lastUpdate: Date.now(),
|
||||||
|
directories: [],
|
||||||
|
photos: []
|
||||||
|
};
|
||||||
|
fs.readdir(directoryInfo.absoluteDirectoryName, (err, list) => {
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
let file = list[i];
|
||||||
|
let fullFilePath = path.normalize(path.resolve(directoryInfo.absoluteDirectoryName, file));
|
||||||
|
if (photosOnly == false && fs.statSync(fullFilePath).isDirectory()) {
|
||||||
|
let promise = parseDir({
|
||||||
|
relativeDirectoryName: path.join(directoryInfo.relativeDirectoryName, path.sep),
|
||||||
|
directoryName: file,
|
||||||
|
directoryParent: path.join(directoryInfo.relativeDirectoryName, path.sep),
|
||||||
|
absoluteDirectoryName: fullFilePath
|
||||||
|
},
|
||||||
|
5, true
|
||||||
|
).then((dir) => {
|
||||||
|
directory.directories.push(dir);
|
||||||
|
});
|
||||||
|
promises.push(promise);
|
||||||
|
} else if (isImage(fullFilePath)) {
|
||||||
|
|
||||||
|
let promise = loadPhotoMetadata(fullFilePath).then((photoMetadata) => {
|
||||||
|
directory.photos.push(<PhotoDTO>{
|
||||||
|
name: file,
|
||||||
|
directory: null,
|
||||||
|
metadata: photoMetadata
|
||||||
|
});
|
||||||
|
});
|
||||||
|
promises.push(promise);
|
||||||
|
|
||||||
|
if (maxPhotos != null && promises.length > maxPhotos) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Promise.all(promises).then(() => {
|
||||||
|
return resolve(directory);
|
||||||
|
}).catch((err) => {
|
||||||
|
return reject({directoryInfo: directoryInfo, error: err});
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
return reject({directoryInfo: directoryInfo, error: err});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
parseDir(input).then((dir) => {
|
||||||
|
done(null, dir);
|
||||||
|
}).catch((err) => {
|
||||||
|
done(err, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export module DiskManagerTask {
|
||||||
|
|
||||||
|
export interface PoolInput {
|
||||||
|
relativeDirectoryName: string;
|
||||||
|
directoryName: string;
|
||||||
|
directoryParent: string;
|
||||||
|
absoluteDirectoryName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -21,16 +21,21 @@ export class PublicRouter {
|
|||||||
return next();
|
return next();
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use(_express.static(_path.resolve(__dirname, './../../frontend')));
|
app.get('/config_inject.js', (req: Request, res: Response) => {
|
||||||
|
res.render(_path.resolve(__dirname, './../../dist/config_inject.ejs'), res.tpl);
|
||||||
|
});
|
||||||
|
app.get(['/', '/login', "/gallery*", "/admin", "/search*"], (req: Request, res: Response) => {
|
||||||
|
res.sendFile(_path.resolve(__dirname, './../../dist/index.html'));
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use(_express.static(_path.resolve(__dirname, './../../dist')));
|
||||||
app.use('/node_modules', _express.static(_path.resolve(__dirname, './../../node_modules')));
|
app.use('/node_modules', _express.static(_path.resolve(__dirname, './../../node_modules')));
|
||||||
app.use('/common', _express.static(_path.resolve(__dirname, './../../common')));
|
app.use('/common', _express.static(_path.resolve(__dirname, './../../common')));
|
||||||
|
|
||||||
const renderIndex = (req: Request, res: Response) => {
|
const renderIndex = (req: Request, res: Response) => {
|
||||||
res.render(_path.resolve(__dirname, './../../frontend/index.ejs'), res.tpl);
|
res.render(_path.resolve(__dirname, './../../dist/index.html'));
|
||||||
};
|
};
|
||||||
|
|
||||||
app.get(['/', '/login', "/gallery*", "/admin", "/search*"], renderIndex);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -26,4 +26,5 @@ export interface ServerConfig {
|
|||||||
imagesFolder: string;
|
imagesFolder: string;
|
||||||
thumbnail: ThumbnailConfig;
|
thumbnail: ThumbnailConfig;
|
||||||
database: DataBaseConfig;
|
database: DataBaseConfig;
|
||||||
|
enableThreading:boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,7 +24,8 @@ export class PrivateConfigClass extends PublicConfigClass {
|
|||||||
database: "pigallery2"
|
database: "pigallery2"
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
enableThreading: true
|
||||||
};
|
};
|
||||||
|
|
||||||
public setDatabaseType(type: DatabaseType) {
|
public setDatabaseType(type: DatabaseType) {
|
||||||
|
|||||||
@ -42,17 +42,21 @@ export class EventLimit<T> {
|
|||||||
|
|
||||||
class EventLimitHandler<T> {
|
class EventLimitHandler<T> {
|
||||||
public lastTriggerValue: T = null;
|
public lastTriggerValue: T = null;
|
||||||
|
|
||||||
constructor(public limit: T, public handler: { (data?: T): void }) {
|
constructor(public limit: T, public handler: { (data?: T): void }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public fire(data?: T) {
|
public fire(data?: T) {
|
||||||
this.handler(data);
|
this.handler(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public isValid(): boolean {
|
public isValid(): boolean {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class SingleFireEventLimitHandler<T> extends EventLimitHandler<T> {
|
class SingleFireEventLimitHandler<T> extends EventLimitHandler<T> {
|
||||||
public fired = false;
|
public fired = false;
|
||||||
|
|
||||||
constructor(public limit: T, public handler: { (data?: T): void }) {
|
constructor(public limit: T, public handler: { (data?: T): void }) {
|
||||||
super(limit, handler);
|
super(limit, handler);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,8 +5,8 @@ import {UserRoles} from "../../../common/entities/UserDTO";
|
|||||||
import {Config} from "../../../common/config/public/Config";
|
import {Config} from "../../../common/config/public/Config";
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'admin',
|
selector: 'admin',
|
||||||
templateUrl: 'app/admin/admin.component.html',
|
templateUrl: './admin.component.html',
|
||||||
styleUrls: ['app/admin/admin.component.css']
|
styleUrls: ['./admin.component.css']
|
||||||
})
|
})
|
||||||
export class AdminComponent implements OnInit {
|
export class AdminComponent implements OnInit {
|
||||||
userManagementEnable: boolean = false;
|
userManagementEnable: boolean = false;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {NgModule} from "@angular/core";
|
import {Injectable, NgModule} from "@angular/core";
|
||||||
import {BrowserModule} from "@angular/platform-browser";
|
import {BrowserModule} from "@angular/platform-browser";
|
||||||
import {FormsModule} from "@angular/forms";
|
import {FormsModule} from "@angular/forms";
|
||||||
import {HttpModule} from "@angular/http";
|
import {HttpModule} from "@angular/http";
|
||||||
@ -31,6 +31,17 @@ import {GalleryMapLightboxComponent} from "./gallery/map/lightbox/lightbox.map.g
|
|||||||
import {ThumbnailManagerService} from "./gallery/thumnailManager.service";
|
import {ThumbnailManagerService} from "./gallery/thumnailManager.service";
|
||||||
import {OverlayService} from "./gallery/overlay.service";
|
import {OverlayService} from "./gallery/overlay.service";
|
||||||
import {Config} from "../../common/config/public/Config";
|
import {Config} from "../../common/config/public/Config";
|
||||||
|
import {LAZY_MAPS_API_CONFIG} from "@agm/core/services";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class GoogleMapsConfig {
|
||||||
|
apiKey: string;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.apiKey = Config.Client.googleApiKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@ -38,9 +49,7 @@ import {Config} from "../../common/config/public/Config";
|
|||||||
FormsModule,
|
FormsModule,
|
||||||
HttpModule,
|
HttpModule,
|
||||||
appRoutes,
|
appRoutes,
|
||||||
AgmCoreModule.forRoot({
|
AgmCoreModule.forRoot()
|
||||||
apiKey: Config.Client.googleApiKey
|
|
||||||
})
|
|
||||||
],
|
],
|
||||||
declarations: [AppComponent,
|
declarations: [AppComponent,
|
||||||
LoginComponent,
|
LoginComponent,
|
||||||
@ -62,6 +71,7 @@ import {Config} from "../../common/config/public/Config";
|
|||||||
FrameComponent,
|
FrameComponent,
|
||||||
StringifyRole],
|
StringifyRole],
|
||||||
providers: [
|
providers: [
|
||||||
|
{provide: LAZY_MAPS_API_CONFIG, useClass: GoogleMapsConfig},
|
||||||
NetworkService,
|
NetworkService,
|
||||||
UserService,
|
UserService,
|
||||||
GalleryCacheService,
|
GalleryCacheService,
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import {ModuleWithProviders} from "@angular/core";
|
import {ModuleWithProviders} from "@angular/core";
|
||||||
import {Routes, RouterModule} from "@angular/router";
|
import {RouterModule, Routes} from "@angular/router";
|
||||||
import {LoginComponent} from "./login/login.component";
|
import {LoginComponent} from "./login/login.component";
|
||||||
import {GalleryComponent} from "./gallery/gallery.component";
|
import {GalleryComponent} from "./gallery/gallery.component";
|
||||||
import {AdminComponent} from "./admin/admin.component";
|
import {AdminComponent} from "./admin/admin.component";
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
</button>
|
</button>
|
||||||
<a class="navbar-brand" href="#"><img src="icon_inv.png" style="max-height: 26px; display: inline;"/> PiGallery2</a>
|
<a class="navbar-brand" href="#"><img src="assets/icon_inv.png" style="max-height: 26px; display: inline;"/> PiGallery2</a>
|
||||||
</div>
|
</div>
|
||||||
<div id="navbar" class="collapse navbar-collapse">
|
<div id="navbar" class="collapse navbar-collapse">
|
||||||
<ul class="nav navbar-nav">
|
<ul class="nav navbar-nav">
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import {Config} from "../../../common/config/public/Config";
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-frame',
|
selector: 'app-frame',
|
||||||
templateUrl: 'app/frame/frame.component.html',
|
templateUrl: './frame.component.html',
|
||||||
providers: [RouterLink],
|
providers: [RouterLink],
|
||||||
encapsulation: ViewEncapsulation.Emulated
|
encapsulation: ViewEncapsulation.Emulated
|
||||||
})
|
})
|
||||||
|
|||||||
@ -4,10 +4,10 @@
|
|||||||
|
|
||||||
<div class="photo-container">
|
<div class="photo-container">
|
||||||
|
|
||||||
<div class="photo" *ngIf="thumbnail && thumbnail.available"
|
<div class="photo" *ngIf="thumbnail && thumbnail.Available"
|
||||||
[style.background-image]="'url('+thumbnail.src+')'"></div>
|
[style.background-image]="'url('+thumbnail.src+')'"></div>
|
||||||
|
|
||||||
<span *ngIf="!thumbnail || !thumbnail.available" class="glyphicon glyphicon-folder-open no-image"
|
<span *ngIf="!thumbnail || !thumbnail.Available" class="glyphicon glyphicon-folder-open no-image"
|
||||||
aria-hidden="true">
|
aria-hidden="true">
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {Component, Input, OnInit, OnDestroy, ViewChild, ElementRef} from "@angular/core";
|
import {Component, ElementRef, Input, OnDestroy, OnInit, ViewChild} from "@angular/core";
|
||||||
import {DirectoryDTO} from "../../../../common/entities/DirectoryDTO";
|
import {DirectoryDTO} from "../../../../common/entities/DirectoryDTO";
|
||||||
import {RouterLink} from "@angular/router";
|
import {RouterLink} from "@angular/router";
|
||||||
import {Utils} from "../../../../common/Utils";
|
import {Utils} from "../../../../common/Utils";
|
||||||
@ -7,8 +7,8 @@ import {Thumbnail, ThumbnailManagerService} from "../thumnailManager.service";
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'gallery-directory',
|
selector: 'gallery-directory',
|
||||||
templateUrl: 'app/gallery/directory/directory.gallery.component.html',
|
templateUrl: './directory.gallery.component.html',
|
||||||
styleUrls: ['app/gallery/directory/directory.gallery.component.css'],
|
styleUrls: ['./directory.gallery.component.css'],
|
||||||
providers: [RouterLink],
|
providers: [RouterLink],
|
||||||
})
|
})
|
||||||
export class GalleryDirectoryComponent implements OnInit, OnDestroy {
|
export class GalleryDirectoryComponent implements OnInit, OnDestroy {
|
||||||
|
|||||||
@ -9,8 +9,8 @@ import {Config} from "../../../common/config/public/Config";
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'gallery',
|
selector: 'gallery',
|
||||||
templateUrl: 'app/gallery/gallery.component.html',
|
templateUrl: './gallery.component.html',
|
||||||
styleUrls: ['app/gallery/gallery.component.css']
|
styleUrls: ['./gallery.component.css']
|
||||||
})
|
})
|
||||||
export class GalleryComponent implements OnInit {
|
export class GalleryComponent implements OnInit {
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ export class GalleryComponent implements OnInit {
|
|||||||
|
|
||||||
public showSearchBar: boolean = true;
|
public showSearchBar: boolean = true;
|
||||||
|
|
||||||
constructor(private _galleryService: GalleryService,
|
constructor(public _galleryService: GalleryService,
|
||||||
private _authService: AuthenticationService,
|
private _authService: AuthenticationService,
|
||||||
private _router: Router,
|
private _router: Router,
|
||||||
private _route: ActivatedRoute) {
|
private _route: ActivatedRoute) {
|
||||||
@ -60,8 +60,6 @@ export class GalleryComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onLightboxLastElement() {
|
onLightboxLastElement() {
|
||||||
|
|||||||
@ -20,8 +20,8 @@ import {Config} from "../../../../common/config/public/Config";
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'gallery-grid',
|
selector: 'gallery-grid',
|
||||||
templateUrl: 'app/gallery/grid/grid.gallery.component.html',
|
templateUrl: './grid.gallery.component.html',
|
||||||
styleUrls: ['app/gallery/grid/grid.gallery.component.css'],
|
styleUrls: ['./grid.gallery.component.css'],
|
||||||
})
|
})
|
||||||
export class GalleryGridComponent implements OnChanges, AfterViewInit {
|
export class GalleryGridComponent implements OnChanges, AfterViewInit {
|
||||||
|
|
||||||
@ -52,9 +52,9 @@ export class GalleryGridComponent implements OnChanges, AfterViewInit {
|
|||||||
this.updateContainerWidth();
|
this.updateContainerWidth();
|
||||||
this.sortPhotos();
|
this.sortPhotos();
|
||||||
this.mergeNewPhotos();
|
this.mergeNewPhotos();
|
||||||
setImmediate(() => {
|
setTimeout(() => {
|
||||||
this.renderPhotos();
|
this.renderPhotos();
|
||||||
});
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@HostListener('window:resize')
|
@HostListener('window:resize')
|
||||||
@ -82,9 +82,9 @@ export class GalleryGridComponent implements OnChanges, AfterViewInit {
|
|||||||
this.updateContainerWidth();
|
this.updateContainerWidth();
|
||||||
this.sortPhotos();
|
this.sortPhotos();
|
||||||
this.clearRenderedPhotos();
|
this.clearRenderedPhotos();
|
||||||
setImmediate(() => {
|
setTimeout(() => {
|
||||||
this.renderPhotos();
|
this.renderPhotos();
|
||||||
});
|
}, 0);
|
||||||
this.isAfterViewInit = true;
|
this.isAfterViewInit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,8 +2,8 @@ import {Component, Input} from "@angular/core";
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'gallery-grid-photo-loading',
|
selector: 'gallery-grid-photo-loading',
|
||||||
templateUrl: 'app/gallery/grid/photo/loading/loading.photo.grid.gallery.component.html',
|
templateUrl: './loading.photo.grid.gallery.component.html',
|
||||||
styleUrls: ['app/gallery/grid/photo/loading/loading.photo.grid.gallery.component.css'],
|
styleUrls: ['./loading.photo.grid.gallery.component.css'],
|
||||||
})
|
})
|
||||||
export class GalleryPhotoLoadingComponent {
|
export class GalleryPhotoLoadingComponent {
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<div #photoContainer class="photo-container" (mouseover)="hover()" (mouseout)="mouseOut()">
|
<div #photoContainer class="photo-container" (mouseover)="hover()" (mouseout)="mouseOut()">
|
||||||
<img #img [src]="thumbnail.src" [hidden]="!thumbnail.available">
|
<img #img [src]="thumbnail.Src" [hidden]="!thumbnail.Available">
|
||||||
|
|
||||||
<gallery-grid-photo-loading [animate]="thumbnail.loading" *ngIf="!thumbnail.available">
|
<gallery-grid-photo-loading [animate]="thumbnail.loading" *ngIf="!thumbnail.Available">
|
||||||
</gallery-grid-photo-loading>
|
</gallery-grid-photo-loading>
|
||||||
|
|
||||||
<!--Info box -->
|
<!--Info box -->
|
||||||
|
|||||||
@ -8,8 +8,8 @@ import {Config} from "../../../../../common/config/public/Config";
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'gallery-grid-photo',
|
selector: 'gallery-grid-photo',
|
||||||
templateUrl: 'app/gallery/grid/photo/photo.grid.gallery.component.html',
|
templateUrl: './photo.grid.gallery.component.html',
|
||||||
styleUrls: ['app/gallery/grid/photo/photo.grid.gallery.component.css'],
|
styleUrls: ['./photo.grid.gallery.component.css'],
|
||||||
providers: [RouterLink],
|
providers: [RouterLink],
|
||||||
})
|
})
|
||||||
export class GalleryPhotoComponent implements IRenderable, OnInit, OnDestroy {
|
export class GalleryPhotoComponent implements IRenderable, OnInit, OnDestroy {
|
||||||
|
|||||||
@ -17,8 +17,8 @@ import {Subscription} from "rxjs";
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'gallery-lightbox',
|
selector: 'gallery-lightbox',
|
||||||
styleUrls: ['app/gallery/lightbox/lightbox.gallery.component.css'],
|
styleUrls: ['./lightbox.gallery.component.css'],
|
||||||
templateUrl: 'app/gallery/lightbox/lightbox.gallery.component.html',
|
templateUrl: './lightbox.gallery.component.html',
|
||||||
})
|
})
|
||||||
export class GalleryLightboxComponent {
|
export class GalleryLightboxComponent {
|
||||||
@Output('onLastElement') onLastElement = new EventEmitter();
|
@Output('onLastElement') onLastElement = new EventEmitter();
|
||||||
@ -26,19 +26,19 @@ export class GalleryLightboxComponent {
|
|||||||
public navigation = {hasPrev: true, hasNext: true};
|
public navigation = {hasPrev: true, hasNext: true};
|
||||||
public photoDimension: Dimension = <Dimension>{top: 0, left: 0, width: 0, height: 0};
|
public photoDimension: Dimension = <Dimension>{top: 0, left: 0, width: 0, height: 0};
|
||||||
public lightboxDimension: Dimension = <Dimension>{top: 0, left: 0, width: 0, height: 0};
|
public lightboxDimension: Dimension = <Dimension>{top: 0, left: 0, width: 0, height: 0};
|
||||||
private transition: string = "";
|
public transition: string = "";
|
||||||
public blackCanvasOpacity: any = 0;
|
public blackCanvasOpacity: any = 0;
|
||||||
|
|
||||||
private activePhoto: GalleryPhotoComponent;
|
public activePhoto: GalleryPhotoComponent;
|
||||||
private gridPhotoQL: QueryList<GalleryPhotoComponent>;
|
private gridPhotoQL: QueryList<GalleryPhotoComponent>;
|
||||||
|
|
||||||
private visible = false;
|
public visible = false;
|
||||||
private changeSubscription: Subscription = null;
|
private changeSubscription: Subscription = null;
|
||||||
|
|
||||||
@ViewChild("root") elementRef: ElementRef;
|
@ViewChild("root") elementRef: ElementRef;
|
||||||
|
|
||||||
|
|
||||||
constructor(private fullScreenService: FullScreenService, private changeDetector: ChangeDetectorRef, private overlayService: OverlayService) {
|
constructor(public fullScreenService: FullScreenService, private changeDetector: ChangeDetectorRef, private overlayService: OverlayService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -3,8 +3,8 @@ import {GridPhoto} from "../../grid/GridPhoto";
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'gallery-lightbox-photo',
|
selector: 'gallery-lightbox-photo',
|
||||||
styleUrls: ['app/gallery/lightbox/photo/photo.lightbox.gallery.component.css'],
|
styleUrls: ['./photo.lightbox.gallery.component.css'],
|
||||||
templateUrl: 'app/gallery/lightbox/photo/photo.lightbox.gallery.component.html'
|
templateUrl: './photo.lightbox.gallery.component.html'
|
||||||
})
|
})
|
||||||
export class GalleryLightboxPhotoComponent implements OnChanges {
|
export class GalleryLightboxPhotoComponent implements OnChanges {
|
||||||
|
|
||||||
|
|||||||
@ -8,8 +8,8 @@ import {IconPhoto} from "../../IconPhoto";
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'gallery-map-lightbox',
|
selector: 'gallery-map-lightbox',
|
||||||
styleUrls: ['app/gallery/map/lightbox/lightbox.map.gallery.component.css'],
|
styleUrls: ['./lightbox.map.gallery.component.css'],
|
||||||
templateUrl: 'app/gallery/map/lightbox/lightbox.map.gallery.component.html',
|
templateUrl: './lightbox.map.gallery.component.html',
|
||||||
})
|
})
|
||||||
export class GalleryMapLightboxComponent implements OnChanges {
|
export class GalleryMapLightboxComponent implements OnChanges {
|
||||||
|
|
||||||
@ -17,8 +17,8 @@ export class GalleryMapLightboxComponent implements OnChanges {
|
|||||||
private startPosition = null;
|
private startPosition = null;
|
||||||
public lightboxDimension: Dimension = <Dimension>{top: 0, left: 0, width: 0, height: 0};
|
public lightboxDimension: Dimension = <Dimension>{top: 0, left: 0, width: 0, height: 0};
|
||||||
public mapDimension: Dimension = <Dimension>{top: 0, left: 0, width: 0, height: 0};
|
public mapDimension: Dimension = <Dimension>{top: 0, left: 0, width: 0, height: 0};
|
||||||
private visible = false;
|
public visible = false;
|
||||||
private opacity = 1.0;
|
public opacity = 1.0;
|
||||||
mapPhotos: Array<{ latitude: number, longitude: number, iconUrl?: string, thumbnail: IconThumbnail }> = [];
|
mapPhotos: Array<{ latitude: number, longitude: number, iconUrl?: string, thumbnail: IconThumbnail }> = [];
|
||||||
mapCenter = {latitude: 0, longitude: 0};
|
mapCenter = {latitude: 0, longitude: 0};
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ export class GalleryMapLightboxComponent implements OnChanges {
|
|||||||
@ViewChild(AgmMap) map: AgmMap;
|
@ViewChild(AgmMap) map: AgmMap;
|
||||||
|
|
||||||
|
|
||||||
constructor(private fullScreenService: FullScreenService, private thumbnailService: ThumbnailManagerService) {
|
constructor(public fullScreenService: FullScreenService, private thumbnailService: ThumbnailManagerService) {
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import {Component, OnChanges, Input, ViewChild, ElementRef} from "@angular/core";
|
import {Component, ElementRef, Input, OnChanges, ViewChild} from "@angular/core";
|
||||||
import {PhotoDTO} from "../../../../common/entities/PhotoDTO";
|
import {PhotoDTO} from "../../../../common/entities/PhotoDTO";
|
||||||
import {IRenderable, Dimension} from "../../model/IRenderable";
|
import {Dimension, IRenderable} from "../../model/IRenderable";
|
||||||
import {GalleryMapLightboxComponent} from "./lightbox/lightbox.map.gallery.component";
|
import {GalleryMapLightboxComponent} from "./lightbox/lightbox.map.gallery.component";
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'gallery-map',
|
selector: 'gallery-map',
|
||||||
templateUrl: 'app/gallery/map/map.gallery.component.html',
|
templateUrl: './map.gallery.component.html',
|
||||||
styleUrls: ['app/gallery/map/map.gallery.component.css']
|
styleUrls: ['./map.gallery.component.css']
|
||||||
})
|
})
|
||||||
export class GalleryMapComponent implements OnChanges, IRenderable {
|
export class GalleryMapComponent implements OnChanges, IRenderable {
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import {RouterLink} from "@angular/router";
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'gallery-navbar',
|
selector: 'gallery-navbar',
|
||||||
templateUrl: 'app/gallery/navigator/navigator.gallery.component.html',
|
templateUrl: './navigator.gallery.component.html',
|
||||||
providers: [RouterLink],
|
providers: [RouterLink],
|
||||||
})
|
})
|
||||||
export class GalleryNavigatorComponent implements OnChanges {
|
export class GalleryNavigatorComponent implements OnChanges {
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
<form class="navbar-form" role="search" #SearchForm="ngForm">
|
<form class="navbar-form" role="search" #SearchForm="ngForm">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="text" class="form-control" placeholder="Search" (keyup)="onSearchChange($event)"
|
<input type="text" class="form-control" placeholder="Search" (keyup)="onSearchChange($event)"
|
||||||
(blur)="onFocusLost($event)" (focus)="onFocus($evnet)" [(ngModel)]="searchText" #name="ngModel"
|
(blur)="onFocusLost()" (focus)="onFocus()" [(ngModel)]="searchText" #name="ngModel"
|
||||||
ngControl="search"
|
ngControl="search"
|
||||||
name="srch-term" id="srch-term" autocomplete="off">
|
name="srch-term" id="srch-term" autocomplete="off">
|
||||||
|
|
||||||
|
|||||||
@ -8,14 +8,14 @@ import {Config} from "../../../../common/config/public/Config";
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'gallery-search',
|
selector: 'gallery-search',
|
||||||
templateUrl: 'app/gallery/search/search.gallery.component.html',
|
templateUrl: './search.gallery.component.html',
|
||||||
styleUrls: ['app/gallery/search/search.gallery.component.css'],
|
styleUrls: ['./search.gallery.component.css'],
|
||||||
providers: [AutoCompleteService, RouterLink]
|
providers: [AutoCompleteService, RouterLink]
|
||||||
})
|
})
|
||||||
export class GallerySearchComponent {
|
export class GallerySearchComponent {
|
||||||
|
|
||||||
autoCompleteItems: Array<AutoCompleteRenderItem> = [];
|
autoCompleteItems: Array<AutoCompleteRenderItem> = [];
|
||||||
private searchText: string = "";
|
public searchText: string = "";
|
||||||
private cache = {
|
private cache = {
|
||||||
lastAutocomplete: "",
|
lastAutocomplete: "",
|
||||||
lastInstantSearch: ""
|
lastInstantSearch: ""
|
||||||
|
|||||||
@ -8,8 +8,8 @@ import {ErrorCodes} from "../../../common/entities/Error";
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'login',
|
selector: 'login',
|
||||||
templateUrl: 'app/login/login.component.html',
|
templateUrl: './login.component.html',
|
||||||
styleUrls: ['app/login/login.component.css'],
|
styleUrls: ['./login.component.css'],
|
||||||
})
|
})
|
||||||
export class LoginComponent implements OnInit {
|
export class LoginComponent implements OnInit {
|
||||||
loginCredential: LoginCredential;
|
loginCredential: LoginCredential;
|
||||||
|
|||||||
@ -10,6 +10,10 @@ class MockUserService {
|
|||||||
public login(credential: LoginCredential) {
|
public login(credential: LoginCredential) {
|
||||||
return Promise.resolve(new Message<UserDTO>(null, <UserDTO>{name: "testUserName"}))
|
return Promise.resolve(new Message<UserDTO>(null, <UserDTO>{name: "testUserName"}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getSessionUser() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('AuthenticationService', () => {
|
describe('AuthenticationService', () => {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import {Injectable} from "@angular/core";
|
import {Injectable} from "@angular/core";
|
||||||
import {Http, Headers, RequestOptions} from "@angular/http";
|
import {Headers, Http, RequestOptions} from "@angular/http";
|
||||||
import {Message} from "../../../../common/entities/Message";
|
import {Message} from "../../../../common/entities/Message";
|
||||||
import "rxjs/Rx";
|
import "rxjs/Rx";
|
||||||
|
|
||||||
|
|||||||
@ -49,7 +49,8 @@
|
|||||||
aria-hidden="true">×</span></button>
|
aria-hidden="true">×</span></button>
|
||||||
<h4 class="modal-title" id="myModalLabel">Modal title</h4>
|
<h4 class="modal-title" id="myModalLabel">Modal title</h4>
|
||||||
</div>
|
</div>
|
||||||
<form (ngSubmit)="onSubmit()" #NewUserForm="ngForm">
|
|
||||||
|
<form #NewUserForm="ngForm">
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<input type="text" class="form-control" placeholder="Username" autofocus
|
<input type="text" class="form-control" placeholder="Username" autofocus
|
||||||
[(ngModel)]="newUser.name" name="name" required>
|
[(ngModel)]="newUser.name" name="name" required>
|
||||||
|
|||||||
@ -8,15 +8,15 @@ import {UserManagerSettingsService} from "./usermanager.settings.service";
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'settings-usermanager',
|
selector: 'settings-usermanager',
|
||||||
templateUrl: 'app/settings/usermanager/usermanager.settings.component.html',
|
templateUrl: './usermanager.settings.component.html',
|
||||||
styleUrls: ['app/settings/usermanager/usermanager.settings.component.css'],
|
styleUrls: ['./usermanager.settings.component.css'],
|
||||||
providers: [UserManagerSettingsService],
|
providers: [UserManagerSettingsService],
|
||||||
})
|
})
|
||||||
export class UserMangerSettingsComponent implements OnInit {
|
export class UserMangerSettingsComponent implements OnInit {
|
||||||
|
|
||||||
private newUser = <UserDTO>{};
|
public newUser = <UserDTO>{};
|
||||||
private userRoles: Array<any> = [];
|
public userRoles: Array<any> = [];
|
||||||
private users: Array<UserDTO> = [];
|
public users: Array<UserDTO> = [];
|
||||||
|
|
||||||
constructor(private _authService: AuthenticationService, private _router: Router, private _userSettings: UserManagerSettingsService) {
|
constructor(private _authService: AuthenticationService, private _router: Router, private _userSettings: UserManagerSettingsService) {
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
4
frontend/config_inject.ejs
Normal file
4
frontend/config_inject.ejs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
var ServerInject = {
|
||||||
|
user: <%- JSON.stringify(user); %>,
|
||||||
|
ConfigInject: <%- JSON.stringify(clientConfig); %>
|
||||||
|
}
|
||||||
3
frontend/environments/environment.prod.ts
Normal file
3
frontend/environments/environment.prod.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export const environment = {
|
||||||
|
production: true
|
||||||
|
};
|
||||||
8
frontend/environments/environment.ts
Normal file
8
frontend/environments/environment.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// The file contents for the current environment will overwrite these during build.
|
||||||
|
// The build system defaults to the dev environment which uses `environment.ts`, but if you do
|
||||||
|
// `ng build --env=prod` then `environment.prod.ts` will be used instead.
|
||||||
|
// The list of which env maps to which file can be found in `.angular-cli.json`.
|
||||||
|
|
||||||
|
export const environment = {
|
||||||
|
production: false
|
||||||
|
};
|
||||||
@ -1,48 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<base href="/"/>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>PiGallery2</title>
|
|
||||||
<link rel="shortcut icon" href="icon.png">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet"
|
|
||||||
integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7"
|
|
||||||
crossorigin="anonymous">
|
|
||||||
<!-- Polyfill(s) for older browsers -->
|
|
||||||
<script src="node_modules/core-js/client/shim.min.js"></script>
|
|
||||||
|
|
||||||
<script src="node_modules/zone.js/dist/zone.js"></script>
|
|
||||||
<script src="node_modules/reflect-metadata/Reflect.js"></script>
|
|
||||||
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
|
||||||
|
|
||||||
|
|
||||||
<script>
|
|
||||||
var ServerInject = {
|
|
||||||
user: <%- JSON.stringify(user); %>,
|
|
||||||
ConfigInject: <%- JSON.stringify(clientConfig); %>
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script src="systemjs.config.js"></script>
|
|
||||||
<script>
|
|
||||||
System.import('').catch(function (err) {
|
|
||||||
console.error(err);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body style="overflow-y: scroll">
|
|
||||||
<pi-gallery2-app>Loading...</pi-gallery2-app>
|
|
||||||
|
|
||||||
<script
|
|
||||||
src="https://code.jquery.com/jquery-2.2.3.min.js"
|
|
||||||
integrity="sha256-a23g1Nt4dtEYOj7bR+vTu7+T8VP13humZFBJNIYoEJo="
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"
|
|
||||||
integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS"
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
28
frontend/index.html
Normal file
28
frontend/index.html
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<base href="/"/>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>PiGallery2</title>
|
||||||
|
<link rel="shortcut icon" href="assets/icon.png">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet"
|
||||||
|
integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7"
|
||||||
|
crossorigin="anonymous">
|
||||||
|
|
||||||
|
|
||||||
|
<script type="text/javascript" src="config_inject.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body style="overflow-y: scroll">
|
||||||
|
<pi-gallery2-app>Loading...</pi-gallery2-app>
|
||||||
|
|
||||||
|
<script
|
||||||
|
src="https://code.jquery.com/jquery-2.2.3.min.js"
|
||||||
|
integrity="sha256-a23g1Nt4dtEYOj7bR+vTu7+T8VP13humZFBJNIYoEJo="
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"
|
||||||
|
integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
61
frontend/polyfills.ts
Normal file
61
frontend/polyfills.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* This file includes polyfills needed by Angular and is loaded before the app.
|
||||||
|
* You can add your own extra polyfills to this file.
|
||||||
|
*
|
||||||
|
* This file is divided into 2 sections:
|
||||||
|
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
|
||||||
|
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
|
||||||
|
* file.
|
||||||
|
*
|
||||||
|
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
|
||||||
|
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
|
||||||
|
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
|
||||||
|
*
|
||||||
|
* Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
|
||||||
|
*/
|
||||||
|
/***************************************************************************************************
|
||||||
|
* BROWSER POLYFILLS
|
||||||
|
*/
|
||||||
|
/** IE9, IE10 and IE11 requires all of the following polyfills. **/
|
||||||
|
// import 'core-js/es6/symbol';
|
||||||
|
import 'core-js/es6/object';
|
||||||
|
// import 'core-js/es6/function';
|
||||||
|
// import 'core-js/es6/parse-int';
|
||||||
|
// import 'core-js/es6/parse-float';
|
||||||
|
// import 'core-js/es6/number';
|
||||||
|
// import 'core-js/es6/math';
|
||||||
|
// import 'core-js/es6/string';
|
||||||
|
// import 'core-js/es6/date';
|
||||||
|
import 'core-js/es6/array';
|
||||||
|
// import 'core-js/es6/regexp';
|
||||||
|
// import 'core-js/es6/map';
|
||||||
|
// import 'core-js/es6/weak-map';
|
||||||
|
// import 'core-js/es6/set';
|
||||||
|
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
|
||||||
|
// import 'classlist.js'; // Run `npm install --save classlist.js`.
|
||||||
|
/** IE10 and IE11 requires the following to support `@angular/animation`. */
|
||||||
|
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
||||||
|
/** Evergreen browsers require these. **/
|
||||||
|
import "core-js/es6/reflect";
|
||||||
|
import "core-js/es7/reflect";
|
||||||
|
/** ALL Firefox browsers require the following to support `@angular/animation`. **/
|
||||||
|
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
||||||
|
/***************************************************************************************************
|
||||||
|
* Zone JS is required by Angular itself.
|
||||||
|
*/
|
||||||
|
import "zone.js/dist/zone"; // Included with Angular CLI.
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************************************
|
||||||
|
* APPLICATION IMPORTS
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Date, currency, decimal and percent pipes.
|
||||||
|
* Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10
|
||||||
|
*/
|
||||||
|
import 'intl'; // Run `npm install --save intl`.
|
||||||
|
/**
|
||||||
|
* Need to import at least one locale-data with intl.
|
||||||
|
*/
|
||||||
|
// import 'intl/locale-data/jsonp/en';
|
||||||
1
frontend/styles.css
Normal file
1
frontend/styles.css
Normal file
@ -0,0 +1 @@
|
|||||||
|
/* You can add global styles to this file, and also import other style files */
|
||||||
@ -1,11 +0,0 @@
|
|||||||
/**
|
|
||||||
* Add barrels and stuff
|
|
||||||
* Adjust as necessary for your application needs.
|
|
||||||
*/
|
|
||||||
// (function (global) {
|
|
||||||
// System.config({
|
|
||||||
// packages: {
|
|
||||||
// // add packages here
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// })(this);
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
/**
|
|
||||||
* System configuration for Angular samples
|
|
||||||
* Adjust as necessary for your application needs.
|
|
||||||
*/
|
|
||||||
(function (global) {
|
|
||||||
System.config({
|
|
||||||
paths: {
|
|
||||||
// paths serve as alias
|
|
||||||
'npm:': 'node_modules/'
|
|
||||||
},
|
|
||||||
// map tells the System loader where to look for things
|
|
||||||
map: {
|
|
||||||
// our app is within the app folder
|
|
||||||
app: '',
|
|
||||||
|
|
||||||
// angular bundles
|
|
||||||
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
|
|
||||||
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
|
|
||||||
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
|
|
||||||
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
|
|
||||||
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
|
|
||||||
'@angular/http': 'npm:@angular/http/bundles/http.umd.js',
|
|
||||||
'@angular/router': 'npm:@angular/router/bundles/router.umd.js',
|
|
||||||
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
|
|
||||||
|
|
||||||
// other libraries
|
|
||||||
'rxjs': 'npm:rxjs',
|
|
||||||
|
|
||||||
'@agm/core': 'npm:@agm/core/core.umd.js',
|
|
||||||
'ng2-cookies': 'npm:ng2-cookies/ng2-cookies',
|
|
||||||
'typeconfig': 'npm:typeconfig'
|
|
||||||
},
|
|
||||||
// packages tells the System loader how to load when no filename and/or no extension
|
|
||||||
packages: {
|
|
||||||
app: {
|
|
||||||
main: './main.js',
|
|
||||||
defaultExtension: 'js'
|
|
||||||
},
|
|
||||||
rxjs: {
|
|
||||||
defaultExtension: 'js'
|
|
||||||
},
|
|
||||||
"angular2-google-maps/core": {
|
|
||||||
"defaultExtension": "js",
|
|
||||||
"main": "index.js"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})(this);
|
|
||||||
30
frontend/test.ts
Normal file
30
frontend/test.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
||||||
|
|
||||||
|
import "zone.js/dist/long-stack-trace-zone";
|
||||||
|
import "zone.js/dist/proxy.js";
|
||||||
|
import "zone.js/dist/sync-test";
|
||||||
|
import "zone.js/dist/jasmine-patch";
|
||||||
|
import "zone.js/dist/async-test";
|
||||||
|
import "zone.js/dist/fake-async-test";
|
||||||
|
import {getTestBed} from "@angular/core/testing";
|
||||||
|
import {BrowserDynamicTestingModule, platformBrowserDynamicTesting} from "@angular/platform-browser-dynamic/testing";
|
||||||
|
|
||||||
|
// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
|
||||||
|
declare const __karma__: any;
|
||||||
|
declare const require: any;
|
||||||
|
|
||||||
|
// Prevent Karma from running prematurely.
|
||||||
|
__karma__.loaded = function () {
|
||||||
|
};
|
||||||
|
|
||||||
|
// First, initialize the Angular testing environment.
|
||||||
|
getTestBed().initTestEnvironment(
|
||||||
|
BrowserDynamicTestingModule,
|
||||||
|
platformBrowserDynamicTesting()
|
||||||
|
);
|
||||||
|
// Then we find all the tests.
|
||||||
|
const context = require.context('./', true, /\.spec\.ts$/);
|
||||||
|
// And load the modules.
|
||||||
|
context.keys().map(context);
|
||||||
|
// Finally, start Karma to run the tests.
|
||||||
|
__karma__.start();
|
||||||
13
frontend/tsconfig.app.json
Normal file
13
frontend/tsconfig.app.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"extends": "../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../out-tsc/app",
|
||||||
|
"module": "es2015",
|
||||||
|
"baseUrl": "",
|
||||||
|
"types": []
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"test.ts",
|
||||||
|
"**/*.spec.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
20
frontend/tsconfig.spec.json
Normal file
20
frontend/tsconfig.spec.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"extends": "../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../out-tsc/spec",
|
||||||
|
"module": "commonjs",
|
||||||
|
"target": "es5",
|
||||||
|
"baseUrl": "",
|
||||||
|
"types": [
|
||||||
|
"jasmine",
|
||||||
|
"node"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"test.ts"
|
||||||
|
],
|
||||||
|
"include": [
|
||||||
|
"**/*.spec.ts",
|
||||||
|
"**/*.d.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
5
frontend/typings.d.ts
vendored
Normal file
5
frontend/typings.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/* SystemJS module definition */
|
||||||
|
declare var module: NodeModule;
|
||||||
|
interface NodeModule {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
@ -1,98 +0,0 @@
|
|||||||
// /*global jasmine, __karma__, window*/
|
|
||||||
Error.stackTraceLimit = 0; // "No stacktrace"" is usually best for app testing.
|
|
||||||
|
|
||||||
// Uncomment to get full stacktrace output. Sometimes helpful, usually not.
|
|
||||||
// Error.stackTraceLimit = Infinity; //
|
|
||||||
|
|
||||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
|
|
||||||
|
|
||||||
// builtPaths: root paths for output ("built") files
|
|
||||||
// get from karma.config.js, then prefix with '/base/' (default is 'app/')
|
|
||||||
var builtPaths = (__karma__.config.builtPaths || ['app/'])
|
|
||||||
.map(function (p) {
|
|
||||||
return '/base/' + p;
|
|
||||||
});
|
|
||||||
|
|
||||||
__karma__.loaded = function () {
|
|
||||||
};
|
|
||||||
|
|
||||||
function isJsFile(path) {
|
|
||||||
return path.slice(-3) == '.js';
|
|
||||||
}
|
|
||||||
|
|
||||||
function isSpecFile(path) {
|
|
||||||
return /\.spec\.(.*\.)?js$/.test(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is a "built" file if is JavaScript file in one of the "built" folders
|
|
||||||
function isBuiltFile(path) {
|
|
||||||
return isJsFile(path) &&
|
|
||||||
builtPaths.reduce(function (keep, bp) {
|
|
||||||
return keep || (path.substr(0, bp.length) === bp);
|
|
||||||
}, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
var allSpecFiles = Object.keys(window.__karma__.files)
|
|
||||||
.filter(isSpecFile)
|
|
||||||
.filter(isBuiltFile);
|
|
||||||
|
|
||||||
System.config({
|
|
||||||
baseURL: 'base',
|
|
||||||
// Extend usual application package list with test folder
|
|
||||||
packages: {'testing': {main: 'index.js', defaultExtension: 'js'}},
|
|
||||||
|
|
||||||
// Assume npm: is set in `paths` in systemjs.config
|
|
||||||
// Map the angular testing umd bundles
|
|
||||||
map: {
|
|
||||||
'@angular/core/testing': 'npm:@angular/core/bundles/core-testing.umd.js',
|
|
||||||
'@angular/common/testing': 'npm:@angular/common/bundles/common-testing.umd.js',
|
|
||||||
'@angular/compiler/testing': 'npm:@angular/compiler/bundles/compiler-testing.umd.js',
|
|
||||||
'@angular/platform-browser/testing': 'npm:@angular/platform-browser/bundles/platform-browser-testing.umd.js',
|
|
||||||
'@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js',
|
|
||||||
'@angular/http/testing': 'npm:@angular/http/bundles/http-testing.umd.js',
|
|
||||||
'@angular/router/testing': 'npm:@angular/router/bundles/router-testing.umd.js',
|
|
||||||
'@angular/forms/testing': 'npm:@angular/forms/bundles/forms-testing.umd.js',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
System.import('frontend/systemjs.config.js')
|
|
||||||
.then(importSystemJsExtras)
|
|
||||||
.then(initTestBed)
|
|
||||||
.then(initTesting);
|
|
||||||
|
|
||||||
/** Optional SystemJS configuration extras. Keep going w/o it */
|
|
||||||
function importSystemJsExtras() {
|
|
||||||
return System.import('frontend/systemjs.config.extras.js')
|
|
||||||
.catch(function (reason) {
|
|
||||||
console.log(
|
|
||||||
'Warning: System.import could not load the optional "systemjs.config.extras.js". Did you omit it by accident? Continuing without it.'
|
|
||||||
);
|
|
||||||
console.log(reason);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function initTestBed() {
|
|
||||||
return Promise.all([
|
|
||||||
System.import('@angular/core/testing'),
|
|
||||||
System.import('@angular/platform-browser-dynamic/testing')
|
|
||||||
])
|
|
||||||
|
|
||||||
.then(function (providers) {
|
|
||||||
var coreTesting = providers[0];
|
|
||||||
var browserTesting = providers[1];
|
|
||||||
|
|
||||||
coreTesting.TestBed.initTestEnvironment(
|
|
||||||
browserTesting.BrowserDynamicTestingModule,
|
|
||||||
browserTesting.platformBrowserDynamicTesting());
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Import all spec files and start karma
|
|
||||||
function initTesting() {
|
|
||||||
return Promise.all(
|
|
||||||
allSpecFiles.map(function (moduleName) {
|
|
||||||
return System.import(moduleName);
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.then(__karma__.start, __karma__.error);
|
|
||||||
}
|
|
||||||
103
karma.conf.js
103
karma.conf.js
@ -1,106 +1,33 @@
|
|||||||
|
// Karma configuration file, see link for more information
|
||||||
|
// https://karma-runner.github.io/0.13/config/configuration-file.html
|
||||||
|
|
||||||
module.exports = function (config) {
|
module.exports = function (config) {
|
||||||
|
|
||||||
var appBase = 'frontend/'; // transpiled app JS and map files
|
|
||||||
var appSrcBase = 'frontend/'; // app source TS files
|
|
||||||
var commonBase = 'common/'; // transpiled app JS and map files
|
|
||||||
var commonSrcBase = 'common/'; // app source TS files
|
|
||||||
|
|
||||||
var appAssets = 'base/'; // component assets fetched by Angular's compiler
|
|
||||||
|
|
||||||
// Testing helpers (optional) are conventionally in a folder called `testing`
|
|
||||||
var testingBase = 'testing/'; // transpiled test JS and map files
|
|
||||||
var testingSrcBase = 'testing/'; // test source TS files
|
|
||||||
|
|
||||||
config.set({
|
config.set({
|
||||||
basePath: '',
|
basePath: '',
|
||||||
frameworks: ['jasmine'],
|
frameworks: ['jasmine', '@angular/cli'],
|
||||||
|
|
||||||
plugins: [
|
plugins: [
|
||||||
require('karma-jasmine'),
|
require('karma-jasmine'),
|
||||||
require('karma-phantomjs-launcher'),
|
require('karma-phantomjs-launcher'),
|
||||||
require('karma-jasmine-html-reporter')
|
require('karma-jasmine-html-reporter'),
|
||||||
|
require('karma-coverage-istanbul-reporter'),
|
||||||
|
require('@angular/cli/plugins/karma')
|
||||||
],
|
],
|
||||||
|
|
||||||
client:{
|
client:{
|
||||||
builtPaths: [appBase, commonBase, testingBase], // add more spec base paths as needed
|
|
||||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||||
},
|
},
|
||||||
|
coverageIstanbulReporter: {
|
||||||
|
reports: [ 'html', 'lcovonly' ],
|
||||||
files: [
|
fixWebpackSourcePaths: true
|
||||||
|
},
|
||||||
// Polyfills
|
angularCli: {
|
||||||
'node_modules/core-js/client/shim.js',
|
environment: 'dev'
|
||||||
'node_modules/reflect-metadata/Reflect.js',
|
|
||||||
|
|
||||||
// System.js for module loading
|
|
||||||
'node_modules/systemjs/dist/system.js',
|
|
||||||
|
|
||||||
|
|
||||||
// zone.js
|
|
||||||
'node_modules/zone.js/dist/zone.js',
|
|
||||||
'node_modules/zone.js/dist/long-stack-trace-zone.js',
|
|
||||||
'node_modules/zone.js/dist/proxy.js',
|
|
||||||
'node_modules/zone.js/dist/sync-test.js',
|
|
||||||
'node_modules/zone.js/dist/jasmine-patch.js',
|
|
||||||
'node_modules/zone.js/dist/async-test.js',
|
|
||||||
'node_modules/zone.js/dist/fake-async-test.js',
|
|
||||||
|
|
||||||
// RxJs
|
|
||||||
{pattern: 'node_modules/rxjs/**/*.js', included: false, watched: false},
|
|
||||||
{pattern: 'node_modules/rxjs/**/*.js.map', included: false, watched: false},
|
|
||||||
//Other libs
|
|
||||||
{pattern: 'node_modules/ng2-cookies/**/*.js', included: false, watched: false},
|
|
||||||
{pattern: 'node_modules/typeconfig/**/*.js', included: false, watched: false},
|
|
||||||
|
|
||||||
|
|
||||||
// Paths loaded via module imports:
|
|
||||||
// Angular itself
|
|
||||||
{pattern: 'node_modules/@angular/**/*.js', included: false, watched: false},
|
|
||||||
{pattern: 'node_modules/@angular/**/*.js.map', included: false, watched: false},
|
|
||||||
|
|
||||||
{pattern: 'systemjs.config.js', included: false, watched: false},
|
|
||||||
{pattern: 'systemjs.config.extras.js', included: false, watched: false},
|
|
||||||
'karma-test-shim.js', // optionally extend SystemJS mapping e.g., with barrels
|
|
||||||
|
|
||||||
|
|
||||||
// transpiled application & spec code paths loaded via module imports
|
|
||||||
{pattern: appBase + '**/*.js', included: false, watched: true},
|
|
||||||
{pattern: commonBase + '**/*.js', included: false, watched: true},
|
|
||||||
{pattern: testingBase + '**/*.js', included: false, watched: true},
|
|
||||||
|
|
||||||
|
|
||||||
// Asset (HTML & CSS) paths loaded via Angular's component compiler
|
|
||||||
// (these paths need to be rewritten, see proxies section)
|
|
||||||
{pattern: appBase + '**/*.html', included: false, watched: true},
|
|
||||||
{pattern: appBase + '**/*.css', included: false, watched: true},
|
|
||||||
{pattern: commonBase + '**/*.html', included: false, watched: true},
|
|
||||||
{pattern: commonBase + '**/*.css', included: false, watched: true},
|
|
||||||
|
|
||||||
// Paths for debugging with source maps in dev tools
|
|
||||||
{pattern: appSrcBase + '**/*.ts', included: false, watched: false},
|
|
||||||
{pattern: commonSrcBase + '**/*.ts', included: false, watched: false},
|
|
||||||
{pattern: appBase + '**/*.js.map', included: false, watched: false},
|
|
||||||
{pattern: commonBase + '**/*.js.map', included: false, watched: false},
|
|
||||||
{pattern: testingSrcBase + '**/*.ts', included: false, watched: false},
|
|
||||||
{pattern: testingBase + '**/*.js.map', included: false, watched: false}
|
|
||||||
],
|
|
||||||
|
|
||||||
// Proxied base paths for loading assets
|
|
||||||
proxies: {
|
|
||||||
// required for component assets fetched by Angular's compiler
|
|
||||||
"/app/": appAssets
|
|
||||||
},
|
},
|
||||||
|
|
||||||
exclude: [],
|
|
||||||
preprocessors: {},
|
|
||||||
reporters: ['progress', 'kjhtml'],
|
reporters: ['progress', 'kjhtml'],
|
||||||
|
|
||||||
port: 9876,
|
port: 9876,
|
||||||
colors: true,
|
colors: true,
|
||||||
logLevel: config.LOG_INFO,
|
logLevel: config.LOG_INFO,
|
||||||
autoWatch: true,
|
autoWatch: true,
|
||||||
browsers: ['PhantomJS'],
|
browsers: ['PhantomJS'],
|
||||||
singleRun: false
|
singleRun: false
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|||||||
19
package.json
19
package.json
@ -7,10 +7,12 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "./backend/server.js",
|
"main": "./backend/server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "ng build",
|
||||||
"pretest": "tsc",
|
"test": "ng test --single-run && mocha --recursive test/backend/unit",
|
||||||
"test": "karma start karma.conf.js --single-run && mocha --recursive test/backend/unit",
|
"start": "node ./backend/server",
|
||||||
"start": "node ./backend/server"
|
"ng": "ng",
|
||||||
|
"lint": "ng lint",
|
||||||
|
"e2e": "ng e2e"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -37,6 +39,7 @@
|
|||||||
"express-session": "^1.15.3",
|
"express-session": "^1.15.3",
|
||||||
"express-winston": "^2.4.0",
|
"express-winston": "^2.4.0",
|
||||||
"flat-file-db": "^1.0.0",
|
"flat-file-db": "^1.0.0",
|
||||||
|
"intl": "^1.2.5",
|
||||||
"jimp": "^0.2.28",
|
"jimp": "^0.2.28",
|
||||||
"mime": "^1.3.6",
|
"mime": "^1.3.6",
|
||||||
"mocha": "^3.4.2",
|
"mocha": "^3.4.2",
|
||||||
@ -54,6 +57,9 @@
|
|||||||
"zone.js": "^0.8.11"
|
"zone.js": "^0.8.11"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@angular/cli": "1.1.1",
|
||||||
|
"@angular/compiler-cli": "^4.0.0",
|
||||||
|
"@angular/language-service": "^4.0.0",
|
||||||
"@types/express": "^4.0.35",
|
"@types/express": "^4.0.35",
|
||||||
"@types/express-session": "1.15.0",
|
"@types/express-session": "1.15.0",
|
||||||
"@types/jasmine": "^2.5.51",
|
"@types/jasmine": "^2.5.51",
|
||||||
@ -62,12 +68,16 @@
|
|||||||
"@types/sharp": "^0.17.2",
|
"@types/sharp": "^0.17.2",
|
||||||
"@types/winston": "^2.3.3",
|
"@types/winston": "^2.3.3",
|
||||||
"chai": "^4.0.1",
|
"chai": "^4.0.1",
|
||||||
|
"codelyzer": "~3.0.1",
|
||||||
|
"ejs-loader": "^0.3.0",
|
||||||
"gulp": "^3.9.1",
|
"gulp": "^3.9.1",
|
||||||
"gulp-typescript": "^3.1.7",
|
"gulp-typescript": "^3.1.7",
|
||||||
"gulp-zip": "^4.0.0",
|
"gulp-zip": "^4.0.0",
|
||||||
"jasmine-core": "^2.6.2",
|
"jasmine-core": "^2.6.2",
|
||||||
|
"jasmine-spec-reporter": "~4.1.0",
|
||||||
"karma": "^1.7.0",
|
"karma": "^1.7.0",
|
||||||
"karma-cli": "^1.0.1",
|
"karma-cli": "^1.0.1",
|
||||||
|
"karma-coverage-istanbul-reporter": "^1.2.1",
|
||||||
"karma-jasmine": "^1.1.0",
|
"karma-jasmine": "^1.1.0",
|
||||||
"karma-jasmine-html-reporter": "^0.2.2",
|
"karma-jasmine-html-reporter": "^0.2.2",
|
||||||
"karma-phantomjs-launcher": "^1.0.4",
|
"karma-phantomjs-launcher": "^1.0.4",
|
||||||
@ -81,6 +91,7 @@
|
|||||||
"rimraf": "^2.6.1",
|
"rimraf": "^2.6.1",
|
||||||
"run-sequence": "^1.2.2",
|
"run-sequence": "^1.2.2",
|
||||||
"ts-helpers": "^1.1.2",
|
"ts-helpers": "^1.1.2",
|
||||||
|
"ts-node": "~3.0.4",
|
||||||
"tslint": "^5.4.2",
|
"tslint": "^5.4.2",
|
||||||
"typescript": "^2.3.4"
|
"typescript": "^2.3.4"
|
||||||
},
|
},
|
||||||
|
|||||||
28
protractor.conf.js
Normal file
28
protractor.conf.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// Protractor configuration file, see link for more information
|
||||||
|
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||||
|
|
||||||
|
const { SpecReporter } = require('jasmine-spec-reporter');
|
||||||
|
|
||||||
|
exports.config = {
|
||||||
|
allScriptsTimeout: 11000,
|
||||||
|
specs: [
|
||||||
|
'./test/e2e/**/*.e2e-spec.ts'
|
||||||
|
],
|
||||||
|
capabilities: {
|
||||||
|
'browserName': 'chrome'
|
||||||
|
},
|
||||||
|
directConnect: true,
|
||||||
|
baseUrl: 'http://localhost:4200/',
|
||||||
|
framework: 'jasmine',
|
||||||
|
jasmineNodeOpts: {
|
||||||
|
showColors: true,
|
||||||
|
defaultTimeoutInterval: 30000,
|
||||||
|
print: function() {}
|
||||||
|
},
|
||||||
|
onPrepare() {
|
||||||
|
require('ts-node').register({
|
||||||
|
project: 'test/e2e/tsconfig.e2e.json'
|
||||||
|
});
|
||||||
|
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
|
||||||
|
}
|
||||||
|
};
|
||||||
14
test/e2e/app.e2e-spec.ts
Normal file
14
test/e2e/app.e2e-spec.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import {TestProjectPage} from "./app.po";
|
||||||
|
|
||||||
|
describe('test-project App', () => {
|
||||||
|
let page: TestProjectPage;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
page = new TestProjectPage();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display welcome message', () => {
|
||||||
|
page.navigateTo();
|
||||||
|
expect(page.getParagraphText()).toEqual('Welcome to app!!');
|
||||||
|
});
|
||||||
|
});
|
||||||
11
test/e2e/app.po.ts
Normal file
11
test/e2e/app.po.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import {browser, by, element} from "protractor";
|
||||||
|
|
||||||
|
export class TestProjectPage {
|
||||||
|
navigateTo() {
|
||||||
|
return browser.get('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
getParagraphText() {
|
||||||
|
return element(by.css('app-root h1')).getText();
|
||||||
|
}
|
||||||
|
}
|
||||||
12
test/e2e/tsconfig.e2e.json
Normal file
12
test/e2e/tsconfig.e2e.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"extends": "../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../out-tsc/e2e",
|
||||||
|
"module": "commonjs",
|
||||||
|
"target": "es5",
|
||||||
|
"types": [
|
||||||
|
"jasmine",
|
||||||
|
"node"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,50 +0,0 @@
|
|||||||
// @AngularClass
|
|
||||||
/*
|
|
||||||
* When testing with webpack and ES6, we have to do some extra
|
|
||||||
* things get testing to work right. Because we are gonna write test
|
|
||||||
* in ES6 to, we have to compile those as well. That's handled in
|
|
||||||
* karma.conf.js with the karma-webpack plugin. This is the entry
|
|
||||||
* file for webpack test. Just like webpack will create a bundle.js
|
|
||||||
* file for our client, when we run test, it well compile and bundle them
|
|
||||||
* all here! Crazy huh. So we need to do some setup
|
|
||||||
*/
|
|
||||||
Error.stackTraceLimit = Infinity;
|
|
||||||
require('core-js');
|
|
||||||
|
|
||||||
// Typescript emit helpers polyfill
|
|
||||||
require('ts-helpers');
|
|
||||||
|
|
||||||
require('zone.js/dist/zone.js');
|
|
||||||
require('zone.js/dist/long-stack-trace-zone.js');
|
|
||||||
require('zone.js/dist/jasmine-patch.js');
|
|
||||||
require('zone.js/dist/async-test');
|
|
||||||
|
|
||||||
var testing = require('@angular/core/testing');
|
|
||||||
var browser = require('@angular/platform-browser-dynamic/testing');
|
|
||||||
|
|
||||||
testing.setBaseTestProviders(
|
|
||||||
browser.TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS,
|
|
||||||
browser.TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS
|
|
||||||
);
|
|
||||||
|
|
||||||
Object.assign(global, testing);
|
|
||||||
/*
|
|
||||||
Ok, this is kinda crazy. We can use the the context method on
|
|
||||||
require that webpack created in order to tell webpack
|
|
||||||
what files we actually want to require or import.
|
|
||||||
Below, context will be an function/object with file names as keys.
|
|
||||||
using that regex we are saying look in ./src/app and ./test then find
|
|
||||||
any file that ends with spec.js and get its path. By passing in true
|
|
||||||
we say do this recursively
|
|
||||||
*/
|
|
||||||
var testContext = require.context('./../frontend', true, /\.spec\.ts/);
|
|
||||||
|
|
||||||
// get all the files, for each file, call the context function
|
|
||||||
// that will require the file and load it up here. Context will
|
|
||||||
// loop and require those spec files here
|
|
||||||
function requireAll(requireContext) {
|
|
||||||
return requireContext.keys().map(requireContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
var modules = requireAll(testContext);
|
|
||||||
// requires and returns all modules that match
|
|
||||||
@ -1,52 +0,0 @@
|
|||||||
// @AngularClass
|
|
||||||
/*
|
|
||||||
* When testing with webpack and ES6, we have to do some extra
|
|
||||||
* things get testing to work right. Because we are gonna write test
|
|
||||||
* in ES6 to, we have to compile those as well. That's handled in
|
|
||||||
* karma.conf.js with the karma-webpack plugin. This is the entry
|
|
||||||
* file for webpack test. Just like webpack will create a bundle.js
|
|
||||||
* file for our client, when we run test, it well compile and bundle them
|
|
||||||
* all here! Crazy huh. So we need to do some setup
|
|
||||||
*/
|
|
||||||
Error.stackTraceLimit = Infinity;
|
|
||||||
// Prefer CoreJS over the polyfills above
|
|
||||||
require('core-js');
|
|
||||||
|
|
||||||
// Typescript emit helpers polyfill
|
|
||||||
require('ts-helpers');
|
|
||||||
|
|
||||||
require('zone.js/dist/zone.js');
|
|
||||||
require('zone.js/dist/long-stack-trace-zone.js');
|
|
||||||
require('zone.js/dist/jasmine-patch.js');
|
|
||||||
require('zone.js/dist/async-test');
|
|
||||||
|
|
||||||
|
|
||||||
var testing = require('@angular/core/testing');
|
|
||||||
var browser = require('@angular/platform-browser-dynamic/testing');
|
|
||||||
|
|
||||||
testing.setBaseTestProviders(
|
|
||||||
browser.TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS,
|
|
||||||
browser.TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS
|
|
||||||
);
|
|
||||||
|
|
||||||
Object.assign(global, testing);
|
|
||||||
/*
|
|
||||||
Ok, this is kinda crazy. We can use the the context method on
|
|
||||||
require that webpack created in order to tell webpack
|
|
||||||
what files we actually want to require or import.
|
|
||||||
Below, context will be an function/object with file names as keys.
|
|
||||||
using that regex we are saying look in ./src/app and ./test then find
|
|
||||||
any file that ends with spec.js and get its path. By passing in true
|
|
||||||
we say do this recursively
|
|
||||||
*/
|
|
||||||
var testContext = require.context('./../frontend', true, /\.ts/);
|
|
||||||
|
|
||||||
// get all the files, for each file, call the context function
|
|
||||||
// that will require the file and load it up here. Context will
|
|
||||||
// loop and require those spec files here
|
|
||||||
function requireAll(requireContext) {
|
|
||||||
return requireContext.keys().map(requireContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
var modules = requireAll(testContext);
|
|
||||||
// requires and returns all modules that match
|
|
||||||
@ -1,71 +0,0 @@
|
|||||||
{
|
|
||||||
"rulesDirectory": [
|
|
||||||
"node_modules/ng2lint/dist/src"
|
|
||||||
],
|
|
||||||
"rules": {
|
|
||||||
"component-selector-name": [true, "kebab-case"],
|
|
||||||
"component-selector-type": [true, "element"],
|
|
||||||
"host-parameter-decorator": true,
|
|
||||||
"input-parameter-decorator": true,
|
|
||||||
"output-parameter-decorator": true,
|
|
||||||
"attribute-parameter-decorator": false,
|
|
||||||
"input-property-directive": true,
|
|
||||||
"output-property-directive": true,
|
|
||||||
|
|
||||||
"class-name": true,
|
|
||||||
"curly": false,
|
|
||||||
"eofline": true,
|
|
||||||
"indent": [
|
|
||||||
true,
|
|
||||||
"spaces"
|
|
||||||
],
|
|
||||||
"max-line-length": [
|
|
||||||
true,
|
|
||||||
100
|
|
||||||
],
|
|
||||||
"member-ordering": [
|
|
||||||
true,
|
|
||||||
"public-before-private",
|
|
||||||
"static-before-instance",
|
|
||||||
"variables-before-functions"
|
|
||||||
],
|
|
||||||
"experimentalDecorators":true,
|
|
||||||
"no-arg": true,
|
|
||||||
"no-construct": true,
|
|
||||||
"no-duplicate-key": true,
|
|
||||||
"no-duplicate-variable": true,
|
|
||||||
"no-empty": false,
|
|
||||||
"no-eval": true,
|
|
||||||
"trailing-comma": true,
|
|
||||||
"no-trailing-whitespace": true,
|
|
||||||
"no-unused-expression": true,
|
|
||||||
"no-unused-variable": false,
|
|
||||||
"no-unreachable": true,
|
|
||||||
"no-use-before-declare": true,
|
|
||||||
"one-line": [
|
|
||||||
true,
|
|
||||||
"check-open-brace",
|
|
||||||
"check-catch",
|
|
||||||
"check-else",
|
|
||||||
"check-whitespace"
|
|
||||||
],
|
|
||||||
"quotemark": [
|
|
||||||
true,
|
|
||||||
"single"
|
|
||||||
],
|
|
||||||
"semicolon": true,
|
|
||||||
"triple-equals": [
|
|
||||||
true,
|
|
||||||
"allow-null-check"
|
|
||||||
],
|
|
||||||
"variable-name": false,
|
|
||||||
"whitespace": [
|
|
||||||
true,
|
|
||||||
"check-branch",
|
|
||||||
"check-decl",
|
|
||||||
"check-operator",
|
|
||||||
"check-separator",
|
|
||||||
"check-type"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,22 +1,18 @@
|
|||||||
{
|
{
|
||||||
|
"compileOnSave": true,
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es5",
|
|
||||||
"module": "commonjs",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
|
"declaration": false,
|
||||||
|
"moduleResolution": "node",
|
||||||
"emitDecoratorMetadata": true,
|
"emitDecoratorMetadata": true,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"suppressImplicitAnyIndexErrors": false,
|
"target": "es5",
|
||||||
"lib": [
|
|
||||||
"es2015",
|
|
||||||
"dom",
|
|
||||||
"es2015.promise"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"exclude": [
|
|
||||||
"node_modules"
|
|
||||||
],
|
|
||||||
"typeRoots": [
|
"typeRoots": [
|
||||||
"node_modules/@types"
|
"node_modules/@types"
|
||||||
|
],
|
||||||
|
"lib": [
|
||||||
|
"es2016",
|
||||||
|
"dom"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
135
tslint.json
Normal file
135
tslint.json
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
{
|
||||||
|
"rulesDirectory": [
|
||||||
|
"node_modules/codelyzer"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"arrow-return-shorthand": true,
|
||||||
|
"callable-types": true,
|
||||||
|
"class-name": true,
|
||||||
|
"comment-format": [
|
||||||
|
true,
|
||||||
|
"check-space"
|
||||||
|
],
|
||||||
|
"curly": true,
|
||||||
|
"eofline": true,
|
||||||
|
"forin": true,
|
||||||
|
"import-blacklist": [
|
||||||
|
true,
|
||||||
|
"rxjs"
|
||||||
|
],
|
||||||
|
"import-spacing": true,
|
||||||
|
"indent": [
|
||||||
|
true,
|
||||||
|
"spaces"
|
||||||
|
],
|
||||||
|
"interface-over-type-literal": true,
|
||||||
|
"label-position": true,
|
||||||
|
"max-line-length": [
|
||||||
|
true,
|
||||||
|
140
|
||||||
|
],
|
||||||
|
"member-access": false,
|
||||||
|
"member-ordering": [
|
||||||
|
true,
|
||||||
|
"static-before-instance",
|
||||||
|
"variables-before-functions"
|
||||||
|
],
|
||||||
|
"no-arg": true,
|
||||||
|
"no-bitwise": true,
|
||||||
|
"no-console": [
|
||||||
|
true,
|
||||||
|
"debug",
|
||||||
|
"info",
|
||||||
|
"time",
|
||||||
|
"timeEnd",
|
||||||
|
"trace"
|
||||||
|
],
|
||||||
|
"no-construct": true,
|
||||||
|
"no-debugger": true,
|
||||||
|
"no-duplicate-super": true,
|
||||||
|
"no-empty": false,
|
||||||
|
"no-empty-interface": true,
|
||||||
|
"no-eval": true,
|
||||||
|
"no-inferrable-types": [
|
||||||
|
true,
|
||||||
|
"ignore-params"
|
||||||
|
],
|
||||||
|
"no-misused-new": true,
|
||||||
|
"no-non-null-assertion": true,
|
||||||
|
"no-shadowed-variable": true,
|
||||||
|
"no-string-literal": false,
|
||||||
|
"no-string-throw": true,
|
||||||
|
"no-switch-case-fall-through": true,
|
||||||
|
"no-trailing-whitespace": true,
|
||||||
|
"no-unnecessary-initializer": true,
|
||||||
|
"no-unused-expression": true,
|
||||||
|
"no-use-before-declare": true,
|
||||||
|
"no-var-keyword": true,
|
||||||
|
"object-literal-sort-keys": false,
|
||||||
|
"one-line": [
|
||||||
|
true,
|
||||||
|
"check-open-brace",
|
||||||
|
"check-catch",
|
||||||
|
"check-else",
|
||||||
|
"check-whitespace"
|
||||||
|
],
|
||||||
|
"prefer-const": true,
|
||||||
|
"quotemark": [
|
||||||
|
true,
|
||||||
|
"single"
|
||||||
|
],
|
||||||
|
"radix": true,
|
||||||
|
"semicolon": [
|
||||||
|
"always"
|
||||||
|
],
|
||||||
|
"triple-equals": [
|
||||||
|
true,
|
||||||
|
"allow-null-check"
|
||||||
|
],
|
||||||
|
"typedef-whitespace": [
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
"call-signature": "nospace",
|
||||||
|
"index-signature": "nospace",
|
||||||
|
"parameter": "nospace",
|
||||||
|
"property-declaration": "nospace",
|
||||||
|
"variable-declaration": "nospace"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"typeof-compare": true,
|
||||||
|
"unified-signatures": true,
|
||||||
|
"variable-name": false,
|
||||||
|
"whitespace": [
|
||||||
|
true,
|
||||||
|
"check-branch",
|
||||||
|
"check-decl",
|
||||||
|
"check-operator",
|
||||||
|
"check-separator",
|
||||||
|
"check-type"
|
||||||
|
],
|
||||||
|
"directive-selector": [
|
||||||
|
true,
|
||||||
|
"attribute",
|
||||||
|
"app",
|
||||||
|
"camelCase"
|
||||||
|
],
|
||||||
|
"component-selector": [
|
||||||
|
true,
|
||||||
|
"element",
|
||||||
|
"app",
|
||||||
|
"kebab-case"
|
||||||
|
],
|
||||||
|
"use-input-property-decorator": true,
|
||||||
|
"use-output-property-decorator": true,
|
||||||
|
"use-host-property-decorator": true,
|
||||||
|
"no-input-rename": true,
|
||||||
|
"no-output-rename": true,
|
||||||
|
"use-life-cycle-interface": true,
|
||||||
|
"use-pipe-transform-interface": true,
|
||||||
|
"component-class-suffix": true,
|
||||||
|
"directive-class-suffix": true,
|
||||||
|
"no-access-missing-member": true,
|
||||||
|
"templates-use-public": true,
|
||||||
|
"invoke-injectable": true
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user