You've already forked pigallery2
mirror of
https://github.com/bpatrik/pigallery2.git
synced 2025-09-16 09:16:27 +02:00
adding angular-cli support (causes major refactoring)
This commit is contained in:
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
|
||||
node_modules/
|
||||
pigallery2.zip
|
||||
frontend/app/**/*.js
|
||||
frontend/app/**/*.js.map
|
||||
frontend/main.js
|
||||
frontend/main.js.map
|
||||
frontend/**/*.js
|
||||
frontend/**/*.js.map
|
||||
frontend/dist
|
||||
backend/**/*.js
|
||||
backend/**/*.js.map
|
||||
@@ -14,6 +12,9 @@ common/**/*.js.map
|
||||
test/coverage
|
||||
test/backend/**/*.js
|
||||
test/backend/**/*.js.map
|
||||
test/e2e/**/*.js
|
||||
test/e2e/**/*.js.map
|
||||
demo/TEMP/
|
||||
config.json
|
||||
users.db
|
||||
dist/
|
||||
|
@@ -21,4 +21,4 @@ class ProjectPathClass {
|
||||
}
|
||||
}
|
||||
|
||||
export let ProjectPath = new ProjectPathClass();
|
||||
export const ProjectPath = new ProjectPathClass();
|
||||
|
@@ -5,7 +5,7 @@ import {Message} from "../../common/entities/Message";
|
||||
|
||||
export class RenderingMWs {
|
||||
|
||||
public static renderResult(req:Request, res:Response, next:NextFunction) {
|
||||
public static renderResult(req: Request, res: Response, next: NextFunction) {
|
||||
if (!req.resultPipe)
|
||||
return next();
|
||||
|
||||
@@ -13,7 +13,7 @@ export class RenderingMWs {
|
||||
}
|
||||
|
||||
|
||||
public static renderSessionUser(req:Request, res:Response, next:NextFunction) {
|
||||
public static renderSessionUser(req: Request, res: Response, next: NextFunction) {
|
||||
if (!(req.session.user)) {
|
||||
return next(new Error(ErrorCodes.GENERAL_ERROR));
|
||||
}
|
||||
@@ -23,19 +23,19 @@ export class RenderingMWs {
|
||||
RenderingMWs.renderMessage(res, user);
|
||||
}
|
||||
|
||||
public static renderFile(req:Request, res:Response, next:NextFunction) {
|
||||
public static renderFile(req: Request, res: Response, next: NextFunction) {
|
||||
if (!req.resultPipe)
|
||||
return next();
|
||||
|
||||
return res.sendFile(req.resultPipe);
|
||||
}
|
||||
|
||||
public static renderOK(req:Request, res:Response, next:NextFunction) {
|
||||
public static renderOK(req: Request, res: Response, next: NextFunction) {
|
||||
let message = new Message<string>(null, "ok");
|
||||
res.json(message);
|
||||
}
|
||||
|
||||
public static renderError(err:any, req:Request, res:Response, next:NextFunction):any {
|
||||
public static renderError(err: any, req: Request, res: Response, next: NextFunction): any {
|
||||
if (err instanceof Error) {
|
||||
let message = new Message<any>(err, null);
|
||||
return res.json(message);
|
||||
@@ -44,7 +44,7 @@ export class RenderingMWs {
|
||||
}
|
||||
|
||||
|
||||
protected static renderMessage<T>(res:Response, content:T) {
|
||||
protected static renderMessage<T>(res: Response, content: T) {
|
||||
let message = new Message<T>(null, content);
|
||||
res.json(message);
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@ import {ContentWrapper} from "../../../common/entities/ConentWrapper";
|
||||
import {DirectoryDTO} from "../../../common/entities/DirectoryDTO";
|
||||
import {ProjectPath} from "../../ProjectPath";
|
||||
import {PhotoDTO} from "../../../common/entities/PhotoDTO";
|
||||
import {hardwareRenderer, softwareRenderer} from "./THRenderers";
|
||||
import {hardwareRenderer, RendererInput, softwareRenderer} from "./THRenderers";
|
||||
import {Config} from "../../../common/config/private/Config";
|
||||
|
||||
|
||||
@@ -139,20 +139,35 @@ export class ThumbnailGeneratorMWs {
|
||||
|
||||
this.initPools();
|
||||
//run on other thread
|
||||
pool.send({
|
||||
|
||||
let input = <RendererInput>{
|
||||
imagePath: imagePath,
|
||||
size: size,
|
||||
thPath: thPath,
|
||||
makeSquare: makeSquare,
|
||||
qualityPriority: Config.Server.thumbnail.qualityPriority,
|
||||
__dirname: __dirname,
|
||||
})
|
||||
};
|
||||
if (Config.Server.enableThreading == true) {
|
||||
pool.send(imagePath)
|
||||
.on('done', (out) => {
|
||||
return next(out);
|
||||
}).on('error', (error) => {
|
||||
console.log(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 {
|
||||
|
@@ -1,197 +1,18 @@
|
||||
///<reference path="exif.d.ts"/>
|
||||
import * as path from "path";
|
||||
import {DirectoryDTO} from "../../common/entities/DirectoryDTO";
|
||||
import {
|
||||
CameraMetadata,
|
||||
GPSMetadata,
|
||||
ImageSize,
|
||||
PhotoDTO,
|
||||
PhotoMetadata,
|
||||
PositionMetaData
|
||||
} from "../../common/entities/PhotoDTO";
|
||||
import {ProjectPath} from "../ProjectPath";
|
||||
import {Logger} from "../Logger";
|
||||
import {diskManagerTask, DiskManagerTask} from "./DiskMangerTask";
|
||||
import {Config} from "../../common/config/private/Config";
|
||||
|
||||
const Pool = require('threads').Pool;
|
||||
const pool = new Pool();
|
||||
|
||||
const LOG_TAG = "[DiskManager]";
|
||||
|
||||
interface PoolInput {
|
||||
relativeDirectoryName: string;
|
||||
directoryName: string;
|
||||
directoryParent: string;
|
||||
absoluteDirectoryName: string;
|
||||
}
|
||||
|
||||
pool.run(
|
||||
(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);
|
||||
});
|
||||
|
||||
});
|
||||
pool.run(diskManagerTask);
|
||||
|
||||
export class DiskManager {
|
||||
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 absoluteDirectoryName = path.join(ProjectPath.ImageFolder, relativeDirectoryName);
|
||||
|
||||
pool.send({
|
||||
let input = <DiskManagerTask.PoolInput>{
|
||||
relativeDirectoryName,
|
||||
directoryName,
|
||||
directoryParent,
|
||||
absoluteDirectoryName
|
||||
}).on('done', (error: any, result: DirectoryDTO) => {
|
||||
};
|
||||
|
||||
let done = (error: any, result: DirectoryDTO) => {
|
||||
if (error || !result) {
|
||||
return cb(error, result);
|
||||
}
|
||||
@@ -220,9 +43,22 @@ export class DiskManager {
|
||||
};
|
||||
addDirs(result);
|
||||
return cb(error, result);
|
||||
}).on('error', (error) => {
|
||||
};
|
||||
|
||||
let error = (error) => {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
4
backend/model/exif.d.ts
vendored
4
backend/model/exif.d.ts
vendored
@@ -1,6 +1,6 @@
|
||||
declare module "node-iptc" {
|
||||
|
||||
function e(data):any;
|
||||
function e(data): any;
|
||||
|
||||
module e {
|
||||
}
|
||||
@@ -10,6 +10,6 @@ declare module "node-iptc" {
|
||||
|
||||
|
||||
declare module "exif-parser" {
|
||||
export function create(data):any;
|
||||
export function create(data): any;
|
||||
}
|
||||
|
||||
|
@@ -21,16 +21,21 @@ export class PublicRouter {
|
||||
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('/common', _express.static(_path.resolve(__dirname, './../../common')));
|
||||
|
||||
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);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@@ -70,8 +70,8 @@ export class Utils {
|
||||
});
|
||||
}
|
||||
|
||||
public static enumToArray(EnumType: any): Array<{key: number;value: string;}> {
|
||||
let arr: Array<{key: number;value: string;}> = [];
|
||||
public static enumToArray(EnumType: any): Array<{ key: number; value: string; }> {
|
||||
let arr: Array<{ key: number; value: string; }> = [];
|
||||
for (let enumMember in EnumType) {
|
||||
if (!EnumType.hasOwnProperty(enumMember)) {
|
||||
continue;
|
||||
|
@@ -26,4 +26,5 @@ export interface ServerConfig {
|
||||
imagesFolder: string;
|
||||
thumbnail: ThumbnailConfig;
|
||||
database: DataBaseConfig;
|
||||
enableThreading:boolean;
|
||||
}
|
||||
|
@@ -24,7 +24,8 @@ export class PrivateConfigClass extends PublicConfigClass {
|
||||
database: "pigallery2"
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
enableThreading: true
|
||||
};
|
||||
|
||||
public setDatabaseType(type: DatabaseType) {
|
||||
|
@@ -6,10 +6,10 @@ export enum SearchTypes {
|
||||
}
|
||||
|
||||
export class AutoCompleteItem {
|
||||
constructor(public text:string, public type:SearchTypes) {
|
||||
constructor(public text: string, public type: SearchTypes) {
|
||||
}
|
||||
|
||||
equals(other:AutoCompleteItem) {
|
||||
equals(other: AutoCompleteItem) {
|
||||
return this.text === other.text && this.type === other.type;
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
export class LoginCredential {
|
||||
constructor(public username:string = "", public password:string = "") {
|
||||
constructor(public username: string = "", public password: string = "") {
|
||||
|
||||
}
|
||||
}
|
@@ -1,10 +1,10 @@
|
||||
import {Error} from "./Error";
|
||||
|
||||
export class Message<T> {
|
||||
public error:Error = null;
|
||||
public result:T = null;
|
||||
public error: Error = null;
|
||||
public result: T = null;
|
||||
|
||||
constructor(error:Error, result:T) {
|
||||
constructor(error: Error, result: T) {
|
||||
this.error = error;
|
||||
this.result = result;
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@ import {UserModificationRequest} from "./UserModificationRequest";
|
||||
|
||||
export class PasswordChangeRequest extends UserModificationRequest {
|
||||
|
||||
constructor(id:number, public oldPassword:string, public newPassword:string) {
|
||||
constructor(id: number, public oldPassword: string, public newPassword: string) {
|
||||
super(id);
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
export class UserModificationRequest {
|
||||
constructor(public id:number) {
|
||||
constructor(public id: number) {
|
||||
}
|
||||
}
|
||||
|
@@ -4,16 +4,16 @@ function isFunction(functionToCheck: any) {
|
||||
}
|
||||
|
||||
export class Event<T> {
|
||||
private handlers: {(data?: T): void;}[] = [];
|
||||
private handlers: { (data?: T): void; }[] = [];
|
||||
|
||||
public on(handler: {(data?: T): void}) {
|
||||
public on(handler: { (data?: T): void }) {
|
||||
if (!isFunction(handler)) {
|
||||
throw new Error("Handler is not a function");
|
||||
}
|
||||
this.handlers.push(handler);
|
||||
}
|
||||
|
||||
public off(handler: {(data?: T): void}) {
|
||||
public off(handler: { (data?: T): void }) {
|
||||
this.handlers = this.handlers.filter(h => h !== handler);
|
||||
}
|
||||
|
||||
|
@@ -1,11 +1,11 @@
|
||||
export class Event2Args<T,M> {
|
||||
private handlers: { (data?: T,data2?: M): void; }[] = [];
|
||||
export class Event2Args<T, M> {
|
||||
private handlers: { (data?: T, data2?: M): void; }[] = [];
|
||||
|
||||
public on(handler: { (data?: T,data2?: M): void }) {
|
||||
public on(handler: { (data?: T, data2?: M): void }) {
|
||||
this.handlers.push(handler);
|
||||
}
|
||||
|
||||
public off(handler: { (data?: T,data2?: M): void }) {
|
||||
public off(handler: { (data?: T, data2?: M): void }) {
|
||||
this.handlers = this.handlers.filter(h => h !== handler);
|
||||
}
|
||||
|
||||
@@ -13,9 +13,9 @@ export class Event2Args<T,M> {
|
||||
this.handlers = [];
|
||||
}
|
||||
|
||||
public trigger(data?: T,data2?: M) {
|
||||
public trigger(data?: T, data2?: M) {
|
||||
if (this.handlers) {
|
||||
this.handlers.slice(0).forEach(h => h(data,data2));
|
||||
this.handlers.slice(0).forEach(h => h(data, data2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,24 +1,24 @@
|
||||
export class EventLimit<T> {
|
||||
|
||||
private lastTriggerValue:T = null;
|
||||
private lastTriggerValue: T = null;
|
||||
|
||||
private handlers:Array<EventLimitHandler<T>> = [];
|
||||
private handlers: Array<EventLimitHandler<T>> = [];
|
||||
|
||||
public on(limit:T, handler:{ (data?:T): void }) {
|
||||
public on(limit: T, handler: { (data?: T): void }) {
|
||||
this.handlers.push(new EventLimitHandler(limit, handler));
|
||||
if (this.lastTriggerValue != null) {
|
||||
this.trigger(this.lastTriggerValue);
|
||||
}
|
||||
}
|
||||
|
||||
public onSingle(limit:T, handler:{ (data?:T): void }) {
|
||||
public onSingle(limit: T, handler: { (data?: T): void }) {
|
||||
this.handlers.push(new SingleFireEventLimitHandler(limit, handler));
|
||||
if (this.lastTriggerValue != null) {
|
||||
this.trigger(this.lastTriggerValue);
|
||||
}
|
||||
}
|
||||
|
||||
public off(limit:T, handler:{ (data?:T): void }) {
|
||||
public off(limit: T, handler: { (data?: T): void }) {
|
||||
this.handlers = this.handlers.filter(h => h.handler !== handler && h.limit !== limit);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ export class EventLimit<T> {
|
||||
this.handlers = [];
|
||||
}
|
||||
|
||||
public trigger = (data?:T) => {
|
||||
public trigger = (data?: T) => {
|
||||
if (this.handlers) {
|
||||
this.handlers.slice(0).forEach(h => {
|
||||
if (h.limit <= data && (h.lastTriggerValue < h.limit || h.lastTriggerValue == null)) {
|
||||
@@ -41,30 +41,34 @@ export class EventLimit<T> {
|
||||
}
|
||||
|
||||
class EventLimitHandler<T> {
|
||||
public lastTriggerValue:T = null;
|
||||
constructor(public limit:T, public handler:{ (data?:T): void }) {
|
||||
public lastTriggerValue: T = null;
|
||||
|
||||
constructor(public limit: T, public handler: { (data?: T): void }) {
|
||||
}
|
||||
public fire(data?: T){
|
||||
|
||||
public fire(data?: T) {
|
||||
this.handler(data);
|
||||
}
|
||||
public isValid():boolean{
|
||||
|
||||
public isValid(): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
class SingleFireEventLimitHandler<T> extends EventLimitHandler<T>{
|
||||
class SingleFireEventLimitHandler<T> extends EventLimitHandler<T> {
|
||||
public fired = false;
|
||||
constructor(public limit:T, public handler:{ (data?:T): void }) {
|
||||
super(limit,handler);
|
||||
|
||||
constructor(public limit: T, public handler: { (data?: T): void }) {
|
||||
super(limit, handler);
|
||||
}
|
||||
|
||||
public fire(data?: T){
|
||||
if(this.fired == false) {
|
||||
public fire(data?: T) {
|
||||
if (this.fired == false) {
|
||||
this.handler(data);
|
||||
}
|
||||
this.fired = true
|
||||
}
|
||||
|
||||
public isValid():boolean{
|
||||
public isValid(): boolean {
|
||||
return this.fired === false;
|
||||
}
|
||||
}
|
||||
|
@@ -5,8 +5,8 @@ import {UserRoles} from "../../../common/entities/UserDTO";
|
||||
import {Config} from "../../../common/config/public/Config";
|
||||
@Component({
|
||||
selector: 'admin',
|
||||
templateUrl: 'app/admin/admin.component.html',
|
||||
styleUrls: ['app/admin/admin.component.css']
|
||||
templateUrl: './admin.component.html',
|
||||
styleUrls: ['./admin.component.css']
|
||||
})
|
||||
export class AdminComponent implements OnInit {
|
||||
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 {FormsModule} from "@angular/forms";
|
||||
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 {OverlayService} from "./gallery/overlay.service";
|
||||
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({
|
||||
imports: [
|
||||
@@ -38,9 +49,7 @@ import {Config} from "../../common/config/public/Config";
|
||||
FormsModule,
|
||||
HttpModule,
|
||||
appRoutes,
|
||||
AgmCoreModule.forRoot({
|
||||
apiKey: Config.Client.googleApiKey
|
||||
})
|
||||
AgmCoreModule.forRoot()
|
||||
],
|
||||
declarations: [AppComponent,
|
||||
LoginComponent,
|
||||
@@ -62,6 +71,7 @@ import {Config} from "../../common/config/public/Config";
|
||||
FrameComponent,
|
||||
StringifyRole],
|
||||
providers: [
|
||||
{provide: LAZY_MAPS_API_CONFIG, useClass: GoogleMapsConfig},
|
||||
NetworkService,
|
||||
UserService,
|
||||
GalleryCacheService,
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import {ModuleWithProviders} from "@angular/core";
|
||||
import {Routes, RouterModule} from "@angular/router";
|
||||
import {RouterModule, Routes} from "@angular/router";
|
||||
import {LoginComponent} from "./login/login.component";
|
||||
import {GalleryComponent} from "./gallery/gallery.component";
|
||||
import {AdminComponent} from "./admin/admin.component";
|
||||
|
@@ -8,7 +8,7 @@
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</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 id="navbar" class="collapse navbar-collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
|
@@ -6,16 +6,16 @@ import {Config} from "../../../common/config/public/Config";
|
||||
|
||||
@Component({
|
||||
selector: 'app-frame',
|
||||
templateUrl: 'app/frame/frame.component.html',
|
||||
templateUrl: './frame.component.html',
|
||||
providers: [RouterLink],
|
||||
encapsulation: ViewEncapsulation.Emulated
|
||||
})
|
||||
export class FrameComponent {
|
||||
|
||||
user: UserDTO;
|
||||
authenticationRequired:boolean = false;
|
||||
authenticationRequired: boolean = false;
|
||||
|
||||
constructor(private _authService:AuthenticationService) {
|
||||
constructor(private _authService: AuthenticationService) {
|
||||
this.user = this._authService.getUser();
|
||||
this.authenticationRequired = Config.Client.authenticationRequired;
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@ import {Utils} from "../../../common/Utils";
|
||||
export class IconPhoto {
|
||||
|
||||
|
||||
protected replacementSizeCache: number|boolean = false;
|
||||
protected replacementSizeCache: number | boolean = false;
|
||||
|
||||
constructor(public photo: PhotoDTO) {
|
||||
|
||||
|
@@ -4,10 +4,10 @@
|
||||
|
||||
<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>
|
||||
|
||||
<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">
|
||||
|
||||
</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 {RouterLink} from "@angular/router";
|
||||
import {Utils} from "../../../../common/Utils";
|
||||
@@ -7,11 +7,11 @@ import {Thumbnail, ThumbnailManagerService} from "../thumnailManager.service";
|
||||
|
||||
@Component({
|
||||
selector: 'gallery-directory',
|
||||
templateUrl: 'app/gallery/directory/directory.gallery.component.html',
|
||||
styleUrls: ['app/gallery/directory/directory.gallery.component.css'],
|
||||
templateUrl: './directory.gallery.component.html',
|
||||
styleUrls: ['./directory.gallery.component.css'],
|
||||
providers: [RouterLink],
|
||||
})
|
||||
export class GalleryDirectoryComponent implements OnInit,OnDestroy {
|
||||
export class GalleryDirectoryComponent implements OnInit, OnDestroy {
|
||||
@Input() directory: DirectoryDTO;
|
||||
@ViewChild("dirContainer") container: ElementRef;
|
||||
thumbnail: Thumbnail = null;
|
||||
|
@@ -9,8 +9,8 @@ import {Config} from "../../../common/config/public/Config";
|
||||
|
||||
@Component({
|
||||
selector: 'gallery',
|
||||
templateUrl: 'app/gallery/gallery.component.html',
|
||||
styleUrls: ['app/gallery/gallery.component.css']
|
||||
templateUrl: './gallery.component.html',
|
||||
styleUrls: ['./gallery.component.css']
|
||||
})
|
||||
export class GalleryComponent implements OnInit {
|
||||
|
||||
@@ -19,7 +19,7 @@ export class GalleryComponent implements OnInit {
|
||||
|
||||
public showSearchBar: boolean = true;
|
||||
|
||||
constructor(private _galleryService: GalleryService,
|
||||
constructor(public _galleryService: GalleryService,
|
||||
private _authService: AuthenticationService,
|
||||
private _router: Router,
|
||||
private _route: ActivatedRoute) {
|
||||
@@ -60,8 +60,6 @@ export class GalleryComponent implements OnInit {
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
onLightboxLastElement() {
|
||||
|
@@ -18,7 +18,7 @@ export class GalleryService {
|
||||
this.content = new ContentWrapper();
|
||||
}
|
||||
|
||||
lastRequest: {directory: string} = {
|
||||
lastRequest: { directory: string } = {
|
||||
directory: null
|
||||
};
|
||||
|
||||
|
@@ -4,20 +4,20 @@ export class GridRowBuilder {
|
||||
|
||||
private photoRow: Array<PhotoDTO> = [];
|
||||
|
||||
private photoIndex:number = 0; //index of the last pushed photo to the photoRow
|
||||
private photoIndex: number = 0; //index of the last pushed photo to the photoRow
|
||||
|
||||
|
||||
constructor(private photos: Array<PhotoDTO>, private startIndex: number, private photoMargin: number, private containerWidth: number) {
|
||||
this.photoIndex = startIndex;
|
||||
}
|
||||
|
||||
public addPhotos(number:number) {
|
||||
public addPhotos(number: number) {
|
||||
for (let i = 0; i < number; i++) {
|
||||
this.addPhoto();
|
||||
}
|
||||
}
|
||||
|
||||
public addPhoto():boolean {
|
||||
public addPhoto(): boolean {
|
||||
if (this.photoIndex + 1 > this.photos.length) {
|
||||
return false;
|
||||
}
|
||||
@@ -26,7 +26,7 @@ export class GridRowBuilder {
|
||||
return true;
|
||||
}
|
||||
|
||||
public removePhoto():boolean {
|
||||
public removePhoto(): boolean {
|
||||
if (this.photoIndex - 1 < this.startIndex) {
|
||||
return false;
|
||||
}
|
||||
@@ -39,7 +39,7 @@ export class GridRowBuilder {
|
||||
return this.photoRow;
|
||||
}
|
||||
|
||||
public adjustRowHeightBetween(minHeight:number, maxHeight:number) {
|
||||
public adjustRowHeightBetween(minHeight: number, maxHeight: number) {
|
||||
while (this.calcRowHeight() > maxHeight && this.addPhoto() === true) { //row too high -> add more images
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ export class GridRowBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
public calcRowHeight():number {
|
||||
public calcRowHeight(): number {
|
||||
let width = 0;
|
||||
for (let i = 0; i < this.photoRow.length; i++) {
|
||||
width += ((this.photoRow[i].metadata.size.width) / (this.photoRow[i].metadata.size.height)); //summing up aspect ratios
|
||||
|
@@ -20,8 +20,8 @@ import {Config} from "../../../../common/config/public/Config";
|
||||
|
||||
@Component({
|
||||
selector: 'gallery-grid',
|
||||
templateUrl: 'app/gallery/grid/grid.gallery.component.html',
|
||||
styleUrls: ['app/gallery/grid/grid.gallery.component.css'],
|
||||
templateUrl: './grid.gallery.component.html',
|
||||
styleUrls: ['./grid.gallery.component.css'],
|
||||
})
|
||||
export class GalleryGridComponent implements OnChanges, AfterViewInit {
|
||||
|
||||
@@ -52,9 +52,9 @@ export class GalleryGridComponent implements OnChanges, AfterViewInit {
|
||||
this.updateContainerWidth();
|
||||
this.sortPhotos();
|
||||
this.mergeNewPhotos();
|
||||
setImmediate(() => {
|
||||
setTimeout(() => {
|
||||
this.renderPhotos();
|
||||
});
|
||||
}, 0);
|
||||
}
|
||||
|
||||
@HostListener('window:resize')
|
||||
@@ -82,9 +82,9 @@ export class GalleryGridComponent implements OnChanges, AfterViewInit {
|
||||
this.updateContainerWidth();
|
||||
this.sortPhotos();
|
||||
this.clearRenderedPhotos();
|
||||
setImmediate(() => {
|
||||
setTimeout(() => {
|
||||
this.renderPhotos();
|
||||
});
|
||||
}, 0);
|
||||
this.isAfterViewInit = true;
|
||||
}
|
||||
|
||||
|
@@ -2,12 +2,12 @@ import {Component, Input} from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: 'gallery-grid-photo-loading',
|
||||
templateUrl: 'app/gallery/grid/photo/loading/loading.photo.grid.gallery.component.html',
|
||||
styleUrls: ['app/gallery/grid/photo/loading/loading.photo.grid.gallery.component.css'],
|
||||
templateUrl: './loading.photo.grid.gallery.component.html',
|
||||
styleUrls: ['./loading.photo.grid.gallery.component.css'],
|
||||
})
|
||||
export class GalleryPhotoLoadingComponent {
|
||||
|
||||
@Input() animate:boolean;
|
||||
@Input() animate: boolean;
|
||||
|
||||
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<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>
|
||||
|
||||
<!--Info box -->
|
||||
|
@@ -8,8 +8,8 @@ import {Config} from "../../../../../common/config/public/Config";
|
||||
|
||||
@Component({
|
||||
selector: 'gallery-grid-photo',
|
||||
templateUrl: 'app/gallery/grid/photo/photo.grid.gallery.component.html',
|
||||
styleUrls: ['app/gallery/grid/photo/photo.grid.gallery.component.css'],
|
||||
templateUrl: './photo.grid.gallery.component.html',
|
||||
styleUrls: ['./photo.grid.gallery.component.css'],
|
||||
providers: [RouterLink],
|
||||
})
|
||||
export class GalleryPhotoComponent implements IRenderable, OnInit, OnDestroy {
|
||||
|
@@ -17,8 +17,8 @@ import {Subscription} from "rxjs";
|
||||
|
||||
@Component({
|
||||
selector: 'gallery-lightbox',
|
||||
styleUrls: ['app/gallery/lightbox/lightbox.gallery.component.css'],
|
||||
templateUrl: 'app/gallery/lightbox/lightbox.gallery.component.html',
|
||||
styleUrls: ['./lightbox.gallery.component.css'],
|
||||
templateUrl: './lightbox.gallery.component.html',
|
||||
})
|
||||
export class GalleryLightboxComponent {
|
||||
@Output('onLastElement') onLastElement = new EventEmitter();
|
||||
@@ -26,19 +26,19 @@ export class GalleryLightboxComponent {
|
||||
public navigation = {hasPrev: true, hasNext: true};
|
||||
public photoDimension: 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;
|
||||
|
||||
private activePhoto: GalleryPhotoComponent;
|
||||
public activePhoto: GalleryPhotoComponent;
|
||||
private gridPhotoQL: QueryList<GalleryPhotoComponent>;
|
||||
|
||||
private visible = false;
|
||||
public visible = false;
|
||||
private changeSubscription: Subscription = null;
|
||||
|
||||
@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({
|
||||
selector: 'gallery-lightbox-photo',
|
||||
styleUrls: ['app/gallery/lightbox/photo/photo.lightbox.gallery.component.css'],
|
||||
templateUrl: 'app/gallery/lightbox/photo/photo.lightbox.gallery.component.html'
|
||||
styleUrls: ['./photo.lightbox.gallery.component.css'],
|
||||
templateUrl: './photo.lightbox.gallery.component.html'
|
||||
})
|
||||
export class GalleryLightboxPhotoComponent implements OnChanges {
|
||||
|
||||
|
@@ -8,8 +8,8 @@ import {IconPhoto} from "../../IconPhoto";
|
||||
|
||||
@Component({
|
||||
selector: 'gallery-map-lightbox',
|
||||
styleUrls: ['app/gallery/map/lightbox/lightbox.map.gallery.component.css'],
|
||||
templateUrl: 'app/gallery/map/lightbox/lightbox.map.gallery.component.html',
|
||||
styleUrls: ['./lightbox.map.gallery.component.css'],
|
||||
templateUrl: './lightbox.map.gallery.component.html',
|
||||
})
|
||||
export class GalleryMapLightboxComponent implements OnChanges {
|
||||
|
||||
@@ -17,9 +17,9 @@ export class GalleryMapLightboxComponent implements OnChanges {
|
||||
private startPosition = null;
|
||||
public lightboxDimension: 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;
|
||||
private opacity = 1.0;
|
||||
mapPhotos: Array<{latitude: number, longitude: number, iconUrl?: string, thumbnail: IconThumbnail}> = [];
|
||||
public visible = false;
|
||||
public opacity = 1.0;
|
||||
mapPhotos: Array<{ latitude: number, longitude: number, iconUrl?: string, thumbnail: IconThumbnail }> = [];
|
||||
mapCenter = {latitude: 0, longitude: 0};
|
||||
|
||||
@ViewChild("root") elementRef: ElementRef;
|
||||
@@ -27,7 +27,7 @@ export class GalleryMapLightboxComponent implements OnChanges {
|
||||
@ViewChild(AgmMap) map: AgmMap;
|
||||
|
||||
|
||||
constructor(private fullScreenService: FullScreenService, private thumbnailService: ThumbnailManagerService) {
|
||||
constructor(public fullScreenService: FullScreenService, private thumbnailService: ThumbnailManagerService) {
|
||||
|
||||
|
||||
}
|
||||
@@ -95,7 +95,7 @@ export class GalleryMapLightboxComponent implements OnChanges {
|
||||
return p.metadata && p.metadata.positionData && p.metadata.positionData.GPSData;
|
||||
}).map(p => {
|
||||
let th = this.thumbnailService.getIcon(new IconPhoto(p));
|
||||
let obj: {latitude: number, longitude: number, iconUrl?: string, thumbnail: IconThumbnail} = {
|
||||
let obj: { latitude: number, longitude: number, iconUrl?: string, thumbnail: IconThumbnail } = {
|
||||
latitude: p.metadata.positionData.GPSData.latitude,
|
||||
longitude: p.metadata.positionData.GPSData.longitude,
|
||||
thumbnail: th
|
||||
|
@@ -1,18 +1,18 @@
|
||||
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 {IRenderable, Dimension} from "../../model/IRenderable";
|
||||
import {Dimension, IRenderable} from "../../model/IRenderable";
|
||||
import {GalleryMapLightboxComponent} from "./lightbox/lightbox.map.gallery.component";
|
||||
@Component({
|
||||
selector: 'gallery-map',
|
||||
templateUrl: 'app/gallery/map/map.gallery.component.html',
|
||||
styleUrls: ['app/gallery/map/map.gallery.component.css']
|
||||
templateUrl: './map.gallery.component.html',
|
||||
styleUrls: ['./map.gallery.component.css']
|
||||
})
|
||||
export class GalleryMapComponent implements OnChanges, IRenderable {
|
||||
|
||||
@Input() photos: Array<PhotoDTO>;
|
||||
@ViewChild(GalleryMapLightboxComponent) mapLightbox: GalleryMapLightboxComponent;
|
||||
|
||||
mapPhotos: Array<{latitude: number, longitude: number}> = [];
|
||||
mapPhotos: Array<{ latitude: number, longitude: number }> = [];
|
||||
mapCenter = {latitude: 0, longitude: 0};
|
||||
@ViewChild("map") map: ElementRef;
|
||||
|
||||
|
@@ -4,7 +4,7 @@ import {RouterLink} from "@angular/router";
|
||||
|
||||
@Component({
|
||||
selector: 'gallery-navbar',
|
||||
templateUrl: 'app/gallery/navigator/navigator.gallery.component.html',
|
||||
templateUrl: './navigator.gallery.component.html',
|
||||
providers: [RouterLink],
|
||||
})
|
||||
export class GalleryNavigatorComponent implements OnChanges {
|
||||
|
@@ -7,10 +7,10 @@ import {Message} from "../../../../common/entities/Message";
|
||||
export class AutoCompleteService {
|
||||
|
||||
|
||||
constructor(private _networkService:NetworkService) {
|
||||
constructor(private _networkService: NetworkService) {
|
||||
}
|
||||
|
||||
public autoComplete(text:string):Promise<Message<Array<AutoCompleteItem> >> {
|
||||
public autoComplete(text: string): Promise<Message<Array<AutoCompleteItem>>> {
|
||||
return this._networkService.getJson("/autocomplete/" + text);
|
||||
}
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<form class="navbar-form" role="search" #SearchForm="ngForm">
|
||||
<div class="input-group">
|
||||
<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"
|
||||
name="srch-term" id="srch-term" autocomplete="off">
|
||||
|
||||
|
@@ -8,14 +8,14 @@ import {Config} from "../../../../common/config/public/Config";
|
||||
|
||||
@Component({
|
||||
selector: 'gallery-search',
|
||||
templateUrl: 'app/gallery/search/search.gallery.component.html',
|
||||
styleUrls: ['app/gallery/search/search.gallery.component.css'],
|
||||
templateUrl: './search.gallery.component.html',
|
||||
styleUrls: ['./search.gallery.component.css'],
|
||||
providers: [AutoCompleteService, RouterLink]
|
||||
})
|
||||
export class GallerySearchComponent {
|
||||
|
||||
autoCompleteItems: Array<AutoCompleteRenderItem> = [];
|
||||
private searchText: string = "";
|
||||
public searchText: string = "";
|
||||
private cache = {
|
||||
lastAutocomplete: "",
|
||||
lastInstantSearch: ""
|
||||
|
@@ -8,8 +8,8 @@ import {ErrorCodes} from "../../../common/entities/Error";
|
||||
|
||||
@Component({
|
||||
selector: 'login',
|
||||
templateUrl: 'app/login/login.component.html',
|
||||
styleUrls: ['app/login/login.component.css'],
|
||||
templateUrl: './login.component.html',
|
||||
styleUrls: ['./login.component.css'],
|
||||
})
|
||||
export class LoginComponent implements OnInit {
|
||||
loginCredential: LoginCredential;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
export interface IRenderable {
|
||||
getDimension():Dimension;
|
||||
getDimension(): Dimension;
|
||||
}
|
||||
|
||||
export interface Dimension {
|
||||
|
@@ -10,6 +10,10 @@ class MockUserService {
|
||||
public login(credential: LoginCredential) {
|
||||
return Promise.resolve(new Message<UserDTO>(null, <UserDTO>{name: "testUserName"}))
|
||||
}
|
||||
|
||||
public async getSessionUser() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
describe('AuthenticationService', () => {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
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 "rxjs/Rx";
|
||||
|
||||
@@ -8,10 +8,10 @@ export class NetworkService {
|
||||
|
||||
_baseUrl = "/api";
|
||||
|
||||
constructor(protected _http:Http) {
|
||||
constructor(protected _http: Http) {
|
||||
}
|
||||
|
||||
private callJson<T>(method:string, url:string, data:any = {}):Promise<T> {
|
||||
private callJson<T>(method: string, url: string, data: any = {}): Promise<T> {
|
||||
let body = JSON.stringify(data);
|
||||
let headers = new Headers({'Content-Type': 'application/json'});
|
||||
let options = new RequestOptions({headers: headers});
|
||||
@@ -29,24 +29,24 @@ export class NetworkService {
|
||||
.catch(NetworkService.handleError);
|
||||
}
|
||||
|
||||
public postJson<T>(url:string, data:any = {}):Promise<T> {
|
||||
public postJson<T>(url: string, data: any = {}): Promise<T> {
|
||||
return this.callJson("post", url, data);
|
||||
}
|
||||
|
||||
public putJson<T>(url:string, data:any = {}):Promise<T> {
|
||||
public putJson<T>(url: string, data: any = {}): Promise<T> {
|
||||
return this.callJson("put", url, data);
|
||||
}
|
||||
|
||||
public getJson<T>(url:string):Promise<T> {
|
||||
public getJson<T>(url: string): Promise<T> {
|
||||
return this.callJson("get", url);
|
||||
}
|
||||
|
||||
|
||||
public deleteJson<T>(url:string):Promise<T> {
|
||||
public deleteJson<T>(url: string): Promise<T> {
|
||||
return this.callJson("delete", url);
|
||||
}
|
||||
|
||||
private static handleError(error:any) {
|
||||
private static handleError(error: any) {
|
||||
// TODO: in a real world app do smthing better
|
||||
// instead of just logging it to the console
|
||||
console.error(error);
|
||||
|
@@ -8,19 +8,19 @@ export class NotificationService {
|
||||
|
||||
}
|
||||
|
||||
public showException(message:string) {
|
||||
public showException(message: string) {
|
||||
|
||||
}
|
||||
|
||||
public showError(message:string) {
|
||||
public showError(message: string) {
|
||||
|
||||
}
|
||||
|
||||
public showWarn(message:string) {
|
||||
public showWarn(message: string) {
|
||||
|
||||
}
|
||||
|
||||
public showInfo(message:string) {
|
||||
public showInfo(message: string) {
|
||||
|
||||
}
|
||||
|
||||
|
@@ -4,7 +4,7 @@ import {UserRoles} from "../../../common/entities/UserDTO";
|
||||
|
||||
@Pipe({name: 'stringifyRole'})
|
||||
export class StringifyRole implements PipeTransform {
|
||||
transform(role:string):number {
|
||||
transform(role: string): number {
|
||||
return UserRoles[role];
|
||||
}
|
||||
}
|
||||
|
@@ -49,7 +49,8 @@
|
||||
aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title" id="myModalLabel">Modal title</h4>
|
||||
</div>
|
||||
<form (ngSubmit)="onSubmit()" #NewUserForm="ngForm">
|
||||
|
||||
<form #NewUserForm="ngForm">
|
||||
<div class="modal-body">
|
||||
<input type="text" class="form-control" placeholder="Username" autofocus
|
||||
[(ngModel)]="newUser.name" name="name" required>
|
||||
|
@@ -8,15 +8,15 @@ import {UserManagerSettingsService} from "./usermanager.settings.service";
|
||||
|
||||
@Component({
|
||||
selector: 'settings-usermanager',
|
||||
templateUrl: 'app/settings/usermanager/usermanager.settings.component.html',
|
||||
styleUrls: ['app/settings/usermanager/usermanager.settings.component.css'],
|
||||
templateUrl: './usermanager.settings.component.html',
|
||||
styleUrls: ['./usermanager.settings.component.css'],
|
||||
providers: [UserManagerSettingsService],
|
||||
})
|
||||
export class UserMangerSettingsComponent implements OnInit {
|
||||
|
||||
private newUser = <UserDTO>{};
|
||||
private userRoles: Array<any> = [];
|
||||
private users: Array<UserDTO> = [];
|
||||
public newUser = <UserDTO>{};
|
||||
public userRoles: Array<any> = [];
|
||||
public users: Array<UserDTO> = [];
|
||||
|
||||
constructor(private _authService: AuthenticationService, private _router: Router, private _userSettings: UserManagerSettingsService) {
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@ import {Message} from "../../../../common/entities/Message";
|
||||
export class UserManagerSettingsService {
|
||||
|
||||
|
||||
constructor(private _networkService:NetworkService) {
|
||||
constructor(private _networkService: NetworkService) {
|
||||
}
|
||||
|
||||
public createUser(user: UserDTO): Promise<Message<string>> {
|
||||
|
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);
|
||||
}
|
105
karma.conf.js
105
karma.conf.js
@@ -1,106 +1,33 @@
|
||||
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
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/0.13/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine'],
|
||||
|
||||
frameworks: ['jasmine', '@angular/cli'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
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: {
|
||||
builtPaths: [appBase, commonBase, testingBase], // add more spec base paths as needed
|
||||
client:{
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
|
||||
|
||||
files: [
|
||||
|
||||
// Polyfills
|
||||
'node_modules/core-js/client/shim.js',
|
||||
'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
|
||||
coverageIstanbulReporter: {
|
||||
reports: [ 'html', 'lcovonly' ],
|
||||
fixWebpackSourcePaths: true
|
||||
},
|
||||
angularCli: {
|
||||
environment: 'dev'
|
||||
},
|
||||
|
||||
exclude: [],
|
||||
preprocessors: {},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['PhantomJS'],
|
||||
singleRun: false
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
19
package.json
19
package.json
@@ -7,10 +7,12 @@
|
||||
"license": "MIT",
|
||||
"main": "./backend/server.js",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"pretest": "tsc",
|
||||
"test": "karma start karma.conf.js --single-run && mocha --recursive test/backend/unit",
|
||||
"start": "node ./backend/server"
|
||||
"build": "ng build",
|
||||
"test": "ng test --single-run && mocha --recursive test/backend/unit",
|
||||
"start": "node ./backend/server",
|
||||
"ng": "ng",
|
||||
"lint": "ng lint",
|
||||
"e2e": "ng e2e"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -37,6 +39,7 @@
|
||||
"express-session": "^1.15.3",
|
||||
"express-winston": "^2.4.0",
|
||||
"flat-file-db": "^1.0.0",
|
||||
"intl": "^1.2.5",
|
||||
"jimp": "^0.2.28",
|
||||
"mime": "^1.3.6",
|
||||
"mocha": "^3.4.2",
|
||||
@@ -54,6 +57,9 @@
|
||||
"zone.js": "^0.8.11"
|
||||
},
|
||||
"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-session": "1.15.0",
|
||||
"@types/jasmine": "^2.5.51",
|
||||
@@ -62,12 +68,16 @@
|
||||
"@types/sharp": "^0.17.2",
|
||||
"@types/winston": "^2.3.3",
|
||||
"chai": "^4.0.1",
|
||||
"codelyzer": "~3.0.1",
|
||||
"ejs-loader": "^0.3.0",
|
||||
"gulp": "^3.9.1",
|
||||
"gulp-typescript": "^3.1.7",
|
||||
"gulp-zip": "^4.0.0",
|
||||
"jasmine-core": "^2.6.2",
|
||||
"jasmine-spec-reporter": "~4.1.0",
|
||||
"karma": "^1.7.0",
|
||||
"karma-cli": "^1.0.1",
|
||||
"karma-coverage-istanbul-reporter": "^1.2.1",
|
||||
"karma-jasmine": "^1.1.0",
|
||||
"karma-jasmine-html-reporter": "^0.2.2",
|
||||
"karma-phantomjs-launcher": "^1.0.4",
|
||||
@@ -81,6 +91,7 @@
|
||||
"rimraf": "^2.6.1",
|
||||
"run-sequence": "^1.2.2",
|
||||
"ts-helpers": "^1.1.2",
|
||||
"ts-node": "~3.0.4",
|
||||
"tslint": "^5.4.2",
|
||||
"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": {
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"declaration": false,
|
||||
"moduleResolution": "node",
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"suppressImplicitAnyIndexErrors": false,
|
||||
"lib": [
|
||||
"es2015",
|
||||
"dom",
|
||||
"es2015.promise"
|
||||
]
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
],
|
||||
"target": "es5",
|
||||
"typeRoots": [
|
||||
"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
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user