mirror of
https://github.com/bpatrik/pigallery2.git
synced 2024-12-31 02:29:51 +02:00
implementing basic thumbnail loading
This commit is contained in:
parent
c5e3d09302
commit
08d6dc994e
@ -10,7 +10,7 @@ Config.Server = {
|
||||
port: 80,
|
||||
imagesFolder: "/demo/images",
|
||||
thumbnailFolder: "/demo/TEMP",
|
||||
databaseType: DatabaseType.mongoDB
|
||||
databaseType: DatabaseType.memory
|
||||
};
|
||||
|
||||
ConfigLoader.init(Config, path.join(__dirname, './../../config.json'), [["PORT", "Server-port"]]);
|
||||
|
@ -11,19 +11,11 @@ export class ObjectManagerRepository {
|
||||
private _searchManager:ISearchManager;
|
||||
private static _instance:ObjectManagerRepository = null;
|
||||
|
||||
public static InitMongoManagers() {
|
||||
let MongoGalleryManager = require("./mongoose/MongoGalleryManager");
|
||||
let MongoUserManager = require("./mongoose/MongoUserManager");
|
||||
let MongoSearchManager = require("./mongoose/MongoSearchManager");
|
||||
ObjectManagerRepository.getInstance().setGalleryManager(new MongoGalleryManager());
|
||||
ObjectManagerRepository.getInstance().setUserManager(new MongoUserManager());
|
||||
ObjectManagerRepository.getInstance().setSearchManager(new MongoSearchManager());
|
||||
}
|
||||
|
||||
public static MemoryMongoManagers() {
|
||||
let GalleryManager = require("./memory/GalleryManager");
|
||||
let UserManager = require("./memory/UserManager");
|
||||
let SearchManager = require("./memory/SearchManager");
|
||||
let GalleryManager = require("./memory/GalleryManager").GalleryManager;
|
||||
let UserManager = require("./memory/UserManager").UserManager;
|
||||
let SearchManager = require("./memory/SearchManager").SearchManager;
|
||||
ObjectManagerRepository.getInstance().setGalleryManager(new GalleryManager());
|
||||
ObjectManagerRepository.getInstance().setUserManager(new UserManager());
|
||||
ObjectManagerRepository.getInstance().setSearchManager(new SearchManager());
|
||||
|
@ -1,55 +0,0 @@
|
||||
///<reference path="../../../typings/index.d.ts"/>
|
||||
|
||||
import * as mongoose from "mongoose";
|
||||
import {Schema} from "mongoose";
|
||||
|
||||
export class DatabaseManager {
|
||||
private static _instance:DatabaseManager = null;
|
||||
private connectionError = false;
|
||||
private errorObject = null;
|
||||
private connectionOpen = false;
|
||||
|
||||
constructor() {
|
||||
mongoose.connect('mongodb://localhost/EQZT6L');
|
||||
}
|
||||
|
||||
public static getInstance(onError?:(err)=>void, onConnected?:() =>void) {
|
||||
if (DatabaseManager._instance === null) {
|
||||
DatabaseManager._instance = new DatabaseManager();
|
||||
}
|
||||
return DatabaseManager._instance;
|
||||
}
|
||||
|
||||
public onConnectionError(onError:(err) => void){
|
||||
if (this.connectionError === true) {
|
||||
return onError(DatabaseManager._instance.errorObject);
|
||||
}
|
||||
mongoose.connection.once('error', (err) => {
|
||||
this.connectionError = true;
|
||||
this.errorObject = err;
|
||||
onError(err);
|
||||
});
|
||||
}
|
||||
|
||||
public onConnected(onConnected:() => void){
|
||||
if (this.connectionOpen === true) {
|
||||
return onConnected();
|
||||
}
|
||||
mongoose.connection.once('open', (err) => {
|
||||
this.connectionOpen = true;
|
||||
onConnected();
|
||||
});
|
||||
}
|
||||
|
||||
public getModel(name:string, schema:any) {
|
||||
return mongoose.model(name, new Schema(schema));
|
||||
}
|
||||
|
||||
public disconnect() {
|
||||
mongoose.disconnect();
|
||||
}
|
||||
|
||||
public isConnectionError() {
|
||||
return this.connectionError;
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
import * as path from "path";
|
||||
import {Directory} from "../../../common/entities/Directory";
|
||||
import {IGalleryManager} from "../IGalleryManager";
|
||||
import {DiskManager} from "../DiskManger";
|
||||
import {Utils} from "../../../common/Utils";
|
||||
import {DirectoryModel} from "./entities/DirectoryModel";
|
||||
import {PhotoModel} from "./entities/PhotoModel";
|
||||
import {Photo} from "../../../common/entities/Photo";
|
||||
|
||||
export class MongoGalleryManager implements IGalleryManager {
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
public listDirectory(relativeDirectoryName, cb:(error:any, result:Directory) => void) {
|
||||
let directoryName = path.basename(relativeDirectoryName);
|
||||
let directoryParent = path.join(path.dirname(relativeDirectoryName), "/");
|
||||
|
||||
|
||||
DirectoryModel.findOne({
|
||||
name: directoryName,
|
||||
path: directoryParent
|
||||
}).populate('photos').populate('directories').exec((err, res:any) => {
|
||||
if (err || !res) {
|
||||
return this.indexDirectory(relativeDirectoryName, cb);
|
||||
}
|
||||
return cb(err, this.modelToEntity(res));
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public indexDirectory(relativeDirectoryName, cb:(error:any, result:Directory) => void) {
|
||||
DiskManager.scanDirectory(relativeDirectoryName, (err, scannedDirectory)=> {
|
||||
let arr = [];
|
||||
scannedDirectory.directories.forEach((value) => {
|
||||
let dir = new DirectoryModel(value);
|
||||
Utils.setKeys(dir, value);
|
||||
dir.save();
|
||||
arr.push(dir);
|
||||
});
|
||||
scannedDirectory.directories = arr;
|
||||
arr = [];
|
||||
scannedDirectory.photos.forEach((value) => {
|
||||
let p = new PhotoModel(value);
|
||||
Utils.setKeys(p, value);
|
||||
p.save();
|
||||
arr.push(p);
|
||||
});
|
||||
|
||||
scannedDirectory.photos = arr;
|
||||
DirectoryModel.create(scannedDirectory, (err, savedDir)=> {
|
||||
scannedDirectory.photos.forEach((value:any) => {
|
||||
value['directory'] = savedDir;
|
||||
value.save();
|
||||
});
|
||||
return cb(err, this.modelToEntity(savedDir));
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private modelToEntity(directoryModel:any):Directory {
|
||||
|
||||
let directoryEntity = new Directory(directoryModel._id, directoryModel.name, directoryModel.path, directoryModel.lastupdate, [], []);
|
||||
|
||||
directoryModel.photos.forEach((photo) => {
|
||||
let photoEntity = new Photo(photo._id, photo.name, directoryEntity, photo.metadata);
|
||||
directoryEntity.photos.push(photoEntity);
|
||||
});
|
||||
|
||||
directoryModel.directories.forEach((dir) => {
|
||||
let dirEntity = new Directory(dir._id, dir.name, dir.path, dir.lastupdate, [], []);
|
||||
directoryEntity.directories.push(dirEntity);
|
||||
});
|
||||
|
||||
return directoryEntity;
|
||||
|
||||
}
|
||||
}
|
@ -1,182 +0,0 @@
|
||||
import {AutoCompleteItem, SearchTypes} from "../../../common/entities/AutoCompleteItem";
|
||||
import {ISearchManager} from "../ISearchManager";
|
||||
import {DirectoryModel} from "./entities/DirectoryModel";
|
||||
import {PhotoModel} from "./entities/PhotoModel";
|
||||
import {SearchResult} from "../../../common/entities/SearchResult";
|
||||
|
||||
export class MongoSearchManager implements ISearchManager {
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
autocomplete(text, cb:(error:any, result:Array<AutoCompleteItem>) => void) {
|
||||
|
||||
console.log("autocomplete: " + text);
|
||||
let items:Array<AutoCompleteItem> = [];
|
||||
let promises = [];
|
||||
|
||||
promises.push(
|
||||
PhotoModel.find({name: {$regex: text, $options: "i"}})
|
||||
.limit(10).select('name').exec().then((res:Array<any>)=> {
|
||||
items = items.concat(this.encapsulateAutoComplete(res.map(r => r.name), SearchTypes.image));
|
||||
}));
|
||||
|
||||
promises.push(
|
||||
PhotoModel.find({"metadata.positionData.city": {$regex: text, $options: "i"}})
|
||||
.limit(10).select('metadata.positionData.city').exec().then((res:Array<any>)=> {
|
||||
items = items.concat(this.encapsulateAutoComplete(res.map(r => r.metadata.positionData.city), SearchTypes.position));
|
||||
}));
|
||||
|
||||
promises.push(
|
||||
PhotoModel.find({"metadata.positionData.state": {$regex: text, $options: "i"}})
|
||||
.limit(10).select('metadata.positionData.state').exec().then((res:Array<any>)=> {
|
||||
items = items.concat(this.encapsulateAutoComplete(res.map(r => r.metadata.positionData.state), SearchTypes.position));
|
||||
}));
|
||||
|
||||
promises.push(
|
||||
PhotoModel.find({"metadata.positionData.country": {$regex: text, $options: "i"}})
|
||||
.limit(10).select('metadata.positionData.country').exec().then((res:Array<any>)=> {
|
||||
items = items.concat(this.encapsulateAutoComplete(res.map(r => r.metadata.positionData.country), SearchTypes.position));
|
||||
}));
|
||||
|
||||
//TODO: fix caseinsensitivity
|
||||
promises.push(
|
||||
PhotoModel.find({"metadata.keywords": {$regex: text, $options: "i"}})
|
||||
.limit(10).select('metadata.keywords').exec().then((res:Array<any>)=> {
|
||||
res.forEach((photo)=> {
|
||||
items = items.concat(this.encapsulateAutoComplete(photo.metadata.keywords.filter(k => k.indexOf(text) != -1), SearchTypes.keyword));
|
||||
});
|
||||
}));
|
||||
|
||||
promises.push(
|
||||
DirectoryModel.find({
|
||||
name: {$regex: text, $options: "i"}
|
||||
}).limit(10).select('name').exec().then((res:Array<any>)=> {
|
||||
items = items.concat(this.encapsulateAutoComplete(res.map(r => r.name), SearchTypes.directory));
|
||||
}));
|
||||
|
||||
|
||||
Promise.all(promises).then(()=> {
|
||||
return cb(null, this.autoCompleteItemsUnique(items));
|
||||
}).catch((err)=> {
|
||||
console.error(err);
|
||||
return cb(err, null);
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
search(text:string, searchType:SearchTypes, cb:(error:any, result:SearchResult) => void) {
|
||||
console.log("search: " + text + ", type:" + searchType);
|
||||
let result:SearchResult = new SearchResult();
|
||||
|
||||
let promises = [];
|
||||
let photoFilterOR = [];
|
||||
|
||||
result.searchText = text;
|
||||
result.searchType = searchType;
|
||||
|
||||
|
||||
if (!searchType || searchType === SearchTypes.image) {
|
||||
photoFilterOR.push({name: {$regex: text, $options: "i"}});
|
||||
}
|
||||
if (!searchType || searchType === SearchTypes.position) {
|
||||
photoFilterOR.push({"metadata.positionData.city": {$regex: text, $options: "i"}});
|
||||
photoFilterOR.push({"metadata.positionData.state": {$regex: text, $options: "i"}});
|
||||
photoFilterOR.push({"metadata.positionData.country": {$regex: text, $options: "i"}});
|
||||
}
|
||||
if (!searchType || searchType === SearchTypes.keyword) {
|
||||
photoFilterOR.push({"metadata.keywords": {$regex: text, $options: "i"}});
|
||||
}
|
||||
|
||||
let photoFilter = {};
|
||||
if (photoFilterOR.length == 1) {
|
||||
photoFilter = photoFilterOR[0];
|
||||
} else {
|
||||
photoFilter = {$or: photoFilterOR};
|
||||
}
|
||||
|
||||
if (!searchType || photoFilterOR.length > 0) {
|
||||
promises.push(PhotoModel.find(photoFilter).populate('directory', 'name path').exec().then((res:Array<any>) => {
|
||||
result.photos = res;
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
if (!searchType || searchType === SearchTypes.directory) {
|
||||
promises.push(DirectoryModel.find({
|
||||
name: {
|
||||
$regex: text,
|
||||
$options: "i"
|
||||
}
|
||||
}).exec().then((res:Array<any>) => {
|
||||
result.directories = res;
|
||||
}));
|
||||
}
|
||||
|
||||
Promise.all(promises).then(()=> {
|
||||
return cb(null, result);
|
||||
}).catch((err)=> {
|
||||
console.error(err);
|
||||
return cb(err, null);
|
||||
});
|
||||
}
|
||||
|
||||
instantSearch(text, cb:(error:any, result:SearchResult) => void) {
|
||||
console.log("instantSearch: " + text);
|
||||
let result:SearchResult = new SearchResult();
|
||||
result.searchText = text;
|
||||
PhotoModel.find({
|
||||
$or: [
|
||||
{name: {$regex: text, $options: "i"}},
|
||||
{"metadata.positionData.city": {$regex: text, $options: "i"}},
|
||||
{"metadata.positionData.state": {$regex: text, $options: "i"}},
|
||||
{"metadata.positionData.country": {$regex: text, $options: "i"}},
|
||||
{"metadata.keywords": {$regex: text, $options: "i"}}
|
||||
]
|
||||
|
||||
}).limit(10).populate('directory', 'name path').exec((err, res:Array<any>) => {
|
||||
if (err || !res) {
|
||||
return cb(err, null);
|
||||
}
|
||||
result.photos = res;
|
||||
|
||||
DirectoryModel.find({
|
||||
name: {
|
||||
$regex: text,
|
||||
$options: "i"
|
||||
}
|
||||
}).limit(10).exec((err, res:Array<any>) => {
|
||||
if (err || !res) {
|
||||
return cb(err, null);
|
||||
}
|
||||
result.directories = res;
|
||||
return cb(null, result);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private encapsulateAutoComplete(values:Array<string>, type:SearchTypes) {
|
||||
let res = [];
|
||||
values.forEach((value)=> {
|
||||
res.push(new AutoCompleteItem(value, type));
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
private autoCompleteItemsUnique(array:Array<AutoCompleteItem>) {
|
||||
var a = array.concat();
|
||||
for (var i = 0; i < a.length; ++i) {
|
||||
for (var j = i + 1; j < a.length; ++j) {
|
||||
if (a[i].equals(a[j]))
|
||||
a.splice(j--, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
import {User, UserRoles} from "../../../common/entities/User";
|
||||
import {IUserManager} from "../IUserManager";
|
||||
import {UserModel} from "./entities/UserModel";
|
||||
|
||||
export class MongoUserManager implements IUserManager {
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
public findOne(filter, cb:(error:any, result:User) => void) {
|
||||
return UserModel.findOne(filter, function (err, result:any) {
|
||||
return cb(err, result);
|
||||
});
|
||||
}
|
||||
|
||||
public find(filter, cb:(error:any, result:Array<User>) => void) {
|
||||
UserModel.find(filter, function (err, result:Array<any>) {
|
||||
return cb(err, result);
|
||||
});
|
||||
}
|
||||
|
||||
public createUser(user, cb:(error:any, result:User) => void) {
|
||||
UserModel.create(user, cb);
|
||||
}
|
||||
|
||||
public deleteUser(id:number, cb:(error:any) => void) {
|
||||
UserModel.remove({id: id}, cb);
|
||||
}
|
||||
|
||||
|
||||
public changeRole(id:number, newRole:UserRoles, cb:(error:any, result:string) => void) {
|
||||
return UserModel.update({id: id}, {role: newRole}, function (err) {
|
||||
if (!err) {
|
||||
return cb(err, "ok")
|
||||
}
|
||||
return cb(err, null);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public changePassword(request:any, cb:(error:any, result:string) => void) {
|
||||
throw new Error("not implemented"); //TODO: implement
|
||||
}
|
||||
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
import {DatabaseManager} from "../DatabaseManager";
|
||||
import {Schema} from "mongoose";
|
||||
|
||||
export var DirectoryModel = DatabaseManager.getInstance().getModel('directory', {
|
||||
name: String,
|
||||
path: String,
|
||||
lastUpdate: Date,
|
||||
directories: [{
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'directory'
|
||||
}],
|
||||
photos: [{
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'photo'
|
||||
}]
|
||||
});
|
@ -1,39 +0,0 @@
|
||||
import {DatabaseManager} from "../DatabaseManager";
|
||||
import {Schema} from "mongoose";
|
||||
|
||||
|
||||
export var PhotoModel = DatabaseManager.getInstance().getModel('photo', {
|
||||
name: String,
|
||||
directory: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'directory'
|
||||
},
|
||||
metadata: {
|
||||
keywords: [String],
|
||||
cameraData: {
|
||||
ISO: Number,
|
||||
maker: String,
|
||||
fStop: Number,
|
||||
exposure: Number,
|
||||
focalLength: Number,
|
||||
lens: String
|
||||
},
|
||||
positionData: {
|
||||
GPSData: {
|
||||
latitude: Number,
|
||||
longitude: Number,
|
||||
altitude: Number
|
||||
},
|
||||
country: String,
|
||||
state: String,
|
||||
city: String
|
||||
},
|
||||
size: {
|
||||
width: Number,
|
||||
height: Number
|
||||
},
|
||||
creationDate: Date
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1,6 +0,0 @@
|
||||
import {DatabaseManager} from "../DatabaseManager";
|
||||
export var UserModel = DatabaseManager.getInstance().getModel('user', {
|
||||
name: {type: String, index: {unique: true}},
|
||||
password: String,
|
||||
role: Number
|
||||
});
|
@ -13,7 +13,6 @@ import {ErrorRouter} from "./routes/ErrorRouter";
|
||||
import {SharingRouter} from "./routes/SharingRouter";
|
||||
import {DatabaseType} from "./../common/config/Config";
|
||||
import {ObjectManagerRepository} from "./model/ObjectManagerRepository";
|
||||
import {DatabaseManager} from "./model/mongoose/DatabaseManager";
|
||||
import {Config} from "./config/Config";
|
||||
|
||||
|
||||
@ -61,13 +60,7 @@ export class Server {
|
||||
if (Config.Server.databaseType === DatabaseType.memory) {
|
||||
ObjectManagerRepository.MemoryMongoManagers();
|
||||
} else {
|
||||
ObjectManagerRepository.InitMongoManagers();
|
||||
DatabaseManager.getInstance().onConnectionError(
|
||||
()=> {
|
||||
console.error("MongoDB connection error. Falling back to memory Object Managers");
|
||||
ObjectManagerRepository.MemoryMongoManagers();
|
||||
Config.setDatabaseType(DatabaseType.memory);
|
||||
});
|
||||
throw new Error("not implemented alternative mangers");
|
||||
}
|
||||
|
||||
new PublicRouter(this.app);
|
||||
|
@ -15,6 +15,7 @@ export class Utils {
|
||||
|
||||
url += part + "/";
|
||||
}
|
||||
url = url.replace("//", "/");
|
||||
|
||||
return url.substring(0, url.length - 1);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
export enum DatabaseType{
|
||||
memory, mongoDB
|
||||
memory
|
||||
}
|
||||
|
||||
interface ServerConfig {
|
||||
|
@ -4,7 +4,8 @@ export class Photo {
|
||||
constructor(public id?:number,
|
||||
public name?:string,
|
||||
public directory?:Directory,
|
||||
public metadata?:PhotoMetadata) {
|
||||
public metadata?:PhotoMetadata,
|
||||
public readyThumbnails:Array<number> = []) {
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ import {UserService} from "./model/network/user.service.ts";
|
||||
import {GalleryService} from "./gallery/gallery.service";
|
||||
import {AdminComponent} from "./admin/admin.component";
|
||||
import {NetworkService} from "./model/network/network.service";
|
||||
import {ThumbnailLoaderService} from "./gallery/grid/thumnailLoader.service";
|
||||
|
||||
|
||||
@Component({
|
||||
@ -23,7 +24,8 @@ import {NetworkService} from "./model/network/network.service";
|
||||
NetworkService,
|
||||
UserService,
|
||||
GalleryService,
|
||||
AuthenticationService]
|
||||
AuthenticationService,
|
||||
ThumbnailLoaderService]
|
||||
})
|
||||
@RouteConfig([
|
||||
{
|
||||
|
@ -6,9 +6,24 @@ export class GridPhoto {
|
||||
|
||||
}
|
||||
|
||||
getThumbnailPath() {
|
||||
|
||||
thumbnailLoaded() {
|
||||
if (!this.isThumbnailAvailable()) {
|
||||
this.photo.readyThumbnails.push(this.getThumbnailSize());
|
||||
}
|
||||
}
|
||||
|
||||
getThumbnailSize() {
|
||||
let renderSize = Math.sqrt(this.renderWidth * this.renderHeight);
|
||||
let size = Utils.findClosest(renderSize, Config.Client.thumbnailSizes);
|
||||
return Utils.findClosest(renderSize, Config.Client.thumbnailSizes);
|
||||
}
|
||||
|
||||
isThumbnailAvailable() {
|
||||
return this.photo.readyThumbnails.indexOf(this.getThumbnailSize()) != -1;
|
||||
}
|
||||
|
||||
getThumbnailPath() {
|
||||
let size = this.getThumbnailSize();
|
||||
return Utils.concatUrls("/api/gallery/content/", this.photo.directory.path, this.photo.directory.name, this.photo.name, "thumbnail", size.toString());
|
||||
}
|
||||
|
||||
|
@ -45,3 +45,84 @@ a {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sk-cube-grid {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube {
|
||||
width: 33%;
|
||||
height: 33%;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
float: left;
|
||||
-webkit-animation: sk-cubeGridScaleDelay 4.6s infinite ease-in-out;
|
||||
animation: sk-cubeGridScaleDelay 4.6s infinite ease-in-out;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube1 {
|
||||
-webkit-animation-delay: 2.4s;
|
||||
animation-delay: 2.4s;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube2 {
|
||||
-webkit-animation-delay: 2.6s;
|
||||
animation-delay: 2.6s;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube3 {
|
||||
-webkit-animation-delay: 2.8s;
|
||||
animation-delay: 2.8s;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube4 {
|
||||
-webkit-animation-delay: 2.2s;
|
||||
animation-delay: 2.2s;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube5 {
|
||||
-webkit-animation-delay: 2.4s;
|
||||
animation-delay: 2.4s;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube6 {
|
||||
-webkit-animation-delay: 2.6s;
|
||||
animation-delay: 2.6s;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube7 {
|
||||
-webkit-animation-delay: 2s;
|
||||
animation-delay: 2s;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube8 {
|
||||
-webkit-animation-delay: 2.2s;
|
||||
animation-delay: 2.2s;
|
||||
}
|
||||
|
||||
.sk-cube-grid .sk-cube9 {
|
||||
-webkit-animation-delay: 2.4s;
|
||||
animation-delay: 2.4s;
|
||||
}
|
||||
|
||||
@-webkit-keyframes sk-cubeGridScaleDelay {
|
||||
0%, 70%, 100% {
|
||||
-webkit-transform: scale3D(1, 1, 1);
|
||||
transform: scale3D(1, 1, 1);
|
||||
}
|
||||
35% {
|
||||
-webkit-transform: scale3D(0, 0, 1);
|
||||
transform: scale3D(0, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes sk-cubeGridScaleDelay {
|
||||
0%, 70%, 100% {
|
||||
-webkit-transform: scale3D(1, 1, 1);
|
||||
transform: scale3D(1, 1, 1);
|
||||
}
|
||||
35% {
|
||||
-webkit-transform: scale3D(0, 0, 1);
|
||||
transform: scale3D(0, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,20 @@
|
||||
<div class="photo-container" (mouseover)="hover()" (mouseout)="mouseOut()">
|
||||
<img #image [src]="gridPhoto.getThumbnailPath()">
|
||||
<div class="info" #info [style.margin-top.px]="-infoStyle.height" [style.background]="infoStyle.background">
|
||||
<img #image [src]="imageSrc" [hidden]="!showImage">
|
||||
<div class="sk-cube-grid" *ngIf="!showImage">
|
||||
<div class="sk-cube sk-cube1"></div>
|
||||
<div class="sk-cube sk-cube2"></div>
|
||||
<div class="sk-cube sk-cube3"></div>
|
||||
<div class="sk-cube sk-cube4"></div>
|
||||
<div class="sk-cube sk-cube5"></div>
|
||||
<div class="sk-cube sk-cube6"></div>
|
||||
<div class="sk-cube sk-cube7"></div>
|
||||
<div class="sk-cube sk-cube8"></div>
|
||||
<div class="sk-cube sk-cube9"></div>
|
||||
</div>
|
||||
|
||||
<!--Info box -->
|
||||
<div #info [hidden]="!showImage" class="info" [style.margin-top.px]="-infoStyle.height"
|
||||
[style.background]="infoStyle.background">
|
||||
<div class="photo-name">{{gridPhoto.photo.name}}</div>
|
||||
|
||||
<div class="photo-position" *ngIf="gridPhoto.photo.metadata.positionData">
|
||||
|
@ -1,11 +1,12 @@
|
||||
///<reference path="../../../../browser.d.ts"/>
|
||||
|
||||
import {Component, Input, ElementRef, ViewChild} from "@angular/core";
|
||||
import {Component, Input, ElementRef, ViewChild, OnChanges} from "@angular/core";
|
||||
import {IRenderable, Dimension} from "../../../model/IRenderable";
|
||||
import {GridPhoto} from "../GridPhoto";
|
||||
import {SearchTypes} from "../../../../../common/entities/AutoCompleteItem";
|
||||
import {RouterLink} from "@angular/router-deprecated";
|
||||
import {Config} from "../../../config/Config";
|
||||
import {ThumbnailLoaderService} from "../thumnailLoader.service";
|
||||
|
||||
@Component({
|
||||
selector: 'gallery-grid-photo',
|
||||
@ -13,23 +14,42 @@ import {Config} from "../../../config/Config";
|
||||
styleUrls: ['app/gallery/grid/photo/photo.grid.gallery.component.css'],
|
||||
directives: [RouterLink],
|
||||
})
|
||||
export class GalleryPhotoComponent implements IRenderable {
|
||||
export class GalleryPhotoComponent implements IRenderable, OnChanges {
|
||||
@Input() gridPhoto:GridPhoto;
|
||||
@ViewChild("image") imageRef:ElementRef;
|
||||
@ViewChild("info") infoDiv:ElementRef;
|
||||
|
||||
imageSrc = "#";
|
||||
showImage = false;
|
||||
infoStyle = {
|
||||
height: 0,
|
||||
background: ""
|
||||
};
|
||||
|
||||
SearchTypes:any = [];
|
||||
searchEnabled:boolean = true;
|
||||
|
||||
constructor() {
|
||||
constructor(private thumbnailService:ThumbnailLoaderService) {
|
||||
this.SearchTypes = SearchTypes;
|
||||
this.searchEnabled = Config.Client.Search.searchEnabled;
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
if (this.gridPhoto.isThumbnailAvailable()) {
|
||||
this.imageSrc = this.gridPhoto.getThumbnailPath();
|
||||
// this.showImage = true;
|
||||
} else {
|
||||
this.thumbnailService.loadImage(this.gridPhoto).then(()=> {
|
||||
this.imageSrc = this.gridPhoto.getThumbnailPath();
|
||||
// this.showImage = true;
|
||||
this.gridPhoto.thumbnailLoaded();
|
||||
}).catch((error)=> {
|
||||
console.error("something bad happened");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
getPositionText():string {
|
||||
if (!this.gridPhoto) {
|
||||
return ""
|
||||
|
73
frontend/app/gallery/grid/thumnailLoader.service.ts
Normal file
73
frontend/app/gallery/grid/thumnailLoader.service.ts
Normal file
@ -0,0 +1,73 @@
|
||||
///<reference path="../../../browser.d.ts"/>
|
||||
|
||||
import {Injectable} from "@angular/core";
|
||||
import {GridPhoto} from "./GridPhoto";
|
||||
|
||||
@Injectable()
|
||||
export class ThumbnailLoaderService {
|
||||
|
||||
que:Array<ThumbnailTask> = [];
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
loadImage(gridPhoto:GridPhoto):Promise<void> {
|
||||
console.log("[LOAD IMG]" + gridPhoto.photo.name);
|
||||
return new Promise<void>((resolve:Function, reject:Function)=> {
|
||||
let tmp:ThumbnailTask = null;
|
||||
for (let i = 0; i < this.que.length; i++) {
|
||||
if (this.que[i].src == gridPhoto.getThumbnailPath()) {
|
||||
tmp = this.que[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (tmp != null) {
|
||||
tmp.resolve.push(resolve);
|
||||
tmp.reject.push(reject);
|
||||
} else {
|
||||
this.que.push({src: gridPhoto.getThumbnailPath(), resolve: [resolve], reject: [reject]});
|
||||
}
|
||||
this.run();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
isRunning:boolean = false;
|
||||
|
||||
run() {
|
||||
if (this.que.length === 0 || this.isRunning === true) {
|
||||
return;
|
||||
}
|
||||
this.isRunning = true;
|
||||
let task = this.que.shift();
|
||||
console.log("loadingstarted: " + task.src);
|
||||
|
||||
let curImg = new Image();
|
||||
curImg.src = task.src;
|
||||
curImg.onload = () => {
|
||||
console.log(task.src + "done");
|
||||
task.resolve.forEach((resolve:()=>{}) => {
|
||||
resolve();
|
||||
|
||||
});
|
||||
this.isRunning = false;
|
||||
this.run();
|
||||
};
|
||||
|
||||
curImg.onerror = (error) => {
|
||||
console.error(task.src + "error");
|
||||
task.reject.forEach((reject:(error)=>{}) => {
|
||||
reject(error);
|
||||
});
|
||||
this.isRunning = false;
|
||||
this.run();
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface ThumbnailTask {
|
||||
src:string;
|
||||
resolve:Array<Function>;
|
||||
reject:Array<Function>;
|
||||
}
|
@ -11,7 +11,7 @@ module.exports = {
|
||||
library: ['peer']
|
||||
},
|
||||
// Turn on sourcemaps
|
||||
//devtool: 'source-map',
|
||||
devtool: 'source-map',
|
||||
resolve: {
|
||||
extensions: ['', '.webpack.js', '.web.js', '.ts', '.js'],
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user