L3PX32UXE2SC4JK3VXLL654DHBZGVXSBLZRQIGUIB4RIXEVHU5IQC
TW5LVJJLFOTPD7UWZ42ZAW6OCAAQ6FUWIZT42R4W7CPFVXMGIG7QC
MCPXLE6LIQRLQA2OBQSVIH4O3HAEZZWKPQMEDERPBHN6IVOAVXVAC
UWXG66NAOSBY6SU4IWCOAKXZDUVAHG4WCTX3W53LXV5QJURTWVPQC
TYCBCSGHUAHVPEH5E3LW7KURDC5W4CZ73WSBVRTOWTBSKUNPATUQC
QX7XVNQYOTBDULUCLFRGSATHL7KWBSEV6QWNFY353S7ZIR6UGJWQC
EUMM2RHPFO5XQ6WMZO7PPRQK6A5WVOUTSPP4FSEP4EQWBXJYZN4QC
R6Y5EKVY5CYGP3RONJ6MKTUBHU4QDHALEYPEPQN25LTACKIGBDVAC
UW4WSPWD7TVQWURMTLRNW6LKVFLUVQT7NBUP5A4WMAIVHSLUPH7AC
NLSNNB46NE4J26V72TP27OHEZFUFG5REZ5N72FDWDNB5K4TAZQIAC
EZHOZLJCJNWS7KMYAFTMVOL54XDQNZQJ4O6E6OKWCBXPHDOMATVQC
DJCUBG53FVIUI6NVFWTBW36JEINQABLIK5FMB3VNS3MYFDXCKMDQC
A4YDN3ZM5CNM43BMK4BAEB6I2GVQQZUKBCZYMYCRVZSS6EXRY3VAC
MOBNZ54BFOL54ETIXQM6NXVFEAXCRMPLPHNHBTNKRRKLSYMUTMBAC
const path = require('path')
const async = require('async')
const resolvePkg = require('resolve-pkg')
const Theme = require('./theme')
exports.build = function(rootAlbum, opts, callback) {
// create the base layer assets
// such as shared JS libs, common handlebars helpers, CSS reset...
const baseDir = path.join(__dirname, 'theme-base')
const base = new Theme(baseDir, opts.output, {
stylesheetName: 'core.css'
})
// then create the actual theme assets
const themeDir = opts.themePath || localThemePath(opts.theme)
const theme = new Theme(themeDir, opts.output, {
stylesheetName: 'theme.css',
customStylesPath: opts.themeStyle
})
// and finally render each page
const gallery = galleryModel(rootAlbum, opts)
const tasks = createRenderingTasks(theme, rootAlbum, gallery, [])
// now build everything
async.series([
next => base.prepare(next),
next => theme.prepare(next),
next => async.parallel(tasks, next)
], callback)
}
function galleryModel(rootAlbum, opts) {
return {
home: rootAlbum,
title: opts.title,
footer: opts.footer,
thumbSize: opts.thumbSize,
largeSize: opts.largeSize,
googleAnalytics: opts.googleAnalytics,
settings: opts.settings // NEW for application settings, see opitons.js
}
}
function createRenderingTasks(theme, album, gallery, breadcrumbs) {
// a function to render this album
const thisAlbumTask = next => {
theme.render(album, {
gallery: gallery,
breadcrumbs: breadcrumbs
}, next)
}
const tasks = [thisAlbumTask]
// and all nested albums
album.albums.forEach(function(nested) {
const nestedTasks = createRenderingTasks(theme, nested, gallery, breadcrumbs.concat([album]))
Array.prototype.push.apply(tasks, nestedTasks)
})
return tasks
}
function localThemePath(themeName) {
const local = resolvePkg(`@thumbsup/theme-${themeName}`, { cwd: __dirname })
if (!local) {
throw new Error(`Could not find a built-in theme called ${themeName}`)
}
return local
}
const messages = require('./messages')
const path = require('path')
const yargs = require('yargs')
const os = require('os')
const OPTIONS = {
// ------------------------------------
// Required arguments
// ------------------------------------
'input': {
group: 'Required:',
description: 'Path to the folder with all photos/videos',
normalize: true,
demand: true
},
'output': {
group: 'Required:',
description: 'Output path for the static website',
normalize: true,
demand: true
},
// ------------------------------------
// Input options
// ------------------------------------
'include-photos': {
group: 'Input options:',
description: 'Include photos in the gallery',
type: 'boolean',
'default': true
},
'include-videos': {
group: 'Input options:',
description: 'Include videos in the gallery',
type: 'boolean',
'default': true
},
'include-raw-photos': {
group: 'Input options:',
description: 'Include raw photos in the gallery',
type: 'boolean',
'default': false
},
'include': {
group: 'Input options:',
description: 'Glob pattern of files to include',
type: 'array'
},
'exclude': {
group: 'Input options:',
description: 'Glob pattern of files to exclude',
type: 'array'
},
// ------------------------------------
// Output options
// ------------------------------------
'thumb-size': {
group: 'Output options:',
description: 'Pixel size of the square thumbnails',
type: 'number',
'default': 120
},
'large-size': {
group: 'Output options:',
description: 'Pixel height of the fullscreen photos',
type: 'number',
'default': 1000
},
'photo-quality': {
group: 'Output options:',
description: 'Quality of the resized/converted photos',
type: 'number',
'default': 90
},
'video-quality': {
group: 'Output options:',
description: 'Quality of the converted video (percent)',
type: 'number',
'default': 75
},
'video-bitrate': {
group: 'Output options:',
description: 'Bitrate of the converted videos (e.g. 120k)',
type: 'string',
'default': null
},
'video-format': {
group: 'Output options:',
description: 'Video output format',
choices: ['mp4', 'webm'],
'default': 'mp4'
},
'photo-preview': {
group: 'Output options:',
description: 'How lightbox photos are generated',
choices: ['resize', 'copy', 'symlink', 'link'],
'default': 'resize'
},
'video-preview': {
group: 'Output options:',
description: 'How lightbox videos are generated',
choices: ['resize', 'copy', 'symlink', 'link'],
'default': 'resize'
},
'photo-download': {
group: 'Output options:',
description: 'How downloadable photos are generated',
choices: ['resize', 'copy', 'symlink', 'link'],
'default': 'resize'
},
'video-download': {
group: 'Output options:',
description: 'How downloadable videos are generated',
choices: ['resize', 'copy', 'symlink', 'link'],
'default': 'resize'
},
'link-prefix': {
group: 'Output options:',
description: 'Path or URL prefix for "linked" photos and videos',
type: 'string'
},
'cleanup': {
group: 'Output options:',
description: 'Remove any output file that\'s no longer needed',
type: 'boolean',
'default': false
},
'concurrency': {
group: 'Output options:',
description: 'Number of parallel parsing/processing operations',
type: 'number',
'default': os.cpus().length
},
'output-structure': {
group: 'Output options:',
description: 'File and folder structure for output media',
choices: ['folders', 'suffix'],
'default': 'folders'
},
'gm-args': {
group: 'Output options:',
description: 'Custom image processing arguments for GraphicsMagick',
type: 'array'
},
'watermark': {
group: 'Output options:',
description: 'Path to a transparent PNG to be overlaid on all images',
type: 'string'
},
'watermark-position': {
group: 'Output options:',
description: 'Position of the watermark',
choices: [
'Repeat', 'Center', 'NorthWest', 'North', 'NorthEast',
'West', 'East', 'SouthWest', 'South', 'SouthEast'
]
},
// ------------------------------------
// Album options
// ------------------------------------
'albums-from': {
group: 'Album options:',
description: 'How files are grouped into albums',
type: 'array',
'default': ['%path']
},
'sort-albums-by': {
group: 'Album options:',
description: 'How to sort albums',
choices: ['title', 'start-date', 'end-date'],
'default': 'start-date'
},
'sort-albums-direction': {
group: 'Album options:',
description: 'Album sorting direction',
choices: ['asc', 'desc'],
'default': 'asc'
},
'sort-media-by': {
group: 'Album options:',
description: 'How to sort photos and videos',
choices: ['filename', 'date'],
'default': 'date'
},
'sort-media-direction': {
group: 'Album options:',
description: 'Media sorting direction',
choices: ['asc', 'desc'],
'default': 'asc'
},
'album-zip-files': {
group: 'Album options:',
description: 'Create a ZIP file per album',
type: 'boolean',
'default': false
},
// ------------------------------------
// Website options
// ------------------------------------
'index': {
group: 'Website options:',
description: 'Filename of the home page',
'default': 'index.html'
},
'albums-output-folder': {
group: 'Website options:',
description: 'Output subfolder for HTML albums (default: website root)',
'default': '.'
},
'theme': {
group: 'Website options:',
description: 'Name of a built-in gallery theme',
choices: ['classic', 'cards', 'mosaic', 'flow'],
'default': 'classic'
},
'theme-path': {
group: 'Website options:',
description: 'Path to a custom theme',
normalize: true
},
'theme-style': {
group: 'Website options:',
description: 'Path to a custom LESS/CSS file for additional styles',
normalize: true
},
'title': {
group: 'Website options:',
description: 'Website title',
'default': 'Photo album'
},
'footer': {
group: 'Website options:',
description: 'Text or HTML footer',
'default': null
},
'google-analytics': {
group: 'Website options:',
description: 'Code for Google Analytics tracking',
type: 'string'
},
'embed-exif': {
group: 'Website options:',
description: 'Embed the exif metadata for each image into the gallery page',
type: 'boolean',
'default': false
},
// ------------------------------------
// Misc options
// ------------------------------------
'config': {
group: 'Misc options:',
description: 'JSON config file (one key per argument)',
normalize: true
},
'log': {
group: 'Misc options:',
description: 'Print a detailed text log',
choices: [null, 'info', 'debug', 'trace'],
'default': null
},
'usage-stats': {
group: 'Misc options:',
description: 'Enable anonymous usage statistics',
type: 'boolean',
'default': true
},
'dry-run': {
group: 'Misc options:',
description: "Update the index, but don't create the media files / website",
type: 'boolean',
'default': false
},
// ------------------------------------
// Applciation settings ! added by PS
// ------------------------------------
'settings': {
group: 'Application settings:',
description: 'JSON config file (one key per argument)',
'default': null
},
// ------------------------------------
// Deprecated options
// ------------------------------------
'original-photos': {
group: 'Deprecated:',
description: 'Copy and allow download of full-size photos',
type: 'boolean'
},
'original-videos': {
group: 'Deprecated:',
description: 'Copy and allow download of full-size videos',
type: 'boolean'
},
'albums-date-format': {
group: 'Deprecated:',
description: 'How albums are named in <date> mode [moment.js pattern]'
},
'css': {
group: 'Deprecated:',
description: 'Path to a custom provided CSS/LESS file for styling',
normalize: true
},
'download-photos': {
group: 'Deprecated:',
description: 'Target of the photo download links',
choices: ['large', 'copy', 'symlink', 'link']
},
'download-videos': {
group: 'Deprecated:',
description: 'Target of the video download links',
choices: ['large', 'copy', 'symlink', 'link']
},
'download-link-prefix': {
group: 'Deprecated:',
description: 'Path or URL prefix for linked downloads',
type: 'string'
}
}
// explicitly pass <process.argv> so we can unit test this logic
// otherwise it pre-loads all process arguments on require()
exports.get = (args) => {
const opts = yargs(args)
.usage(messages.USAGE())
.wrap(null)
.help('help')
.config('config')
.options(OPTIONS)
.epilogue(messages.CONFIG_USAGE())
.argv
// Warn users when they use deprecated options
const deprecated = Object.keys(OPTIONS).filter(name => OPTIONS[name].group === 'Deprecated:')
const specified = deprecated.filter(name => typeof opts[name] !== 'undefined')
if (specified.length > 0) {
const warnings = specified.map(name => `Warning: --${name} is deprecated`)
console.error(warnings.join('\n') + '\n')
}
// Make input/output folder absolute paths
opts['input'] = path.resolve(opts['input'])
opts['output'] = path.resolve(opts['output'])
// By default, use relative links to the input folder
if (opts['download-link-prefix']) opts['link-prefix'] = opts['download-link-prefix']
if (!opts['link-prefix']) {
opts['link-prefix'] = path.relative(opts['output'], opts['input'])
}
// Convert deprecated --download
if (opts['original-photos']) opts['download-photos'] = 'copy'
if (opts['original-videos']) opts['download-videos'] = 'copy'
if (opts['download-photos']) opts['photo-download'] = opts['download-photos']
if (opts['download-videos']) opts['video-download'] = opts['download-videos']
if (opts['photo-download'] === 'large') opts['photo-download'] = 'resize'
if (opts['video-download'] === 'large') opts['video-download'] = 'resize'
// Convert deprecated --albums-from
replaceInArray(opts['albums-from'], 'folders', '%path')
replaceInArray(opts['albums-from'], 'date', `{${opts['albums-date-format']}}`)
// Convert deprecated --css
if (opts['css']) opts['theme-style'] = opts['css']
// Add a dash prefix to any --gm-args value
// We can't specify the prefix on the CLI otherwise the parser thinks it's a thumbsup arg
if (opts['gm-args']) {
opts['gm-args'] = opts['gm-args'].map(val => `-${val}`)
}
// All options as an object
return {
input: opts['input'],
output: opts['output'],
includePhotos: opts['include-photos'],
includeVideos: opts['include-videos'],
includeRawPhotos: opts['include-raw-photos'],
include: opts['include'],
exclude: opts['exclude'],
cleanup: opts['cleanup'],
title: opts['title'],
thumbSize: opts['thumb-size'],
largeSize: opts['large-size'],
photoQuality: opts['photo-quality'],
videoQuality: opts['video-quality'],
videoBitrate: opts['video-bitrate'],
videoFormat: opts['video-format'],
photoPreview: opts['photo-preview'],
videoPreview: opts['video-preview'],
photoDownload: opts['photo-download'],
videoDownload: opts['video-download'],
linkPrefix: opts['link-prefix'],
albumsFrom: opts['albums-from'],
albumsDateFormat: opts['albums-date-format'],
sortAlbumsBy: opts['sort-albums-by'],
sortAlbumsDirection: opts['sort-albums-direction'],
sortMediaBy: opts['sort-media-by'],
sortMediaDirection: opts['sort-media-direction'],
albumZipFiles: opts['album-zip-files'],
theme: opts['theme'],
themePath: opts['theme-path'],
themeStyle: opts['theme-style'],
css: opts['css'],
googleAnalytics: opts['google-analytics'],
index: opts['index'],
footer: opts['footer'],
albumsOutputFolder: opts['albums-output-folder'],
usageStats: opts['usage-stats'],
log: opts['log'],
dryRun: opts['dry-run'],
concurrency: opts['concurrency'],
outputStructure: opts['output-structure'],
gmArgs: opts['gm-args'],
watermark: opts['watermark'],
watermarkPosition: opts['watermark-position'],
embedExif: opts['embed-exif'],
settings: getSettings(opts['settings']), // NEW for settings
}
}
function replaceInArray(list, match, replacement) {
for (var i = 0; i < list.length; ++i) {
if (list[i] === match) {
list[i] = replacement
}
}
}
/**
* read the config settings
* @param {*} filename
*/
function getSettings(filename) {
var contents,
jsonContent = {
"logo": null,
"tabHome": "Home",
"application": "tumbsUp Fotobook",
"appVersion": "2.1.0",
"albumPrefix": "Album",
"albumsPrefix": "Alben",
"albumCardTitle": "Show Album ",
"copyright": null,
"footerLeft": null,
"footerCenter": null,
"footerRight": null
};
if (filename) {
var fs = require("fs");
try {
contents = fs.readFileSync(filename, 'utf-8');
jsonContent = JSON.parse(contents);
return jsonContent;
} catch (ex) {
console.log("ERROR application settings", filename);
}
}
return jsonContent;
}
# @thumbsup/theme-card-flow
[![NPM](https://img.shields.io/npm/v/@thumbsup/theme-flow.svg?style=flat)](https://www.npmjs.com/package/@thumbsup/theme-flow)
[![Travis CI](https://travis-ci.org/thumbsup/theme-flow.svg?branch=master)](https://travis-ci.org/thumbsup/theme-flow)
**Update**
To get the settings from the JSON file to be available in the application,
the two files of the thumbsUp application must be adapted.
**../thumbsup/bin/options.js**
```javascript
// add this lines to Misc options section
'settings': {
group: 'Application settings:',
description: 'JSON config file (one key per argument)',
'default': null
},
....
return {
.....
watermarkPosition: opts['watermark-position'],
embedExif: opts['embed-exif'],
settings: getSettings(opts['settings']), // NEW for settings
}
**
* read the config settings
* @param {*} filename
*/
function getSettings(filename) {
var contents,
jsonContent = { // default settings
"logo": null,
"tabHome": "Home",
"application": "tumbsUp Fotobook",
"appVersion": "2.1.0",
"albumPrefix": "Album",
"albumsPrefix": "Alben",
"albumCardTitle": "Show Album ",
"copyright": null,
"footerLeft": null,
"footerCenter": null,
"footerRight": null
};
if (filename) {
var fs = require("fs");
try {
// try to get the settings from the file
contents = fs.readFileSync(filename, 'utf-8');
jsonContent = JSON.parse(contents);
return jsonContent;
} catch (ex) {
console.log("ERROR application settings", filename);
}
}
return jsonContent;
}
```
**.../thumbsup/src/website/website.js**
```javascript
function galleryModel(rootAlbum, opts) {
return {
home: rootAlbum,
title: opts.title,
footer: opts.footer,
thumbSize: opts.thumbSize,
largeSize: opts.largeSize,
googleAnalytics: opts.googleAnalytics,
settings: opts.settings // NEW for application settings, see opitons.js
}
}
```
**sample settings**
```
{
"author": "Peter Siebler",
"logo": "public/assets/avatar.jpg",
"application": "tumbsUp Fotobook",
"appVersion": "2.1.0",
"tabHome": "Start",
"albumPrefix": "Album",
"albumsPrefix": "Alben",
"albumCardTitle": "Öffne Album ",
"copyright": "2019 Peter Siebler. All rights reserved."
}
```
**usage**
```javascript
gallery.settings.ATTRIBUTE
// for example:
gallery.settings.logo
gallery.settings.author
gallery.settings.application
.....
```
.img-replace {
/* replace text with an image */
display: inline-block;
overflow: hidden;
text-indent: 100%;
color: transparent;
white-space: nowrap;
}
.cd-popup {
position: fixed;
left: 0;
top: 0;
height: 100%;
width: 100%;
background-color: rgba(0, 0, 0, 0.78);
opacity: 0;
visibility: hidden;
-webkit-transition: opacity 0.3s 0s, visibility 0s 0.3s;
-moz-transition: opacity 0.3s 0s, visibility 0s 0.3s;
transition: opacity 0.3s 0s, visibility 0s 0.3s;
z-index: 10000;
}
.cd-popup.is-visible {
opacity: 1;
visibility: visible;
z-index: 10000;
-webkit-transition: opacity 0.3s 0s, visibility 0s 0s;
-moz-transition: opacity 0.3s 0s, visibility 0s 0s;
transition: opacity 0.3s 0s, visibility 0s 0s;
}
.cd-popup-container {
position: relative;
width: 90%;
max-width: 400px;
margin: 4em auto;
background: @card-background-color;
border-radius: .25em .25em .4em .4em;
text-align: center;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.2);
-webkit-transform: translateY(-40px);
-moz-transform: translateY(-40px);
-ms-transform: translateY(-40px);
-o-transform: translateY(-40px);
transform: translateY(-40px);
/* Force Hardware Acceleration in WebKit */
-webkit-backface-visibility: hidden;
-webkit-transition-property: -webkit-transform;
-moz-transition-property: -moz-transform;
transition-property: transform;
-webkit-transition-duration: 0.3s;
-moz-transition-duration: 0.3s;
transition-duration: 0.3s;
}
.cd-popup-container h2 {
line-height: 3em;
font-weight: 500;
}
.cd-popup-container div {
padding: 3em 1em;
}
.cd-popup-container .cd-buttons:after {
content: "";
display: table;
clear: both;
}
.cd-popup-container .cd-buttons li {
float: left;
width: 50%;
list-style: none;
}
.cd-popup-container .cd-buttons a {
display: block;
height: 60px;
line-height: 60px;
text-transform: uppercase;
color: #FFF;
-webkit-transition: background-color 0.2s;
-moz-transition: background-color 0.2s;
transition: background-color 0.2s;
}
.cd-popup-container .cd-buttons li:first-child a {
background: #fc7169;
border-radius: 0 0 0 .25em;
}
.no-touch .cd-popup-container .cd-buttons li:first-child a:hover {
background-color: #fc8982;
}
.cd-popup-container .cd-buttons li:last-child a {
background: #b6bece;
border-radius: 0 0 .25em 0;
}
.no-touch .cd-popup-container .cd-buttons li:last-child a:hover {
background-color: #c5ccd8;
}
.cd-popup-container .cd-popup-close {
position: absolute;
top: 8px;
right: 8px;
width: 30px;
height: 30px;
}
.cd-popup-container .cd-popup-close::before,
.cd-popup-container .cd-popup-close::after {
content: '';
position: absolute;
top: 12px;
width: 14px;
height: 3px;
background-color: #8f9cb5;
}
.cd-popup-container .cd-popup-close::before {
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
left: 8px;
}
.cd-popup-container .cd-popup-close::after {
-webkit-transform: rotate(-45deg);
-moz-transform: rotate(-45deg);
-ms-transform: rotate(-45deg);
-o-transform: rotate(-45deg);
transform: rotate(-45deg);
right: 8px;
}
.is-visible .cd-popup-container {
-webkit-transform: translateY(0);
-moz-transform: translateY(0);
-ms-transform: translateY(0);
-o-transform: translateY(0);
transform: translateY(0);
}
@media only screen and (min-width: 1170px) {
.cd-popup-container {
margin: 8em auto;
}
}
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/mstile-150x150.png"/>
<TileColor>#da532c</TileColor>
</tile>
</msapplication>
</browserconfig>
{{!--
Breadcrumbs
uses helper method to get the text for the crumb
--}}
{{#each breadcrumbs~}}
<a class="breadcrumb-item" href="{{relative url}}">{{albumname this.title}}</a><span class="breadcrumb-sepeartor icon-arrow-right"></span>
{{~/each~}}
<span class="breadcrumb-item-last">{{albumname album.title}}</span>
</nav>
<nav id="menubar" class="breadcrumbs">
<div class="cd-popup" role="alert">
<div class="cd-popup-container">
<h2>Modal Dialog</h2>
<a href="#0" class="cd-popup-close img-replace"></a>
<div id="cd-content">Are you sure you want to delete this element?</div>
</div>
</div>
<img class="fotopreview" src="{{relative urls.small}}" alt="{{#if meta.exif}} {{{meta.exif.DateTimeOriginal}}} {{else}} {{{meta.date}}} {{/if}}" />
<img class="fotopreview" src="{{relative urls.small}}" alt="{{#if meta.exif}} {{{meta.exif.DateTimeOriginal}}} {{else}} {{{date meta.date "MMMM YYYY HH:mm"}}} {{/if}}" />
{{#each breadcrumbs~}}
<a class="menubar-item" href="{{relative url}}">{{this.title}}</a><span class="desktop"> </span>
{{#each breadcrumbs~}}
<a class="menubar-item" href="{{relative url}}">{{#compare this.title '==' 'Home'}}{{@root.gallery.settings.tabHome}}{{else}}{{this.title}}{{/compare}}</a><span class="desktop"> </span>
<footer>
{{#compare album.title '==' 'Home'}}
<div class="left">• {{gallery.title}} •{{album.summary}}</div>
{{else}}
<div class="left">• {{album.title}} •{{album.summary}}</div>
{{/compare}}
<div class="right">{{#if gallery.footer}}{{{gallery.footer}}}: {{/if}} • Copyright © 2019 Peter Siebler. All rights reserved.</div>
</footer>
{{#if gallery.footer}}
<footer>
{{#compare album.title '==' 'Home'}}
<div class="left cd-open">• {{gallery.title}} •{{album.summary}}</div>
{{else}}
<div class="left">• {{album.title}} •{{album.summary}}</div>
{{/compare}}
<div class="right">{{#if gallery.footer}}{{{gallery.footer}}}{{/if}}
{{#if gallery.settings.copyright}} •Copyright ©{{gallery.settings.copyright}}{{/if}}
</div>
</footer>
{{/if}}
{{!-- render all album teaser--}}
<div id="albums" class="cards clearfix{{#compare album.files.length '==' 0}} nocover{{/compare}}">
{{#each album.albums~}}
<a href="{{relative url}}" class="card" title="Show Gallery {{title}}">
{{!-- render all album teaser--}}
{{#compare album.files.length '>' 1}}
<div class="space3em"> </div>
{{/compare}}
<div id="albums" class="cards clearfix{{#compare album.files.length '==' 0}} nocover{{/compare}}">
{{#each album.albums~}}
<a href="{{relative url}}" class="card" title="{{@root.gallery.settings.albumCardTitle}}{{title}}">
* **README.md**
this file...
/*
* read the application settings
* just for testing - not for production
* appsettings
*/
"use strict";
module.exports = obj => {
if (!global.thumbsup) {
var fs = require("fs");
var appsroot = require('path').resolve(__dirname, '../../..');
try {
var contents = fs.readFileSync(appsroot + "/appsettings.json", 'utf-8');
if (contents) {
global.thumbsup = {};
global.thumbsup.basedir = appsroot;
global.thumbsup.settings = JSON.parse(contents);
global.thumbsup.appsettings = true;
}
} catch (ex) {
console.log("ERROR application settings", appsroot);
}
}
}
/*
* check album name
*/
"use strict";
module.exports = name => {
}
return (name == 'Home') ? 'Fotobuch' : 'Album ' + name;
<meta name="viewport" content="width=device-width, user-scalable=no" />
<title>{{gallery.title}} - {{album.title}}</title>
<title>{{gallery.title}} - {{album.title}}</title>
<meta name="viewport" content="width=device-width, user-scalable=no" />
<meta name="application-name" content="{{gallery.settings.application}} {{gallery.settings.appVersion}}">
<meta name="author" content="{{gallery.settings.author}}">
<meta name="robots" content="noindex,nofollow">
<meta name="format-detection" content="telephone=no">
<meta name="generator" content="thumbsup">
<link rel="apple-touch-icon" sizes="180x180" href="public/assets/icons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="public/assets/icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="public/assets/icons/favicon-16x16.png">
<link rel="manifest" href="public/assets/icons/site.webmanifest">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="theme-color" content="#ffffff">
var scrollWindow = function() {
$('body').scroll(function(){
var $w = $(this), st =$(".page-top").position().top,navbar = $('.menubar'),y = (st) ? Math.abs(st):0;
if (y > 150) { if ( !navbar.hasClass('scrolled')) {navbar.addClass('scrolled');}}
if (y < 150) { if ( navbar.hasClass('scrolled')) {navbar.removeClass('scrolled sleep');}}
if ( y > 600) { if ( !navbar.hasClass('awake') ) {navbar.addClass('awake'); }}
if ( y < 600 ) {if ( navbar.hasClass('awake') ) {navbar.removeClass('awake');navbar.addClass('sleep');}}
});
};
// scrollWindow();
//open popup
$('.cd-open').on('click', function(event){
event.preventDefault();
$('.cd-popup').addClass('is-visible');
});
//close popup
$('.cd-popup-close').on('click', function(event){
event.preventDefault();
$('.cd-popup').removeClass('is-visible');
});
//close popup when clicking the esc keyboard button
$(document).keyup(function(event){
if(event.which=='27'){
$('.cd-popup').removeClass('is-visible');
}
});
## Open Task
How to add or handle additional setting (appsettings) with config.json ?
```bash
sample: appsettings.json
{
"tab-home": "Fotobuch",
"ablum-prefix-title": "Meine Alben",
"album-card-title": "Öffne Album",
.....
}