You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
319 lines
9.4 KiB
TypeScript
319 lines
9.4 KiB
TypeScript
let path = require('path')
|
|
let fs = require('fs');
|
|
const shell = require('electron').shell;
|
|
let fileExtensionToImage: object;
|
|
|
|
export enum ConfigPaths {
|
|
ApplicationConfigName = "dashboard.json",
|
|
UserConfigName = "user.json",
|
|
FileExtensionsName = "file-extensions.json"
|
|
}
|
|
|
|
export module Configurator {
|
|
|
|
export function getFileExtensionToImageMap(): Object {
|
|
if (!fileExtensionToImage) {
|
|
fileExtensionToImage = this.loadAppConfig(ConfigPaths.FileExtensionsName);
|
|
}
|
|
return fileExtensionToImage;
|
|
}
|
|
|
|
export function loadAppConfig(fileName: string): object {
|
|
let filePath = FileUtils.getPathToConfig(fileName);
|
|
let fileBuffer = fs.readFileSync(filePath);
|
|
let content = fileBuffer.toString('utf8')
|
|
return JSON.parse(content)
|
|
}
|
|
|
|
export function loadUserConfig(fileName: string): object {
|
|
let userConfigPath = FileUtils.getPathToUserDir(fileName);
|
|
if (!fs.existsSync(userConfigPath)) {
|
|
return buildDefaultConfig();
|
|
} else {
|
|
let content = fs.readFileSync(userConfigPath);
|
|
return JSON.parse(content);
|
|
}
|
|
}
|
|
|
|
export function saveUserConfig(fileName: string, obj: any) {
|
|
let userConfigPath = FileUtils.getPathToUserDir(fileName);
|
|
let content = JSON.stringify(obj);
|
|
fs.writeFile(userConfigPath, content, (err) => {
|
|
if (err) {
|
|
console.log(err);
|
|
throw err;
|
|
}
|
|
});
|
|
}
|
|
|
|
function buildDefaultConfig(): object {
|
|
let userConfig = {};
|
|
userConfig['theme'] = "theme-dark";
|
|
saveUserConfig(ConfigPaths.UserConfigName, userConfig)
|
|
return userConfig
|
|
}
|
|
|
|
}
|
|
|
|
export module FileUtils {
|
|
export function getPathToView(templateName: string): string {
|
|
return path.join(getPathToAssets(), "views", templateName + ".mustache")
|
|
}
|
|
export function getPathToPage(pageName: string): string {
|
|
return path.join(getPathToAssets(), "pages", pageName)
|
|
}
|
|
|
|
export function getPathToImage(imageName: string): string {
|
|
return path.join(getPathToAssets(), "images", imageName)
|
|
}
|
|
|
|
export function getPathToDocument(documentName: string): string {
|
|
return path.join(getPathToAssets(), "documents", documentName)
|
|
}
|
|
|
|
export function getPathToConfig(documentName: string): string {
|
|
return path.join(getPathToAssets(), "conf", documentName)
|
|
}
|
|
|
|
export function getPathToAssets(): string {
|
|
return path.join(__dirname, "..", "assets");
|
|
}
|
|
|
|
export function getFileExtension(fileName: string): string {
|
|
return path.extname(fileName);
|
|
}
|
|
|
|
export function getPathToUserDir(fileName: string): string {
|
|
let dirPath = path.join(__dirname, "..", "user");
|
|
let filePath = path.join(dirPath, fileName);
|
|
touchDir(dirPath);
|
|
return filePath;
|
|
}
|
|
|
|
export function touchDir(dirPath: string) {
|
|
if (!fs.existsSync(dirPath)) {
|
|
fs.mkdirSync(dirPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
export class DocumentDirectory {
|
|
/**
|
|
* you *must* provide an absolute file path here
|
|
*/
|
|
constructor(filePath: string) {
|
|
this.root = new DirectoryNode(filePath, null);
|
|
}
|
|
|
|
root: DirectoryNode;
|
|
|
|
getCards(): Map<string, object[]> {
|
|
return DocumentDirectory.walkCards(this.root);
|
|
}
|
|
|
|
private static walkCards(d: DirectoryNode): Map<string, object[]> {
|
|
let cardsByCategory = new Map<string, object[]>();
|
|
for (let child of d.children) {
|
|
if (child instanceof DirectoryNode) {
|
|
let dir = child as DirectoryNode;
|
|
if (dir.containsDirectory()) {
|
|
let childDirectories = dir.getDirectories();
|
|
for (let dir of childDirectories) {
|
|
let subCards = DocumentDirectory.walkCards(dir);
|
|
this.mergeMaps(cardsByCategory, subCards);
|
|
}
|
|
}
|
|
let sCards = dir.getDocuments();
|
|
let category = dir.getCategory();
|
|
for (let sCard of sCards) {
|
|
let cards = cardsByCategory.get(category);
|
|
if (cards === undefined || cards === null) {
|
|
cards = [];
|
|
}
|
|
cards.push(sCard.toCard());
|
|
cardsByCategory.set(category, cards);
|
|
}
|
|
} else {
|
|
let category = (child.parent as DirectoryNode).getCategory();
|
|
let cards = cardsByCategory.get(category);
|
|
if (cards === undefined || cards === null) {
|
|
cards = [];
|
|
}
|
|
cards.push(child.toCard());
|
|
cardsByCategory.set(category, cards);
|
|
}
|
|
}
|
|
|
|
return cardsByCategory;
|
|
}
|
|
|
|
private static mergeMaps(a: Map<string, Object[]>, b: Map<string, Object[]>) {
|
|
let keys = Object.keys(b);
|
|
for (let key of keys) {
|
|
let sa = a.get(key)
|
|
if (sa === undefined || sa === null) {
|
|
sa = [];
|
|
}
|
|
let sb = b.get(key)
|
|
if (sb === undefined) {
|
|
sb = [];
|
|
}
|
|
sa.push(...sb);
|
|
a[key] = sa;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
export class FileNode {
|
|
|
|
filePath: string;
|
|
parent: FileNode;
|
|
|
|
constructor(filePath: string, parent: FileNode) {
|
|
this.filePath = filePath;
|
|
this.parent = parent;
|
|
}
|
|
|
|
static loadAltProps(path: string): object {
|
|
let props = {};
|
|
let jsonPath = path + ".json"
|
|
if (fs.existsSync(jsonPath)) {
|
|
let raw = fs.readFileSync(jsonPath);
|
|
props = JSON.parse(raw.toString());
|
|
}
|
|
return props
|
|
}
|
|
|
|
private static getImagePathFromDocumentName(name: string): string {
|
|
let ext = FileUtils.getFileExtension(name);
|
|
let thumbnail = Configurator.getFileExtensionToImageMap()[ext];
|
|
return thumbnail;
|
|
}
|
|
|
|
open() {
|
|
shell.openItem(this.filePath);
|
|
}
|
|
|
|
show() {
|
|
shell.showItemInFolder(this.filePath);
|
|
}
|
|
|
|
toCard(): object {
|
|
let altProps = FileNode.loadAltProps(this.filePath);
|
|
let imageName = FileNode.getImagePathFromDocumentName(this.filePath);
|
|
let cardObj = {
|
|
"title": this.getTitle(),
|
|
"description": "",
|
|
"imagePath": imageName,
|
|
"urlText": this.filePath,
|
|
"altText": "",
|
|
"fileCard": true,
|
|
"open": this.open
|
|
}
|
|
|
|
let altKeys = Object.keys(altProps);
|
|
for (let key of altKeys) {
|
|
cardObj[key] = altProps[key]
|
|
}
|
|
|
|
return cardObj
|
|
}
|
|
|
|
getTitle(): string {
|
|
let name = path.basename(this.filePath);
|
|
let ext = path.extname(name);
|
|
let cName = name.replace(ext, "");
|
|
cName = cName.replace(/[\-._]/ig, " ")
|
|
return `${cName} (${ext.substr(1).toUpperCase()})`
|
|
}
|
|
|
|
isDescriptor() {
|
|
|
|
}
|
|
|
|
static compare(a: FileNode, b: FileNode): number {
|
|
return a.filePath.localeCompare(b.filePath);
|
|
}
|
|
}
|
|
|
|
export class DirectoryNode extends FileNode {
|
|
children: FileNode[] = [];
|
|
|
|
constructor(filePath: string, parent: FileNode) {
|
|
super(filePath, parent);
|
|
if (fs.existsSync(filePath)) {
|
|
let stats = fs.lstatSync(filePath);
|
|
if (stats.isDirectory()) {
|
|
let contents = fs.readdirSync(filePath);
|
|
for (let fileName of contents) {
|
|
if (path.extname(fileName) === ".json") continue;
|
|
let childPath = path.join(filePath, fileName);
|
|
let childStats = fs.lstatSync(childPath);
|
|
if (childStats.isDirectory()) {
|
|
this.children.push(new DirectoryNode(childPath, this));
|
|
} else {
|
|
this.children.push(new DocumentNode(childPath, this));
|
|
}
|
|
}
|
|
} else {
|
|
throw new DirectoryIsActuallyFileError(filePath);
|
|
}
|
|
} else {
|
|
throw new DirectoryDoesNotExistError(filePath);
|
|
}
|
|
}
|
|
|
|
getDocuments(): DocumentNode[] {
|
|
return this.children.filter((x) => {
|
|
return x instanceof DocumentNode
|
|
}) as DocumentNode[]
|
|
}
|
|
|
|
getDirectories(): DirectoryNode[] {
|
|
return this.children.filter((x) => {
|
|
return x instanceof DirectoryNode
|
|
}) as DirectoryNode[]
|
|
}
|
|
|
|
containsDirectory(): Boolean {
|
|
for (let child of this.children) {
|
|
if (child instanceof DirectoryNode) return true;
|
|
}
|
|
return false
|
|
}
|
|
|
|
getCategory(): string {
|
|
let rawName = path.basename(this.filePath);
|
|
let parts = rawName.split("-");
|
|
for (let i = 0, l = parts.length; i < l; i++) {
|
|
let part = parts[i].split('');
|
|
part[0] = part[0].toUpperCase();
|
|
parts[i] = part.join('')
|
|
}
|
|
return parts.join(" ")
|
|
}
|
|
}
|
|
|
|
|
|
export class DocumentNode extends FileNode {
|
|
constructor(filePath: string, parent: FileNode) {
|
|
super(filePath, parent);
|
|
}
|
|
|
|
}
|
|
|
|
export class DirectoryIsActuallyFileError extends Error {
|
|
constructor(filePath: string) {
|
|
super(`attempted to scan non-directory: ${filePath}`);
|
|
}
|
|
}
|
|
|
|
export class DirectoryDoesNotExistError extends Error {
|
|
constructor(filePath: string) {
|
|
super(`attempted to load document directory for non-existing directory: ${filePath}`);
|
|
}
|
|
}
|
|
|