Merge pull request #376 from electerious/develop

Lychee v3.0.4
pull/422/head v3.0.4
Tobias Reich 9 years ago
commit aee5d9b65b

2
dist/main.css vendored

File diff suppressed because one or more lines are too long

12
dist/main.js vendored

File diff suppressed because one or more lines are too long

8
dist/view.js vendored

File diff suppressed because one or more lines are too long

@ -4,18 +4,17 @@ First you have to install the following dependencies:
- `node` [Node.js](http://nodejs.org) v0.10 or later
- `npm` [Node Packaged Modules](https://www.npmjs.org)
- `bower` [Bower](http://bower.io)
- `gulp` [Gulp.js](http://gulpjs.com)
After [installing Node.js](http://nodejs.org) you can use the included `npm` package manager to install the global requirements and Lychee-dependencies with the following command:
cd src/
npm install -g bower gulp
npm install && bower install
npm install -g gulp
npm install
### Build
The Gruntfile is located in `src/` and can be easily executed using the `gulp` command.
The Gulpfile is located in `src/` and can be easily executed using the `gulp` command.
### Watch for changes
@ -23,4 +22,4 @@ While developing, you might want to use the following command to watch for chang
gulp watch
`gulp` will automatically build Lychee everytime you save a file.
`gulp watch` will automatically build Lychee everytime you save a file.

@ -1,9 +1,21 @@
## v3.0.4
Released July 17, 2015
- `Improved` Removed bower and updated basicModal & basicContext
- `Improved` Small interface performance improvements
- `Improved` Updated all JS-files to take advantage of ES2015
- `Improved` Better error-handling for the Dropbox-, URL- and Server-Import
- `Improved` Added skipDuplicates- and identifier-check to the diagnostics
- `Fixed` error when using "Merge All" with one selected album
- `Fixed` error when saving username and password after the initial setup
- `Fixed` Clicks not recognized when using a mouse on a touchscreen-device (#345)
## v3.0.3
Released June 28, 2015
- `New` Skip duplicates on upload (#367, [How to activate](settings.md))
- `Fixed` Clicks not recognized when using a mouse on a touchscreen-device (#345)
## v3.0.2

@ -219,14 +219,16 @@ class Admin extends Access {
private function importUrl() {
Module::dependencies(isset($_POST['url'], $_POST['albumID']));
echo Import::url($_POST['url'], $_POST['albumID']);
$import = new Import($this->database, $this->plugins, $this->settings);
echo $import->url($_POST['url'], $_POST['albumID']);
}
private function importServer() {
Module::dependencies(isset($_POST['albumID'], $_POST['path']));
echo Import::server($_POST['albumID'], $_POST['path']);
$import = new Import($this->database, $this->plugins, $this->settings);
echo $import->server($_POST['path'], $_POST['albumID']);
}

@ -15,4 +15,5 @@ VALUES
('imagick','1'),
('dropboxKey',''),
('identifier',''),
('skipDuplicates','0'),
('plugins','');

@ -54,14 +54,6 @@ if ($result->num_rows===0) {
Log::error($database, 'update_030001', __LINE__, 'Could not update database (' . $database->error . ')');
return false;
}
} else {
$identifier = md5(microtime(true));
$query = Database::prepare($database, "UPDATE `?` SET `value` = '?' WHERE `key` = 'identifier' LIMIT 1", array(LYCHEE_TABLE_SETTINGS, $identifier));
$result = $database->query($query);
if (!$result) {
Log::error($database, 'update_030001', __LINE__, 'Could not reset public albums (' . $database->error . ')');
return false;
}
}
# Set version

@ -198,6 +198,14 @@ if(!defined('LYCHEE')) exit('Error: Direct access is not allowed!');
return false;
}
# Generate identifier
$identifier = md5(microtime(true));
$query = Database::prepare($database, "UPDATE `?` SET `value` = '?' WHERE `key` = 'identifier' LIMIT 1", array(LYCHEE_TABLE_SETTINGS, $identifier));
if (!$database->query($query)) {
Log::error($database, __METHOD__, __LINE__, $database->error);
return false;
}
}
# Create albums

@ -9,14 +9,32 @@ if (!defined('LYCHEE')) exit('Error: Direct access is not allowed!');
class Import extends Module {
static function photo($database, $plugins, $settings, $path, $albumID = 0, $description = '', $tags = '') {
private $database = null;
private $settings = null;
private $albumIDs = null;
public function __construct($database, $plugins, $settings) {
# Init vars
$this->database = $database;
$this->plugins = $plugins;
$this->settings = $settings;
return true;
}
private function photo($path, $albumID = 0, $description = '', $tags = '') {
# Check dependencies
self::dependencies(isset($this->database, $this->plugins, $this->settings, $path));
# No need to validate photo type and extension in this function.
# $photo->add will take care of it.
$info = getimagesize($path);
$size = filesize($path);
$photo = new Photo($database, $plugins, $settings, null);
$photo = new Photo($this->database, $this->plugins, $this->settings, null);
$nameFile = array(array());
$nameFile[0]['name'] = $path;
@ -25,25 +43,35 @@ class Import extends Module {
$nameFile[0]['error'] = 0;
$nameFile[0]['size'] = $size;
if (!$photo->add($nameFile, $albumID, $description, $tags)) return false;
if (!$photo->add($nameFile, $albumID, $description, $tags, true)) return false;
return true;
}
static function url($urls, $albumID = 0) {
public function url($urls, $albumID = 0) {
# Check dependencies
self::dependencies(isset($this->database, $urls));
# Call plugins
$this->plugins(__METHOD__, 0, func_get_args());
$error = false;
# Parse
# Parse URLs
$urls = str_replace(' ', '%20', $urls);
$urls = explode(',', $urls);
foreach ($urls as &$url) {
# Validate photo type and extension even when $this->photo (=> $photo->add) will do the same.
# This prevents us from downloading invalid photos.
# Verify extension
$extension = getExtension($url);
if (!in_array(strtolower($extension), Photo::$validExtensions, true)) {
$error = true;
Log::error($this->database, __METHOD__, __LINE__, 'Photo format not supported (' . $url . ')');
continue;
}
@ -51,6 +79,7 @@ class Import extends Module {
$type = @exif_imagetype($url);
if (!in_array($type, Photo::$validTypes, true)) {
$error = true;
Log::error($this->database, __METHOD__, __LINE__, 'Photo type not supported (' . $url . ')');
continue;
}
@ -58,27 +87,40 @@ class Import extends Module {
$filename = $pathinfo['filename'] . '.' . $pathinfo['extension'];
$tmp_name = LYCHEE_DATA . $filename;
if (@copy($url, $tmp_name)===false) $error = true;
if (@copy($url, $tmp_name)===false) {
$error = true;
Log::error($this->database, __METHOD__, __LINE__, 'Could not copy file (' . $tmp_name . ') to temp-folder (' . $tmp_name . ')');
continue;
}
# Import photo
if (!$this->photo($tmp_name, $albumID)) {
$error = true;
Log::error($this->database, __METHOD__, __LINE__, 'Could not import file: ' . $tmp_name);
continue;
}
}
$import = Import::server($albumID, LYCHEE_DATA);
# Call plugins
$this->plugins(__METHOD__, 1, func_get_args());
if ($error===false&&$import===true) return true;
else return false;
if ($error===false) return true;
return false;
}
static function server($albumID = 0, $path) {
public function server($path, $albumID = 0) {
global $database, $plugins, $settings;
# Check dependencies
self::dependencies(isset($this->database, $this->plugins, $this->settings));
# Parse path
if (!isset($path)) $path = LYCHEE_UPLOADS_IMPORT;
if (substr($path, -1)==='/') $path = substr($path, 0, -1);
if (!isset($path)) $path = LYCHEE_UPLOADS_IMPORT;
if (substr($path, -1)==='/') $path = substr($path, 0, -1);
if (is_dir($path)===false) {
Log::error($database, __METHOD__, __LINE__, 'Given path is not a directory (' . $path . ')');
Log::error($this->database, __METHOD__, __LINE__, 'Given path is not a directory (' . $path . ')');
return 'Error: Given path is not a directory!';
}
@ -86,7 +128,7 @@ class Import extends Module {
if ($path===LYCHEE_UPLOADS_BIG||($path . '/')===LYCHEE_UPLOADS_BIG||
$path===LYCHEE_UPLOADS_MEDIUM||($path . '/')===LYCHEE_UPLOADS_MEDIUM||
$path===LYCHEE_UPLOADS_THUMB||($path . '/')===LYCHEE_UPLOADS_THUMB) {
Log::error($database, __METHOD__, __LINE__, 'The given path is a reserved path of Lychee (' . $path . ')');
Log::error($this->database, __METHOD__, __LINE__, 'The given path is a reserved path of Lychee (' . $path . ')');
return 'Error: Given path is a reserved path of Lychee!';
}
@ -94,10 +136,10 @@ class Import extends Module {
$contains['photos'] = false;
$contains['albums'] = false;
# Invoke plugins directly, as instance method not valid here
# Call plugins
# Note that updated albumId and path explicitly passed, rather
# than using func_get_args() which will only return original ones
$plugins->activate(__METHOD__ . ":before", array($albumID, $path));
$this->plugins(__METHOD__, 0, array($albumID, $path));
# Get all files
$files = glob($path . '/*');
@ -108,7 +150,7 @@ class Import extends Module {
# the file may still be unreadable by the user
if (!is_readable($file)) {
$error = true;
Log::error($database, __METHOD__, __LINE__, 'Could not read file or directory: ' . $file);
Log::error($this->database, __METHOD__, __LINE__, 'Could not read file or directory: ' . $file);
continue;
}
@ -116,33 +158,33 @@ class Import extends Module {
# Photo
if (!Import::photo($database, $plugins, $settings, $file, $albumID)) {
$contains['photos'] = true;
if (!$this->photo($file, $albumID)) {
$error = true;
Log::error($database, __METHOD__, __LINE__, 'Could not import file: ' . $file);
Log::error($this->database, __METHOD__, __LINE__, 'Could not import file: ' . $file);
continue;
}
$contains['photos'] = true;
} else if (is_dir($file)) {
# Folder
$name = mysqli_real_escape_string($database, basename($file));
$album = new Album($database, null, null, null);
$newAlbumID = $album->add('[Import] ' . $name);
$album = new Album($this->database, $this->plugins, $this->settings, null);
$newAlbumID = $album->add('[Import] ' . basename($file));
$contains['albums'] = true;
if ($newAlbumID===false) {
$error = true;
Log::error($database, __METHOD__, __LINE__, 'Could not create album in Lychee (' . $newAlbumID . ')');
Log::error($this->database, __METHOD__, __LINE__, 'Could not create album in Lychee (' . $newAlbumID . ')');
continue;
}
$import = Import::server($newAlbumID, $file . '/');
$import = $this->server($file . '/', $newAlbumID);
if ($import!==true&&$import!=='Notice: Import only contains albums!') {
$error = true;
Log::error($database, __METHOD__, __LINE__, 'Could not import folder. Function returned warning');
Log::error($this->database, __METHOD__, __LINE__, 'Could not import folder. Function returned warning.');
continue;
}
@ -150,13 +192,16 @@ class Import extends Module {
}
# Invoke plugins directly, as instance method not valid here
# Call plugins
# Note that updated albumId and path explicitly passed, rather
# than using func_get_args() which will only return original ones
$plugins->activate(__METHOD__ . ":after", array($albumID, $path));
$this->plugins(__METHOD__, 1, array($albumID, $path));
# The following returns will be caught in the front-end
if ($contains['photos']===false&&$contains['albums']===false) return 'Warning: Folder empty or no readable files to process!';
if ($contains['photos']===false&&$contains['albums']===true) return 'Notice: Import only contains albums!';
if ($contains['photos']===false&&$contains['albums']===true) return 'Notice: Import only contained albums!';
if ($error===true) return false;
return true;
}

@ -37,7 +37,10 @@ class Photo extends Module {
}
public function add($files, $albumID, $description = '', $tags = '') {
public function add($files, $albumID = 0, $description = '', $tags = '', $returnOnError = false) {
# Use $returnOnError if you want to handle errors by your own
# e.g. when calling this functions inside an if-condition
# Check dependencies
self::dependencies(isset($this->database, $this->settings, $files));
@ -89,6 +92,7 @@ class Photo extends Module {
$extension = getExtension($file['name']);
if (!in_array(strtolower($extension), Photo::$validExtensions, true)) {
Log::error($this->database, __METHOD__, __LINE__, 'Photo format not supported');
if ($returnOnError===true) return false;
exit('Error: Photo format not supported!');
}
@ -96,6 +100,7 @@ class Photo extends Module {
$type = @exif_imagetype($file['tmp_name']);
if (!in_array($type, Photo::$validTypes, true)) {
Log::error($this->database, __METHOD__, __LINE__, 'Photo type not supported');
if ($returnOnError===true) return false;
exit('Error: Photo type not supported!');
}
@ -112,6 +117,7 @@ class Photo extends Module {
$checksum = sha1_file($tmp_name);
if ($checksum===false) {
Log::error($this->database, __METHOD__, __LINE__, 'Could not calculate checksum for photo');
if ($returnOnError===true) return false;
exit('Error: Could not calculate checksum for photo!');
}
@ -141,11 +147,13 @@ class Photo extends Module {
if (!is_uploaded_file($tmp_name)) {
if (!@copy($tmp_name, $path)) {
Log::error($this->database, __METHOD__, __LINE__, 'Could not copy photo to uploads');
if ($returnOnError===true) return false;
exit('Error: Could not copy photo to uploads!');
} else @unlink($tmp_name);
} else {
if (!@move_uploaded_file($tmp_name, $path)) {
Log::error($this->database, __METHOD__, __LINE__, 'Could not move photo to uploads');
if ($returnOnError===true) return false;
exit('Error: Could not move photo to uploads!');
}
}
@ -156,6 +164,7 @@ class Photo extends Module {
# Check if the user wants to skip duplicates
if ($this->settings['skipDuplicates']==='1') {
Log::notice($this->database, __METHOD__, __LINE__, 'Skipped upload of existing photo because skipDuplicates is activated');
if ($returnOnError===true) return false;
exit('Warning: This photo has been skipped because it\'s already in your library.');
}
@ -185,6 +194,7 @@ class Photo extends Module {
# Create Thumb
if (!$this->createThumb($path, $photo_name, $info['type'], $info['width'], $info['height'])) {
Log::error($this->database, __METHOD__, __LINE__, 'Could not create thumbnail for photo');
if ($returnOnError===true) return false;
exit('Error: Could not create thumbnail for photo!');
}
@ -204,6 +214,7 @@ class Photo extends Module {
if (!$result) {
Log::error($this->database, __METHOD__, __LINE__, $this->database->error);
if ($returnOnError===true) return false;
exit('Error: Could not save photo in database!');
}

@ -68,13 +68,15 @@ if (!isset($dbPassword)) $error .= ('Error: No property for $dbPassword in con
if (!isset($dbHost)||$dbHost==='') $error .= ('Error: No property for $dbHost in config.php' . PHP_EOL);
# Settings
if (!isset($settings['username'])||$settings['username']=='') $error .= ('Error: Username empty or not set in database' . PHP_EOL);
if (!isset($settings['password'])||$settings['password']=='') $error .= ('Error: Password empty or not set in database' . PHP_EOL);
if (!isset($settings['thumbQuality'])||$settings['thumbQuality']=='') $error .= ('Error: No or wrong property for thumbQuality in database' . PHP_EOL);
if (!isset($settings['sortingPhotos'])||$settings['sortingPhotos']=='') $error .= ('Error: Wrong property for sortingPhotos in database' . PHP_EOL);
if (!isset($settings['sortingAlbums'])||$settings['sortingAlbums']=='') $error .= ('Error: Wrong property for sortingAlbums in database' . PHP_EOL);
if (!isset($settings['plugins'])) $error .= ('Error: No property for plugins in database' . PHP_EOL);
if (!isset($settings['imagick'])||$settings['imagick']=='') $error .= ('Error: No or wrong property for imagick in database' . PHP_EOL);
if (!isset($settings['username'])||$settings['username']=='') $error .= ('Error: Username empty or not set in database' . PHP_EOL);
if (!isset($settings['password'])||$settings['password']=='') $error .= ('Error: Password empty or not set in database' . PHP_EOL);
if (!isset($settings['thumbQuality'])||$settings['thumbQuality']=='') $error .= ('Error: No or wrong property for thumbQuality in database' . PHP_EOL);
if (!isset($settings['sortingPhotos'])||$settings['sortingPhotos']=='') $error .= ('Error: Wrong property for sortingPhotos in database' . PHP_EOL);
if (!isset($settings['sortingAlbums'])||$settings['sortingAlbums']=='') $error .= ('Error: Wrong property for sortingAlbums in database' . PHP_EOL);
if (!isset($settings['plugins'])) $error .= ('Error: No property for plugins in database' . PHP_EOL);
if (!isset($settings['imagick'])||$settings['imagick']=='') $error .= ('Error: No or wrong property for imagick in database' . PHP_EOL);
if (!isset($settings['identifier'])||$settings['identifier']=='') $error .= ('Error: No or wrong property for identifier in database' . PHP_EOL);
if (!isset($settings['skipDuplicates'])||$settings['skipDuplicates']=='') $error .= ('Error: No or wrong property for skipDuplicates in database' . PHP_EOL);
if (!isset($settings['checkForUpdates'])||($settings['checkForUpdates']!='0'&&$settings['checkForUpdates']!='1')) $error .= ('Error: No or wrong property for checkForUpdates in database' . PHP_EOL);
# Check dropboxKey

@ -1,7 +0,0 @@
{
"name": "Lychee",
"private": true,
"dependencies": {
"basicModal": "~2.0.8"
}
}

@ -88,7 +88,7 @@ paths.main = {
'node_modules/mousetrap/mousetrap.min.js',
'node_modules/mousetrap/plugins/global-bind/mousetrap-global-bind.min.js',
'node_modules/basiccontext/dist/basicContext.min.js',
'bower_components/basicModal/dist/basicModal.min.js',
'node_modules/basicmodal/dist/basicModal.min.js',
'../dist/_main--javascript.js'
],
scss: [
@ -96,7 +96,7 @@ paths.main = {
],
styles: [
'node_modules/basiccontext/src/styles/main.scss',
'bower_components/basicModal/src/styles/main.scss',
'node_modules/basicmodal/src/styles/main.scss',
'./styles/main.scss'
],
svg: [

@ -1,6 +1,6 @@
{
"name": "Lychee",
"version": "3.0.3",
"version": "3.0.4",
"description": "Self-hosted photo-management done right.",
"authors": "Tobias Reich <tobias@electerious.com>",
"license": "MIT",
@ -10,7 +10,8 @@
"url": "https://github.com/electerious/Lychee.git"
},
"devDependencies": {
"basiccontext": "^3.1.2",
"basiccontext": "^3.3.0",
"basicmodal": "^3.1.1",
"gulp": "^3.9.0",
"gulp-autoprefixer": "2.3.1",
"gulp-babel": "^5.1.0",
@ -22,6 +23,6 @@
"gulp-sass": "^2.0.3",
"gulp-uglify": "^1.2.0",
"jquery": "^2.1.4",
"mousetrap": "^1.5.2"
"mousetrap": "^1.5.3"
}
}

@ -1,12 +1,12 @@
function gup(b) {
b = b.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
b = b.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]")
var a = "[\\?&]" + b + "=([^&#]*)",
let a = "[\\?&]" + b + "=([^&#]*)",
d = new RegExp(a),
c = d.exec(window.location.href);
c = d.exec(window.location.href)
if (c === null) return '';
else return c[1];
if (c === null) return ''
else return c[1]
};
}

@ -1,74 +1,74 @@
(function($) {
var Swipe = function(el) {
var self = this;
var self = this
this.el = $(el);
this.pos = { start: { x: 0, y: 0 }, end: { x: 0, y: 0 } };
this.startTime;
this.el = $(el)
this.pos = { start: { x: 0, y: 0 }, end: { x: 0, y: 0 } }
this.startTime
el.on('touchstart', function(e) { self.touchStart(e); });
el.on('touchmove', function(e) { self.touchMove(e); });
el.on('touchend', function(e) { self.swipeEnd(); });
el.on('mousedown', function(e) { self.mouseDown(e); });
};
el.on('touchstart', function(e) { self.touchStart(e) })
el.on('touchmove', function(e) { self.touchMove(e) })
el.on('touchend', function(e) { self.swipeEnd() })
el.on('mousedown', function(e) { self.mouseDown(e) })
}
Swipe.prototype = {
touchStart: function(e) {
var touch = e.originalEvent.touches[0];
var touch = e.originalEvent.touches[0]
this.swipeStart(e, touch.pageX, touch.pageY);
this.swipeStart(e, touch.pageX, touch.pageY)
},
touchMove: function(e) {
var touch = e.originalEvent.touches[0];
var touch = e.originalEvent.touches[0]
this.swipeMove(e, touch.pageX, touch.pageY);
this.swipeMove(e, touch.pageX, touch.pageY)
},
mouseDown: function(e) {
var self = this;
var self = this
this.swipeStart(e, e.pageX, e.pageY);
this.swipeStart(e, e.pageX, e.pageY)
this.el.on('mousemove', function(e) { self.mouseMove(e); });
this.el.on('mouseup', function() { self.mouseUp(); });
this.el.on('mousemove', function(e) { self.mouseMove(e) })
this.el.on('mouseup', function() { self.mouseUp() })
},
mouseMove: function(e) {
this.swipeMove(e, e.pageX, e.pageY);
this.swipeMove(e, e.pageX, e.pageY)
},
mouseUp: function(e) {
this.swipeEnd(e);
this.swipeEnd(e)
this.el.off('mousemove');
this.el.off('mouseup');
this.el.off('mousemove')
this.el.off('mouseup')
},
swipeStart: function(e, x, y) {
this.pos.start.x = x;
this.pos.start.y = y;
this.pos.end.x = x;
this.pos.end.y = y;
this.pos.start.x = x
this.pos.start.y = y
this.pos.end.x = x
this.pos.end.y = y
this.startTime = new Date().getTime();
this.startTime = new Date().getTime()
this.trigger('swipeStart', e);
this.trigger('swipeStart', e)
},
swipeMove: function(e, x, y) {
this.pos.end.x = x;
this.pos.end.y = y;
this.pos.end.x = x
this.pos.end.y = y
this.trigger('swipeMove', e);
this.trigger('swipeMove', e)
},
swipeEnd: function(e) {
this.trigger('swipeEnd', e);
this.trigger('swipeEnd', e)
},
trigger: function(e, originalEvent) {
var self = this;
var self = this
var
event = $.Event(e),
@ -78,31 +78,31 @@
direction = 'up',
distance = Math.round(Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))),
angle = Math.round(radians * 180 / Math.PI),
speed = Math.round(distance / ( new Date().getTime() - self.startTime ) * 1000);
speed = Math.round(distance / ( new Date().getTime() - self.startTime ) * 1000)
if ( angle < 0 ) {
angle = 360 - Math.abs(angle);
angle = 360 - Math.abs(angle)
}
if ( ( angle <= 45 && angle >= 0 ) || ( angle <= 360 && angle >= 315 ) ) {
direction = 'left';
direction = 'left'
} else if ( angle >= 135 && angle <= 225 ) {
direction = 'right';
direction = 'right'
} else if ( angle > 45 && angle < 135 ) {
direction = 'down';
direction = 'down'
}
event.originalEvent = originalEvent;
event.originalEvent = originalEvent
event.swipe = { x: x, y: y, direction: direction, distance: distance, angle: angle, speed: speed };
event.swipe = { x: x, y: y, direction: direction, distance: distance, angle: angle, speed: speed }
$(self.el).trigger(event);
$(self.el).trigger(event)
}
};
}
$.fn.swipe = function() {
var swipe = new Swipe(this);
var swipe = new Swipe(this)
return this;
};
})(jQuery);
return this
}
})(jQuery)

@ -1,6 +1,6 @@
/**
* @description Takes care of every action an album can handle and execute.
* @copyright 2015 by Tobias Reich
* @description Takes care of every action an album can handle and execute.
* @copyright 2015 by Tobias Reich
*/
album = {
@ -11,134 +11,125 @@ album = {
album.getID = function() {
var id = null;
let id = null
var isID = function(id) {
if (id==='0'||id==='f'||id==='s'||id==='r') return true;
return $.isNumeric(id);
let isID = (id) => {
if (id==='0' || id==='f' || id==='s' || id==='r') return true
return $.isNumeric(id)
}
if (photo.json) id = photo.json.album;
else if (album.json) id = album.json.id;
if (photo.json) id = photo.json.album
else if (album.json) id = album.json.id
// Search
if (isID(id)===false) id = $('.album:hover, .album.active').attr('data-id');
if (isID(id)===false) id = $('.photo:hover, .photo.active').attr('data-album-id');
if (isID(id)===false) id = $('.album:hover, .album.active').attr('data-id')
if (isID(id)===false) id = $('.photo:hover, .photo.active').attr('data-album-id')
if (isID(id)===true) return id;
else return false;
if (isID(id)===true) return id
else return false
}
album.load = function(albumID, refresh) {
var startTime,
params,
durationTime,
waitTime;
password.get(albumID, function() {
if (!refresh) lychee.animate('#content', 'contentZoomOut');
if (!refresh) lychee.animate('#content', 'contentZoomOut')
startTime = new Date().getTime();
let startTime = new Date().getTime()
params = {
let params = {
albumID,
password: password.value
}
api.post('Album::get', params, function(data) {
let waitTime = 0
if (data==='Warning: Album private!') {
if (document.location.hash.replace('#', '').split('/')[1]!=undefined) {
// Display photo only
lychee.setMode('view');
lychee.setMode('view')
} else {
// Album not public
lychee.content.show();
lychee.goto('');
lychee.content.show()
lychee.goto('')
}
return false;
return false
}
if (data==='Warning: Wrong password!') {
album.load(albumID, refresh);
return false;
album.load(albumID, refresh)
return false
}
album.json = data;
album.json = data
// Calculate delay
durationTime = (new Date().getTime() - startTime);
if (durationTime>300) waitTime = 0;
else waitTime = 300 - durationTime;
let durationTime = (new Date().getTime() - startTime)
if (durationTime>300) waitTime = 0
else waitTime = 300 - durationTime
// Skip delay when refresh is true
// Skip delay when opening a blank Lychee
if (refresh===true) waitTime = 0;
if (!visible.albums()&&!visible.photo()&&!visible.album()) waitTime = 0;
if (refresh===true) waitTime = 0
if (!visible.albums() && !visible.photo() && !visible.album()) waitTime = 0
setTimeout(function() {
setTimeout(() => {
view.album.init();
view.album.init()
if (!refresh) {
lychee.animate('#content', 'contentZoomIn');
header.setMode('album');
lychee.animate('#content', 'contentZoomIn')
header.setMode('album')
}
}, waitTime);
}, waitTime)
});
})
});
})
}
album.parse = function() {
if (!album.json.title) album.json.title = 'Untitled';
if (!album.json.title) album.json.title = 'Untitled'
}
album.add = function() {
var action;
action = function(data) {
const action = function(data) {
var isNumber,
title = data.title;
let title = data.title
basicModal.close();
const isNumber = (n) => (!isNaN(parseFloat(n)) && isFinite(n))
isNumber = function(n) {
basicModal.close()
return !isNaN(parseFloat(n)) && isFinite(n)
}
if (title.length===0) title = 'Untitled';
if (title.length===0) title = 'Untitled'
api.post('Album::add', { title }, function(data) {
// Avoid first album to be true
if (data===true) data = 1;
if (data===true) data = 1
if (data!==false&&isNumber(data)) {
albums.refresh();
lychee.goto(data);
if (data!==false && isNumber(data)) {
albums.refresh()
lychee.goto(data)
} else {
lychee.error(null, params, data);
lychee.error(null, params, data)
}
});
})
}
basicModal.show({
body: "<p>Enter a title for the new album: <input class='text' data-name='title' type='text' maxlength='50' placeholder='Title' value='Untitled'></p>",
body: `<p>Enter a title for the new album: <input class='text' name='title' type='text' maxlength='50' placeholder='Title' value='Untitled'></p>`,
buttons: {
action: {
title: 'Create Album',
@ -149,25 +140,24 @@ album.add = function() {
fn: basicModal.close
}
}
});
})
}
album.delete = function(albumIDs) {
var action = {},
cancel = {},
msg = '',
albumTitle = '';
let action = {},
cancel = {},
msg = ''
if (!albumIDs) return false;
if (albumIDs instanceof Array===false) albumIDs = [albumIDs];
if (!albumIDs) return false
if (albumIDs instanceof Array===false) albumIDs = [albumIDs]
action.fn = function() {
var params;
let params
basicModal.close();
basicModal.close()
params = {
albumIDs: albumIDs.join()
@ -178,48 +168,50 @@ album.delete = function(albumIDs) {
if (visible.albums()) {
albumIDs.forEach(function(id) {
albums.json.num--;
view.albums.content.delete(id);
albums.deleteByID(id);
});
albums.json.num--
view.albums.content.delete(id)
albums.deleteByID(id)
})
} else {
albums.refresh();
lychee.goto('');
albums.refresh()
lychee.goto('')
}
if (data!==true) lychee.error(null, params, data);
if (data!==true) lychee.error(null, params, data)
});
})
}
if (albumIDs.toString()==='0') {
action.title = 'Clear Unsorted';
cancel.title = 'Keep Unsorted';
action.title = 'Clear Unsorted'
cancel.title = 'Keep Unsorted'
msg = "<p>Are you sure you want to delete all photos from 'Unsorted'?<br>This action can't be undone!</p>";
msg = `<p>Are you sure you want to delete all photos from 'Unsorted'?<br>This action can't be undone!</p>`
} else if (albumIDs.length===1) {
action.title = 'Delete Album and Photos';
cancel.title = 'Keep Album';
let albumTitle = ''
action.title = 'Delete Album and Photos'
cancel.title = 'Keep Album'
// Get title
if (album.json) albumTitle = album.json.title;
else if (albums.json) albumTitle = albums.getByID(albumIDs).title;
if (album.json) albumTitle = album.json.title
else if (albums.json) albumTitle = albums.getByID(albumIDs).title
msg = "<p>Are you sure you want to delete the album '" + albumTitle + "' and all of the photos it contains? This action can't be undone!</p>";
msg = `<p>Are you sure you want to delete the album ${ albumTitle } and all of the photos it contains? This action can't be undone!</p>`
} else {
action.title = 'Delete Albums and Photos';
cancel.title = 'Keep Albums';
action.title = 'Delete Albums and Photos'
cancel.title = 'Keep Albums'
msg = "<p>Are you sure you want to delete all " + albumIDs.length + " selected albums and all of the photos they contain? This action can't be undone!</p>";
msg = `<p>Are you sure you want to delete all ${ albumIDs.length } selected albums and all of the photos they contain? This action can't be undone!</p>`
}
@ -236,80 +228,78 @@ album.delete = function(albumIDs) {
fn: basicModal.close
}
}
});
})
}
album.setTitle = function(albumIDs) {
var oldTitle = '',
input = '',
msg = '',
action;
let oldTitle = '',
msg = ''
if (!albumIDs) return false;
if (albumIDs instanceof Array===false) albumIDs = [albumIDs];
if (!albumIDs) return false
if (albumIDs instanceof Array===false) albumIDs = [albumIDs]
if (albumIDs.length===1) {
// Get old title if only one album is selected
if (album.json) oldTitle = album.json.title;
else if (albums.json) oldTitle = albums.getByID(albumIDs).title;
if (album.json) oldTitle = album.json.title
else if (albums.json) oldTitle = albums.getByID(albumIDs).title
if (!oldTitle) oldTitle = '';
oldTitle = oldTitle.replace(/'/g, '&apos;');
if (!oldTitle) oldTitle = ''
oldTitle = oldTitle.replace(/'/g, '&apos;')
}
action = function(data) {
const action = function(data) {
var params,
newTitle = data.title;
let newTitle = data.title
basicModal.close();
basicModal.close()
// Remove html from input
newTitle = lychee.removeHTML(newTitle);
newTitle = lychee.removeHTML(newTitle)
// Set to Untitled when empty
newTitle = (newTitle==='') ? 'Untitled' : newTitle;
// Set title to Untitled when empty
newTitle = (newTitle==='') ? 'Untitled' : newTitle
if (visible.album()) {
album.json.title = newTitle;
view.album.title();
// Rename only one album
if (albums.json) {
var id = albumIDs[0];
albums.getByID(id).title = newTitle;
}
album.json.title = newTitle
view.album.title()
if (albums.json) albums.getByID(albumIDs[0]).title = newTitle
} else if (visible.albums()) {
// Rename all albums
albumIDs.forEach(function(id) {
albums.getByID(id).title = newTitle;
view.albums.content.title(id);
});
albums.getByID(id).title = newTitle
view.albums.content.title(id)
})
}
params = {
albumIDs: albumIDs.join(),
title: newTitle
let params = {
albumIDs : albumIDs.join(),
title : newTitle
}
api.post('Album::setTitle', params, function(data) {
if (data!==true) lychee.error(null, params, data);
if (data!==true) lychee.error(null, params, data)
});
})
}
input = "<input class='text' data-name='title' type='text' maxlength='50' placeholder='Title' value='" + oldTitle + "'>";
let input = `<input class='text' name='title' type='text' maxlength='50' placeholder='Title' value='${ oldTitle }'>`
if (albumIDs.length===1) msg = "<p>Enter a new title for this album: " + input + "</p>";
else msg = "<p>Enter a title for all " + albumIDs.length + " selected albums: " + input +"</p>";
if (albumIDs.length===1) msg = `<p>Enter a new title for this album: ${ input }</p>`
else msg = `<p>Enter a title for all ${ albumIDs.length } selected albums: ${ input }</p>`
basicModal.show({
body: msg,
@ -323,45 +313,43 @@ album.setTitle = function(albumIDs) {
fn: basicModal.close
}
}
});
})
}
album.setDescription = function(albumID) {
var oldDescription = album.json.description.replace(/'/g, '&apos;'),
action;
let oldDescription = album.json.description.replace(/'/g, '&apos;')
action = function(data) {
const action = function(data) {
var params,
description = data.description;
let description = data.description
basicModal.close();
basicModal.close()
// Remove html from input
description = lychee.removeHTML(description);
description = lychee.removeHTML(description)
if (visible.album()) {
album.json.description = description;
view.album.description();
album.json.description = description
view.album.description()
}
params = {
let params = {
albumID,
description
}
api.post('Album::setDescription', params, function(data) {
if (data!==true) lychee.error(null, params, data);
if (data!==true) lychee.error(null, params, data)
});
})
}
basicModal.show({
body: "<p>Please enter a description for this album: <input class='text' data-name='description' type='text' maxlength='800' placeholder='Description' value='" + oldDescription + "'></p>",
body: `<p>Please enter a description for this album: <input class='text' name='description' type='text' maxlength='800' placeholder='Description' value='${ oldDescription }'></p>`,
buttons: {
action: {
title: 'Set Description',
@ -372,69 +360,71 @@ album.setDescription = function(albumID) {
fn: basicModal.close
}
}
});
})
}
album.setPublic = function(albumID, modal, e) {
var params,
password = '';
let password = ''
albums.refresh();
albums.refresh()
if (modal===true) {
let msg = '',
text = '',
action = {};
let text = '',
action = {}
action.fn = function() {
action.fn = () => {
// Current function without showing the modal
album.setPublic(album.getID(), false, e);
// setPublic function without showing the modal
album.setPublic(album.getID(), false, e)
};
}
// Album public = Editing a shared album
if (album.json.public==='1') {
action.title = 'Edit Sharing';
text = 'The sharing-properties of this album will be changed to the following:';
action.title = 'Edit Sharing'
text = 'The sharing-properties of this album will be changed to the following:'
} else {
action.title = 'Share Album';
text = 'This album will be shared with the following properties:';
action.title = 'Share Album'
text = 'This album will be shared with the following properties:'
}
msg = `
<p class='less'>${ text }</p>
<form>
<div class='choice'>
<label>
<input type='checkbox' name='visible'>
<span class='checkbox'>${ build.iconic('check') }</span>
<span class='label'>Visible</span>
</label>
<p>Listed to visitors of your Lychee.</p>
</div>
<div class='choice'>
<label>
<input type='checkbox' name='downloadable'>
<span class='checkbox'>${ build.iconic('check') }</span>
<span class='label'>Downloadable</span>
</label>
<p>Visitors of your Lychee can download this album.</p>
</div>
<div class='choice'>
<label>
<input type='checkbox' name='password'>
<span class='checkbox'>${ build.iconic('check') }</span>
<span class='label'>Password protected</span>
</label>
<p>Only accessible with a valid password.</p>
<input class='text' data-name='password' type='password' placeholder='password' value=''>
</div>
</form>
`
let msg = `
<p class='less'>${ text }</p>
<form>
<div class='choice'>
<label>
<input type='checkbox' name='visible'>
<span class='checkbox'>${ build.iconic('check') }</span>
<span class='label'>Visible</span>
</label>
<p>Listed to visitors of your Lychee.</p>
</div>
<div class='choice'>
<label>
<input type='checkbox' name='downloadable'>
<span class='checkbox'>${ build.iconic('check') }</span>
<span class='label'>Downloadable</span>
</label>
<p>Visitors of your Lychee can download this album.</p>
</div>
<div class='choice'>
<label>
<input type='checkbox' name='password'>
<span class='checkbox'>${ build.iconic('check') }</span>
<span class='label'>Password protected</span>
</label>
<p>Only accessible with a valid password.</p>
<input class='text' name='passwordtext' type='password' placeholder='password' value=''>
</div>
</form>
`
basicModal.show({
body: msg,
@ -448,21 +438,20 @@ album.setPublic = function(albumID, modal, e) {
fn: basicModal.close
}
}
});
})
// Active visible by default (public = 0)
if ((album.json.public==='1'&&album.json.visible==='1')||
(album.json.public==='0')) $('.basicModal .choice input[name="visible"]').click();
if (album.json.downloadable==='1') $('.basicModal .choice input[name="downloadable"]').click();
if ((album.json.public==='1' && album.json.visible==='1') || (album.json.public==='0')) $('.basicModal .choice input[name="visible"]').click()
if (album.json.downloadable==='1') $('.basicModal .choice input[name="downloadable"]').click()
$('.basicModal .choice input[name="password"]').on('change', function() {
if ($(this).prop('checked')===true) $('.basicModal .choice input[data-name="password"]').show().focus();
else $('.basicModal .choice input[data-name="password"]').hide();
if ($(this).prop('checked')===true) $('.basicModal .choice input[name="passwordtext"]').show().focus()
else $('.basicModal .choice input[name="passwordtext"]').hide()
});
})
return true;
return true
}
@ -470,157 +459,154 @@ album.setPublic = function(albumID, modal, e) {
if (basicModal.visible()) {
// Visible modal => Set album public
album.json.public = '1';
album.json.public = '1'
// Set visible
if ($('.basicModal .choice input[name="visible"]:checked').length===1) album.json.visible = '1';
else album.json.visible = '0';
if ($('.basicModal .choice input[name="visible"]:checked').length===1) album.json.visible = '1'
else album.json.visible = '0'
// Set downloadable
if ($('.basicModal .choice input[name="downloadable"]:checked').length===1) album.json.downloadable = '1';
else album.json.downloadable = '0';
if ($('.basicModal .choice input[name="downloadable"]:checked').length===1) album.json.downloadable = '1'
else album.json.downloadable = '0'
// Set password
if ($('.basicModal .choice input[name="password"]:checked').length===1) {
password = $('.basicModal .choice input[data-name="password"]').val();
album.json.password = '1';
password = $('.basicModal .choice input[name="passwordtext"]').val()
album.json.password = '1'
} else {
password = '';
album.json.password = '0';
password = ''
album.json.password = '0'
}
// Modal input has been processed, now it can be closed
basicModal.close();
basicModal.close()
} else {
// Modal not visible => Set album private
album.json.public = '0';
album.json.public = '0'
}
// Set data and refresh view
if (visible.album()) {
album.json.visible = (album.json.public==='0') ? '0' : album.json.visible;
album.json.downloadable = (album.json.public==='0') ? '0' : album.json.downloadable;
album.json.password = (album.json.public==='0') ? '0' : album.json.password;
album.json.visible = (album.json.public==='0') ? '0' : album.json.visible
album.json.downloadable = (album.json.public==='0') ? '0' : album.json.downloadable
album.json.password = (album.json.public==='0') ? '0' : album.json.password
view.album.public();
view.album.visible();
view.album.downloadable();
view.album.password();
view.album.public()
view.album.visible()
view.album.downloadable()
view.album.password()
if (album.json.public==='1') contextMenu.shareAlbum(albumID, e);
if (album.json.public==='1') contextMenu.shareAlbum(albumID, e)
}
params = {
let params = {
albumID,
public: album.json.public,
password: password,
visible: album.json.visible,
downloadable: album.json.downloadable
public : album.json.public,
password : password,
visible : album.json.visible,
downloadable : album.json.downloadable
}
api.post('Album::setPublic', params, function(data) {
if (data!==true) lychee.error(null, params, data);
if (data!==true) lychee.error(null, params, data)
});
})
}
album.share = function(service) {
var link = '',
url = location.href;
let link = '',
url = location.href
switch (service) {
case 0:
link = 'https://twitter.com/share?url=' + encodeURI(url);
break;
case 1:
link = 'http://www.facebook.com/sharer.php?u=' + encodeURI(url) + '&t=' + encodeURI(album.json.title);
break;
case 2:
link = 'mailto:?subject=' + encodeURI(album.json.title) + '&body=' + encodeURI(url);
break;
case 'twitter':
link = `https://twitter.com/share?url=${ encodeURI(url) }`
break
case 'facebook':
link = `http://www.facebook.com/sharer.php?u=${ encodeURI(url) }&t=${ encodeURI(album.json.title) }`
break
case 'mail':
link = `mailto:?subject=${ encodeURI(album.json.title) }&body=${ encodeURI(url) }`
break
default:
link = '';
break;
link = ''
break
}
if (link.length>5) location.href = link;
if (link!=='') location.href = link
}
album.getArchive = function(albumID) {
var link,
url = api.path + '?function=Album::getArchive&albumID=' + albumID;
let link,
url = `${ api.path }?function=Album::getArchive&albumID=${ albumID }`
if (location.href.indexOf('index.html')>0) link = location.href.replace(location.hash, '').replace('index.html', url);
else link = location.href.replace(location.hash, '') + url;
if (location.href.indexOf('index.html')>0) link = location.href.replace(location.hash, '').replace('index.html', url)
else link = location.href.replace(location.hash, '') + url
if (lychee.publicMode===true) link += '&password=' + password.value;
if (lychee.publicMode===true) link += `&password=${ password.value }`
location.href = link;
location.href = link
}
album.merge = function(albumIDs) {
var action,
title = '',
sTitle = '',
msg = '';
let title = '',
sTitle = '',
msg = ''
if (!albumIDs) return false;
if (albumIDs instanceof Array===false) albumIDs = [albumIDs];
if (!albumIDs) return false
if (albumIDs instanceof Array===false) albumIDs = [albumIDs]
// Get title of first album
if (albums.json) title = albums.getByID(albumIDs[0]).title;
if (albums.json) title = albums.getByID(albumIDs[0]).title
if (!title) title = ''
if (!title) title = '';
title = title.replace(/'/g, '&apos;');
title = title.replace(/'/g, '&apos;')
if (albumIDs.length===2) {
// Get title of second album
if (albums.json) sTitle = albums.getByID(albumIDs[1]).title;
if (albums.json) sTitle = albums.getByID(albumIDs[1]).title
if (!sTitle) sTitle = '';
sTitle = sTitle.replace(/'/g, '&apos;');
if (!sTitle) sTitle = ''
sTitle = sTitle.replace(/'/g, '&apos;')
msg = "<p>Are you sure you want to merge the album '" + sTitle + "' into the album '" + title + "'?</p>";
msg = `<p>Are you sure you want to merge the album '${ sTitle }' into the album '${ title }'?</p>`
} else {
msg = "<p>Are you sure you want to merge all selected albums into the album '" + title + "'?</p>";
msg = `<p>Are you sure you want to merge all selected albums into the album '${ title }'?</p>`
}
action = function() {
const action = function() {
var params;
basicModal.close()
basicModal.close();
params = {
let params = {
albumIDs: albumIDs.join()
}
api.post('Album::merge', params, function(data) {
if (data!==true) {
lychee.error(null, params, data);
lychee.error(null, params, data)
} else {
albums.refresh();
albums.load();
albums.refresh()
albums.load()
}
});
})
}
@ -637,6 +623,6 @@ album.merge = function(albumIDs) {
fn: basicModal.close
}
}
});
})
}

@ -1,6 +1,6 @@
/**
* @description Takes care of every action albums can handle and execute.
* @copyright 2015 by Tobias Reich
* @description Takes care of every action albums can handle and execute.
* @copyright 2015 by Tobias Reich
*/
albums = {
@ -11,60 +11,59 @@ albums = {
albums.load = function() {
var startTime,
durationTime,
waitTime;
let startTime = new Date().getTime()
lychee.animate('#content', 'contentZoomOut');
startTime = new Date().getTime();
lychee.animate('#content', 'contentZoomOut')
if (albums.json===null) {
api.post('Album::getAll', {}, function(data) {
/* Smart Albums */
if (lychee.publicMode===false) albums._createSmartAlbums(data.smartalbums);
let waitTime = 0
// Smart Albums
if (lychee.publicMode===false) albums._createSmartAlbums(data.smartalbums)
albums.json = data;
albums.json = data
// Calculate delay
durationTime = (new Date().getTime() - startTime);
if (durationTime>300) waitTime = 0;
else waitTime = 300 - durationTime;
let durationTime = (new Date().getTime() - startTime)
if (durationTime>300) waitTime = 0
else waitTime = 300 - durationTime
// Skip delay when opening a blank Lychee
if (!visible.albums()&&!visible.photo()&&!visible.album()) waitTime = 0;
if (visible.album()&&lychee.content.html()==='') waitTime = 0;
if (!visible.albums() && !visible.photo() && !visible.album()) waitTime = 0
if (visible.album() && lychee.content.html()==='') waitTime = 0
setTimeout(function() {
header.setMode('albums');
view.albums.init();
lychee.animate('#content', 'contentZoomIn');
}, waitTime);
});
setTimeout(() => {
header.setMode('albums')
view.albums.init()
lychee.animate('#content', 'contentZoomIn')
}, waitTime)
})
} else {
setTimeout(function() {
header.setMode('albums');
view.albums.init();
lychee.animate('#content', 'contentZoomIn');
}, 300);
setTimeout(() => {
header.setMode('albums')
view.albums.init()
lychee.animate('#content', 'contentZoomIn')
}, 300)
}
}
albums.parse = function(album) {
if (album.password==='1'&&lychee.publicMode===true) {
album.thumbs[0] = 'src/images/password.svg';
album.thumbs[1] = 'src/images/password.svg';
album.thumbs[2] = 'src/images/password.svg';
if (album.password==='1' && lychee.publicMode===true) {
album.thumbs[0] = 'src/images/password.svg'
album.thumbs[1] = 'src/images/password.svg'
album.thumbs[2] = 'src/images/password.svg'
} else {
if (!album.thumbs[0]) album.thumbs[0] = 'src/images/no_images.svg';
if (!album.thumbs[1]) album.thumbs[1] = 'src/images/no_images.svg';
if (!album.thumbs[2]) album.thumbs[2] = 'src/images/no_images.svg';
if (!album.thumbs[0]) album.thumbs[0] = 'src/images/no_images.svg'
if (!album.thumbs[1]) album.thumbs[1] = 'src/images/no_images.svg'
if (!album.thumbs[2]) album.thumbs[2] = 'src/images/no_images.svg'
}
}
@ -72,36 +71,36 @@ albums.parse = function(album) {
albums._createSmartAlbums = function(data) {
data.unsorted = {
id: 0,
title: 'Unsorted',
sysdate: data.unsorted.num + ' photos',
unsorted: '1',
thumbs: data.unsorted.thumbs
};
id : 0,
title : 'Unsorted',
sysdate : data.unsorted.num + ' photos',
unsorted : '1',
thumbs : data.unsorted.thumbs
}
data.starred = {
id: 'f',
title: 'Starred',
sysdate: data.starred.num + ' photos',
star: '1',
thumbs: data.starred.thumbs
};
id : 'f',
title : 'Starred',
sysdate : data.starred.num + ' photos',
star : '1',
thumbs : data.starred.thumbs
}
data.public = {
id: 's',
title: 'Public',
sysdate: data.public.num + ' photos',
public: '1',
thumbs: data.public.thumbs
};
id : 's',
title : 'Public',
sysdate : data.public.num + ' photos',
public : '1',
thumbs : data.public.thumbs
}
data.recent = {
id: 'r',
title: 'Recent',
sysdate: data.recent.num + ' photos',
recent: '1',
thumbs: data.recent.thumbs
};
id : 'r',
title : 'Recent',
sysdate : data.recent.num + ' photos',
recent : '1',
thumbs : data.recent.thumbs
}
}
@ -109,21 +108,21 @@ albums.getByID = function(albumID) {
// Function returns the JSON of an album
if (albumID===undefined||albumID===null) return undefined;
if (!albums.json) return undefined;
if (!albums.json.albums) return undefined;
if (albumID==null) return undefined
if (!albums.json) return undefined
if (!albums.json.albums) return undefined
var json = undefined;
let json = undefined
$.each(albums.json.albums, function(i) {
let elem = albums.json.albums[i];
let elem = albums.json.albums[i]
if (elem.id==albumID) json = elem;
if (elem.id==albumID) json = elem
});
})
return json;
return json
}
@ -131,28 +130,28 @@ albums.deleteByID = function(albumID) {
// Function returns the JSON of an album
if (albumID===undefined||albumID===null) return false;
if (!albums.json) return false;
if (!albums.json.albums) return false;
if (albumID==null) return false
if (!albums.json) return false
if (!albums.json.albums) return false
var deleted = false;
var deleted = false
$.each(albums.json.albums, function(i) {
if (albums.json.albums[i].id==albumID) {
albums.json.albums.splice(i, 1);
deleted = true;
return false;
albums.json.albums.splice(i, 1)
deleted = true
return false
}
});
})
return deleted;
return deleted
}
albums.refresh = function() {
albums.json = null;
albums.json = null
}

@ -1,54 +1,50 @@
/**
* @description This module communicates with Lychee's API
* @copyright 2015 by Tobias Reich
* @description This module communicates with Lychee's API
* @copyright 2015 by Tobias Reich
*/
api = {
path: 'php/api.php',
onError: null
path : 'php/api.php',
onError : null
}
api.post = function(fn, params, callback) {
var success,
error;
loadingBar.show()
loadingBar.show();
params = $.extend({function: fn}, params)
params = $.extend({function: fn}, params);
const success = (data) => {
success = function(data) {
setTimeout(function() { loadingBar.hide() }, 100);
setTimeout(() => loadingBar.hide(), 100)
// Catch errors
if (typeof data==='string'&&
data.substring(0, 7)==='Error: ') {
api.onError(data.substring(7, data.length), params, data);
return false;
if (typeof data==='string' && data.substring(0, 7)==='Error: ') {
api.onError(data.substring(7, data.length), params, data)
return false
}
// Convert 1 to true and an empty string to false
if (data==='1') data = true;
else if (data==='') data = false;
if (data==='1') data = true
else if (data==='') data = false
// Convert to JSON if string start with '{' and ends with '}'
if (typeof data==='string'&&
data.substring(0, 1)==='{'&&
data.substring(data.length-1, data.length)==='}') data = $.parseJSON(data);
if (typeof data==='string' &&
data.substring(0, 1)==='{' &&
data.substring(data.length-1, data.length)==='}') data = $.parseJSON(data)
// Output response when debug mode is enabled
if (lychee.debugMode) console.log(data);
if (lychee.debugMode) console.log(data)
callback(data);
callback(data)
}
error = function(jqXHR, textStatus, errorThrown) {
const error = (jqXHR, textStatus, errorThrown) => {
api.onError('Server error or API not found.', params, errorThrown);
api.onError('Server error or API not found.', params, errorThrown)
}
@ -59,6 +55,6 @@ api.post = function(fn, params, callback) {
dataType: 'text',
success,
error
});
})
}

@ -1,123 +1,101 @@
/**
* @description This module is used to generate HTML-Code.
* @copyright 2015 by Tobias Reich
* @description This module is used to generate HTML-Code.
* @copyright 2015 by Tobias Reich
*/
window.build = {}
build = {}
build.iconic = function(icon, classes) {
build.iconic = function(icon, classes = '') {
var html = '';
classes = classes || '';
html = `
<svg class='iconic ${ classes }'>
<use xlink:href='#${ icon }' />
</svg>
`
return html;
return `<svg class='iconic ${ classes }'><use xlink:href='#${ icon }' /></svg>`
}
build.divider = function(title) {
var html = '';
html = `
<div class='divider'>
<h1>${ title }</h1>
</div>
`
return html;
return `<div class='divider'><h1>${ title }</h1></div>`
}
build.editIcon = function(id) {
var html = '';
html = `<div id='${ id }' class='edit'>${ build.iconic('pencil') }</div>`
return html;
return `<div id='${ id }' class='edit'>${ build.iconic('pencil') }</div>`
}
build.multiselect = function(top, left) {
var html = '';
html = `<div id='multiselect' style='top: ${ top }px; left: ${ left }px;'></div>`
return html;
return `<div id='multiselect' style='top: ${ top }px; left: ${ left }px;'></div>`
}
build.album = function(data) {
if (data===null||data===undefined) return '';
if (data==null) return ''
var html = '';
let { path: thumbPath, hasRetina: thumbRetina } = lychee.retinize(data.thumbs[0])
var {path: thumbPath, hasRetina: thumbRetina} = lychee.retinize(data.thumbs[0]);
html = `
<div class='album' data-id='${ data.id }'>
<img src='${ data.thumbs[2] }' width='200' height='200' alt='thumb' data-retina='false'>
<img src='${ data.thumbs[1] }' width='200' height='200' alt='thumb' data-retina='false'>
<img src='${ thumbPath }' width='200' height='200' alt='thumb' data-retina='${ thumbRetina }'>
<div class='overlay'>
<h1 title='${ data.title }'>${ data.title }</h1>
<a>${ data.sysdate }</a>
</div>
`
let html = `
<div class='album' data-id='${ data.id }'>
<img src='${ data.thumbs[2] }' width='200' height='200' alt='thumb' data-retina='false'>
<img src='${ data.thumbs[1] }' width='200' height='200' alt='thumb' data-retina='false'>
<img src='${ thumbPath }' width='200' height='200' alt='thumb' data-retina='${ thumbRetina }'>
<div class='overlay'>
<h1 title='${ data.title }'>${ data.title }</h1>
<a>${ data.sysdate }</a>
</div>
`
if (lychee.publicMode===false) {
if (data.star==='1') html += `<a class='badge icn-star'>${ build.iconic('star') }</a>`;
if (data.public==='1') html += `<a class='badge icn-share'>${ build.iconic('eye') }</a>`;
if (data.unsorted==='1') html += `<a class='badge'>${ build.iconic('list') }</a>`;
if (data.recent==='1') html += `<a class='badge'>${ build.iconic('clock') }</a>`;
if (data.password==='1') html += `<a class='badge'>${ build.iconic('lock-locked') }</a>`;
html += `
<div class='badges'>
<a class='badge ${ (data.star==='1' ? 'badge--visible' : '') } icn-star'>${ build.iconic('star') }</a>
<a class='badge ${ (data.public==='1' ? 'badge--visible' : '') } icn-share'>${ build.iconic('eye') }</a>
<a class='badge ${ (data.unsorted==='1' ? 'badge--visible' : '') }'>${ build.iconic('list') }</a>
<a class='badge ${ (data.recent==='1' ? 'badge--visible' : '') }'>${ build.iconic('clock') }</a>
<a class='badge ${ (data.password==='1' ? 'badge--visible' : '') }'>${ build.iconic('lock-locked') }</a>
</div>
`
}
html += '</div>'
return html;
return html
}
build.photo = function(data) {
if (data===null||data===undefined) return '';
if (data==null) return ''
var html = '';
let { path: thumbPath, hasRetina: thumbRetina } = lychee.retinize(data.thumbUrl)
var {path: thumbPath, hasRetina: thumbRetina} = lychee.retinize(data.thumbUrl);
let html = `
<div class='photo' data-album-id='${ data.album }' data-id='${ data.id }'>
<img src='${ thumbPath }' width='200' height='200' alt='thumb'>
<div class='overlay'>
<h1 title='${ data.title }'>${ data.title }</h1>
`
html = `
<div class='photo' data-album-id='${ data.album }' data-id='${ data.id }'>
<img src='${ thumbPath }' width='200' height='200' alt='thumb'>
<div class='overlay'>
<h1 title='${ data.title }'>${ data.title }</h1>
`
if (data.cameraDate==='1') html += `<a><span title='Camera Date'>${ build.iconic('camera-slr') }</span>${ data.sysdate }</a>`
else html += `<a>${ data.sysdate }</a>`
if (data.cameraDate==='1') html += `<a><span title='Camera Date'>${ build.iconic('camera-slr') }</span>${ data.sysdate }</a>`;
else html += `<a>${ data.sysdate }</a>`;
html += '</div>';
html += '</div>'
if (lychee.publicMode===false) {
if (data.star==='1') html += `<a class='badge iconic-star'>${ build.iconic('star') }</a>`;
if (data.public==='1'&&album.json.public!=='1') html += `<a class='badge iconic-share'>${ build.iconic('eye') }</a>`;
html += `
<div class='badges'>
<a class='badge ${ (data.star==='1' ? 'badge--visible' : '') } icn-star'>${ build.iconic('star') }</a>
<a class='badge ${ ((data.public==='1' && album.json.public!=='1') ? 'badge--visible' : '') } icn-share'>${ build.iconic('eye') }</a>
</div>
`
}
html += '</div>';
html += '</div>'
return html
@ -125,127 +103,120 @@ build.photo = function(data) {
build.imageview = function(data, size, visibleControls) {
if (data===null||data===undefined) return '';
if (data==null) return ''
var html = '';
html = `
<div class='arrow_wrapper arrow_wrapper--previous'><a id='previous'>${ build.iconic('caret-left') }</a></div>
<div class='arrow_wrapper arrow_wrapper--next'><a id='next'>${ build.iconic('caret-right') }</a></div>
`
let html = ''
if (size==='big') {
if (visibleControls===true)
html += `<div id='image' style='background-image: url(${ data.url })'></div>`;
else
html += `<div id='image' style='background-image: url(${ data.url });' class='full'></div>`;
if (visibleControls===true) html += `<div id='image' style='background-image: url(${ data.url })'></div>`
else html += `<div id='image' style='background-image: url(${ data.url });' class='full'></div>`
} else if (size==='medium') {
if (visibleControls===true)
html += `<div id='image' style='background-image: url(${ data.medium })'></div>`;
else
html += `<div id='image' style='background-image: url(${ data.medium });' class='full'></div>`;
if (visibleControls===true) html += `<div id='image' style='background-image: url(${ data.medium })'></div>`
else html += `<div id='image' style='background-image: url(${ data.medium });' class='full'></div>`
} else if (size==='small') {
if (visibleControls===true)
html += `<div id='image' class='small' style='background-image: url(${ data.url }); width: ${ data.width }px; height: ${ data.height }px; margin-top: -${ parseInt(data.height/2-20) }px; margin-left: -${ data.width/2 }px;'></div>`;
else
html += `<div id='image' class='small' style='background-image: url(${ data.url }); width: ${ data.width }px; height: ${ data.height }px; margin-top: -${ parseInt(data.height/2) }px; margin-left: -${ data.width/2 }px;'></div>`;
if (visibleControls===true) html += `<div id='image' class='small' style='background-image: url(${ data.url }); width: ${ data.width }px; height: ${ data.height }px; margin-top: -${ parseInt(data.height/2-20) }px; margin-left: -${ data.width/2 }px;'></div>`
else html += `<div id='image' class='small' style='background-image: url(${ data.url }); width: ${ data.width }px; height: ${ data.height }px; margin-top: -${ parseInt(data.height/2) }px; margin-left: -${ data.width/2 }px;'></div>`
}
return html;
html += `
<div class='arrow_wrapper arrow_wrapper--previous'><a id='previous'>${ build.iconic('caret-left') }</a></div>
<div class='arrow_wrapper arrow_wrapper--next'><a id='next'>${ build.iconic('caret-right') }</a></div>
`
return html
}
build.no_content = function(typ) {
var html;
html = `
<div class='no_content fadeIn'>
${ build.iconic(typ) }
`
let html = `
<div class='no_content fadeIn'>
${ build.iconic(typ) }
`
switch (typ) {
case 'magnifying-glass': html += '<p>No results</p>';
break;
case 'eye': html += '<p>No public albums</p>';
break;
case 'cog': html += '<p>No configuration</p>';
break;
case 'question-mark': html += '<p>Photo not found</p>';
break;
case 'magnifying-glass':
html += '<p>No results</p>'
break
case 'eye':
html += '<p>No public albums</p>'
break
case 'cog':
html += '<p>No configuration</p>'
break
case 'question-mark':
html += '<p>Photo not found</p>'
break
}
html += '</div>';
html += '</div>'
return html;
return html
}
build.uploadModal = function(title, files) {
var html = '',
i = 0,
file = null;
let html = `
<h1>${ title }</h1>
<div class='rows'>
`
html = `
<h1>${ title }</h1>
<div class='rows'>
`
let i = 0
while (i<files.length) {
file = files[i];
let file = files[i]
if (file.name.length>40) file.name = file.name.substr(0, 17) + '...' + file.name.substr(file.name.length-20, 20);
if (file.name.length>40) file.name = file.name.substr(0, 17) + '...' + file.name.substr(file.name.length-20, 20)
html += `
<div class='row'>
<a class='name'>${ lychee.escapeHTML(file.name) }</a>
`
html += `
<div class='row'>
<a class='name'>${ lychee.escapeHTML(file.name) }</a>
`
if (file.supported===true) html += `<a class='status'></a>`;
else html += `<a class='status error'>Not supported</a>`;
if (file.supported===true) html += `<a class='status'></a>`
else html += `<a class='status error'>Not supported</a>`
html += `
<p class='notice'></p>
</div>
`
html += `
<p class='notice'></p>
</div>
`
i++;
i++
}
html += '</div>';
html += '</div>'
return html;
return html
}
build.tags = function(tags) {
var html = '',
editTagsHTML = '';
let html = ''
if (tags!=='') {
tags = tags.split(',');
tags = tags.split(',')
tags.forEach(function(tag, index, array) {
html += `<a class='tag'>${ tag }<span data-index='${ index }'>${ build.iconic('x') }</span></a>`
});
})
} else {
html = `<div class='empty'>No Tags</div>`;
html = `<div class='empty'>No Tags</div>`
}
return html;
return html
}

@ -1,43 +1,43 @@
/**
* @description This module is used for the context menu.
* @copyright 2015 by Tobias Reich
* @description This module is used for the context menu.
* @copyright 2015 by Tobias Reich
*/
contextMenu = {}
contextMenu.add = function(e) {
var items = [
{ type: 'item', title: build.iconic('image') + 'Upload Photo', fn: function() { $('#upload_files').click() } },
let items = [
{ type: 'item', title: build.iconic('image') + 'Upload Photo', fn: () => $('#upload_files').click() },
{ type: 'separator' },
{ type: 'item', title: build.iconic('link-intact') + 'Import from Link', fn: upload.start.url },
{ type: 'item', title: build.iconic('dropbox', 'ionicons') + 'Import from Dropbox', fn: upload.start.dropbox },
{ type: 'item', title: build.iconic('terminal') + 'Import from Server', fn: upload.start.server },
{ type: 'separator' },
{ type: 'item', title: build.iconic('folder') + 'New Album', fn: album.add }
];
]
basicContext.show(items, e.originalEvent);
basicContext.show(items, e.originalEvent)
upload.notify();
upload.notify()
}
contextMenu.settings = function(e) {
var items = [
let items = [
{ type: 'item', title: build.iconic('person') + 'Change Login', fn: settings.setLogin },
{ type: 'item', title: build.iconic('sort-ascending') + 'Change Sorting', fn: settings.setSorting },
{ type: 'item', title: build.iconic('dropbox', 'ionicons') + 'Set Dropbox', fn: settings.setDropboxKey },
{ type: 'separator' },
{ type: 'item', title: build.iconic('info') + 'About Lychee', fn: function() { window.open(lychee.website) } },
{ type: 'item', title: build.iconic('wrench') + 'Diagnostics', fn: function() { window.open('plugins/check/') } },
{ type: 'item', title: build.iconic('align-left') + 'Show Log', fn: function() { window.open('plugins/displaylog/') } },
{ type: 'item', title: build.iconic('info') + 'About Lychee', fn: () => window.open(lychee.website) },
{ type: 'item', title: build.iconic('wrench') + 'Diagnostics', fn: () => window.open('plugins/check/') },
{ type: 'item', title: build.iconic('align-left') + 'Show Log', fn: () => window.open('plugins/displaylog/') },
{ type: 'separator' },
{ type: 'item', title: build.iconic('account-logout') + 'Sign Out', fn: lychee.logout }
];
]
basicContext.show(items, e.originalEvent);
basicContext.show(items, e.originalEvent)
}
@ -47,97 +47,103 @@ contextMenu.album = function(albumID, e) {
// fn must call basicContext.close() first,
// in order to keep the selection
if (albumID==='0'||albumID==='f'||albumID==='s'||albumID==='r') return false;
if (albumID==='0' || albumID==='f' || albumID==='s' || albumID==='r') return false
var items = [
{ type: 'item', title: build.iconic('pencil') + 'Rename', fn: function() { album.setTitle([albumID]) } },
{ type: 'item', title: build.iconic('collapse-left') + 'Merge', fn: function () { basicContext.close(); contextMenu.mergeAlbum(albumID, e) } },
{ type: 'item', title: build.iconic('trash') + 'Delete', fn: function() { album.delete([albumID]) } }
];
// Show merge-item when there's more than one album
let showMerge = (albums.json && albums.json.albums && Object.keys(albums.json.albums).length>1)
// Remove merge when there is only one album
if (albums.json&&albums.json.albums&&Object.keys(albums.json.albums).length<=1) items.splice(1, 1);
let items = [
{ type: 'item', title: build.iconic('pencil') + 'Rename', fn: () => album.setTitle([albumID]) },
{ type: 'item', title: build.iconic('collapse-left') + 'Merge', visible: showMerge, fn: () => { basicContext.close(); contextMenu.mergeAlbum(albumID, e) } },
{ type: 'item', title: build.iconic('trash') + 'Delete', fn: () => album.delete([albumID]) }
]
$('.album[data-id="' + albumID + '"]').addClass('active');
$('.album[data-id="' + albumID + '"]').addClass('active')
basicContext.show(items, e.originalEvent, contextMenu.close);
basicContext.show(items, e.originalEvent, contextMenu.close)
}
contextMenu.albumMulti = function(albumIDs, e) {
multiselect.stopResize();
multiselect.stopResize()
var items = [
{ type: 'item', title: build.iconic('pencil') + 'Rename All', fn: function() { album.setTitle(albumIDs) } },
{ type: 'item', title: build.iconic('collapse-left') + 'Merge All', fn: function () { album.merge(albumIDs) } },
{ type: 'item', title: build.iconic('trash') + 'Delete All', fn: function() { album.delete(albumIDs) } }
];
// Automatically merge selected albums when albumIDs contains more than one album
// Show list of albums otherwise
let autoMerge = (albumIDs.length>1 ? true : false)
basicContext.show(items, e.originalEvent, contextMenu.close);
// Show merge-item when there's more than one album
let showMerge = (albums.json && albums.json.albums && Object.keys(albums.json.albums).length>1)
let items = [
{ type: 'item', title: build.iconic('pencil') + 'Rename All', fn: () => album.setTitle(albumIDs) },
{ type: 'item', title: build.iconic('collapse-left') + 'Merge All', visible: showMerge && autoMerge, fn: () => album.merge(albumIDs) },
{ type: 'item', title: build.iconic('collapse-left') + 'Merge', visible: showMerge && !autoMerge, fn: () => { basicContext.close(); contextMenu.mergeAlbum(albumIDs[0], e) } },
{ type: 'item', title: build.iconic('trash') + 'Delete All', fn: () => album.delete(albumIDs) }
]
items.push()
basicContext.show(items, e.originalEvent, contextMenu.close)
}
contextMenu.albumTitle = function(albumID, e) {
var items = [];
api.post('Album::getAll', {}, function(data) {
if (data.albums&&data.num>1) {
let items = []
// Generate list of albums
$.each(data.albums, function(index) {
if (data.albums && data.num>1) {
var that = this,
title = '';
// Generate list of albums
$.each(data.albums, function() {
if (!that.thumbs[0]) that.thumbs[0] = 'src/images/no_cover.svg';
if (!this.thumbs[0]) this.thumbs[0] = 'src/images/no_cover.svg'
title = "<img class='cover' width='16' height='16' src='" + that.thumbs[0] + "'><div class='title'>" + that.title + "</div>";
let title = `<img class='cover' width='16' height='16' src='${ this.thumbs[0] }'><div class='title'>${ this.title }</div>`
if (that.id!=albumID) items.push({ type: 'item', title, fn: function() { lychee.goto(that.id) } });
if (this.id!=albumID) items.push({ type: 'item', title, fn: () => lychee.goto(this.id) })
});
})
items.unshift({ type: 'separator' });
items.unshift({ type: 'separator' })
}
items.unshift({ type: 'item', title: build.iconic('pencil') + 'Rename', fn: function() { album.setTitle([albumID]) } });
items.unshift({ type: 'item', title: build.iconic('pencil') + 'Rename', fn: () => album.setTitle([albumID]) })
basicContext.show(items, e.originalEvent, contextMenu.close);
basicContext.show(items, e.originalEvent, contextMenu.close)
});
})
}
contextMenu.mergeAlbum = function(albumID, e) {
var items = [];
api.post('Album::getAll', {}, function(data) {
if (data.albums&&data.num>1) {
let items = []
$.each(data.albums, function(){
if (data.albums && data.num>1) {
var that = this;
$.each(data.albums, function() {
if (!that.thumbs[0]) that.thumbs[0] = 'src/images/no_cover.svg';
that.contextTitle = "<img class='cover' width='16' height='16' src='" + that.thumbs[0] + "'><div class='title'>" + that.title + "</div>";
if (!this.thumbs[0]) this.thumbs[0] = 'src/images/no_cover.svg'
if (that.id!=albumID) items.push({ type: 'item', title: that.contextTitle, fn: function() { album.merge([albumID, that.id]) } });
let title = `<img class='cover' width='16' height='16' src='${ this.thumbs[0] }'><div class='title'>${ this.title }</div>`
});
if (this.id!=albumID) items.push({ type: 'item', title, fn: () => album.merge([albumID, this.id]) })
})
}
if (items.length===0) return false;
if (items.length===0) return false
basicContext.show(items, e.originalEvent, contextMenu.close);
basicContext.show(items, e.originalEvent, contextMenu.close)
});
})
}
@ -147,19 +153,19 @@ contextMenu.photo = function(photoID, e) {
// fn must call basicContext.close() first,
// in order to keep the selection
var items = [
{ type: 'item', title: build.iconic('star') + 'Star', fn: function() { photo.setStar([photoID]) } },
{ type: 'item', title: build.iconic('tag') + 'Tags', fn: function() { photo.editTags([photoID]) } },
let items = [
{ type: 'item', title: build.iconic('star') + 'Star', fn: () => photo.setStar([photoID]) },
{ type: 'item', title: build.iconic('tag') + 'Tags', fn: () => photo.editTags([photoID]) },
{ type: 'separator' },
{ type: 'item', title: build.iconic('pencil') + 'Rename', fn: function() { photo.setTitle([photoID]) } },
{ type: 'item', title: build.iconic('layers') + 'Duplicate', fn: function() { photo.duplicate([photoID]) } },
{ type: 'item', title: build.iconic('folder') + 'Move', fn: function() { basicContext.close(); contextMenu.move([photoID], e); } },
{ type: 'item', title: build.iconic('trash') + 'Delete', fn: function() { photo.delete([photoID]) } }
];
{ type: 'item', title: build.iconic('pencil') + 'Rename', fn: () => photo.setTitle([photoID]) },
{ type: 'item', title: build.iconic('layers') + 'Duplicate', fn: () => photo.duplicate([photoID]) },
{ type: 'item', title: build.iconic('folder') + 'Move', fn: () => { basicContext.close(); contextMenu.move([photoID], e) } },
{ type: 'item', title: build.iconic('trash') + 'Delete', fn: () => photo.delete([photoID]) }
]
$('.photo[data-id="' + photoID + '"]').addClass('active');
$('.photo[data-id="' + photoID + '"]').addClass('active')
basicContext.show(items, e.originalEvent, contextMenu.close);
basicContext.show(items, e.originalEvent, contextMenu.close)
}
@ -169,72 +175,68 @@ contextMenu.photoMulti = function(photoIDs, e) {
// fn must call basicContext.close() first,
// in order to keep the selection and multiselect
multiselect.stopResize();
multiselect.stopResize()
var items = [
{ type: 'item', title: build.iconic('star') + 'Star All', fn: function() { photo.setStar(photoIDs) } },
{ type: 'item', title: build.iconic('tag') + 'Tag All', fn: function() { photo.editTags(photoIDs) } },
let items = [
{ type: 'item', title: build.iconic('star') + 'Star All', fn: () => photo.setStar(photoIDs) },
{ type: 'item', title: build.iconic('tag') + 'Tag All', fn: () => photo.editTags(photoIDs) },
{ type: 'separator' },
{ type: 'item', title: build.iconic('pencil') + 'Rename All', fn: function() { photo.setTitle(photoIDs) } },
{ type: 'item', title: build.iconic('layers') + 'Duplicate All', fn: function() { photo.duplicate(photoIDs) } },
{ type: 'item', title: build.iconic('folder') + 'Move All', fn: function() { basicContext.close(); contextMenu.move(photoIDs, e); } },
{ type: 'item', title: build.iconic('trash') + 'Delete All', fn: function() { photo.delete(photoIDs) } }
];
{ type: 'item', title: build.iconic('pencil') + 'Rename All', fn: () => photo.setTitle(photoIDs) },
{ type: 'item', title: build.iconic('layers') + 'Duplicate All', fn: () => photo.duplicate(photoIDs) },
{ type: 'item', title: build.iconic('folder') + 'Move All', fn: () => { basicContext.close(); contextMenu.move(photoIDs, e) } },
{ type: 'item', title: build.iconic('trash') + 'Delete All', fn: () => photo.delete(photoIDs) }
]
basicContext.show(items, e.originalEvent, contextMenu.close);
basicContext.show(items, e.originalEvent, contextMenu.close)
}
contextMenu.photoTitle = function(albumID, photoID, e) {
var items = [
{ type: 'item', title: build.iconic('pencil') + 'Rename', fn: function() { photo.setTitle([photoID]) } }
];
let items = [
{ type: 'item', title: build.iconic('pencil') + 'Rename', fn: () => photo.setTitle([photoID]) }
]
var data = album.json;
let data = album.json
if (data.content!==false&&data.num>1) {
if (data.content!==false && data.num>1) {
items.push({ type: 'separator' });
items.push({ type: 'separator' })
// Generate list of albums
$.each(data.content, function(index) {
var that = this,
title = '';
title = "<img class='cover' width='16' height='16' src='" + that.thumbUrl + "'><div class='title'>" + that.title + "</div>";
let title = `<img class='cover' width='16' height='16' src='${ this.thumbUrl }'><div class='title'>${ this.title }</div>`
if (that.id!=photoID) items.push({ type: 'item', title, fn: function() { lychee.goto(albumID + '/' + that.id) } });
if (this.id!=photoID) items.push({ type: 'item', title, fn: () => lychee.goto(albumID + '/' + this.id) })
});
})
}
basicContext.show(items, e.originalEvent, contextMenu.close);
basicContext.show(items, e.originalEvent, contextMenu.close)
}
contextMenu.photoMore = function(photoID, e) {
var items = [
{ type: 'item', title: build.iconic('fullscreen-enter') + 'Full Photo', fn: function() { window.open(photo.getDirectLink()) } },
{ type: 'item', title: build.iconic('cloud-download') + 'Download', fn: function() { photo.getArchive(photoID) } }
];
// Show download-item when
// a) Public mode is off
// b) Downloadable is 1 and public mode is on
let showDownload = lychee.publicMode===false || ((album.json && album.json.downloadable && album.json.downloadable==='1') && lychee.publicMode===true)
// Remove download-item when
// 1) In public mode
// 2) Downloadable not 1 or not found
if (!(album.json&&album.json.downloadable&&album.json.downloadable==='1')&&
lychee.publicMode===true) items.splice(1, 1);
let items = [
{ type: 'item', title: build.iconic('fullscreen-enter') + 'Full Photo', fn: () => window.open(photo.getDirectLink()) },
{ type: 'item', title: build.iconic('cloud-download') + 'Download', visible: showDownload, fn: () => photo.getArchive(photoID) }
]
basicContext.show(items, e.originalEvent);
basicContext.show(items, e.originalEvent)
}
contextMenu.move = function(photoIDs, e) {
var items = [];
var items = []
api.post('Album::getAll', {}, function(data) {
@ -243,89 +245,88 @@ contextMenu.move = function(photoIDs, e) {
// Show only 'Add album' when no album available
items = [
{ type: 'item', title: 'New Album', fn: album.add }
];
]
} else {
// Generate list of albums
$.each(data.albums, function(index) {
$.each(data.albums, function() {
var that = this;
if (!this.thumbs[0]) this.thumbs[0] = 'src/images/no_cover.svg'
if (!that.thumbs[0]) that.thumbs[0] = 'src/images/no_cover.svg';
that.title = "<img class='cover' width='16' height='16' src='" + that.thumbs[0] + "'><div class='title'>" + that.title + "</div>";
let title = `<img class='cover' width='16' height='16' src='${ this.thumbs[0] }'><div class='title'>${ this.title }</div>`
if (that.id!=album.getID()) items.push({ type: 'item', title: that.title, fn: function() { photo.setAlbum(photoIDs, that.id) } });
if (this.id!=album.getID()) items.push({ type: 'item', title, fn: () => photo.setAlbum(photoIDs, this.id) })
});
})
// Show Unsorted when unsorted is not the current album
if (album.getID()!=='0') {
items.unshift({ type: 'separator' });
items.unshift({ type: 'item', title: 'Unsorted', fn: function() { photo.setAlbum(photoIDs, 0) } });
items.unshift({ type: 'separator' })
items.unshift({ type: 'item', title: 'Unsorted', fn: () => photo.setAlbum(photoIDs, 0) })
}
}
basicContext.show(items, e.originalEvent, contextMenu.close);
basicContext.show(items, e.originalEvent, contextMenu.close)
});
})
}
contextMenu.sharePhoto = function(photoID, e) {
var link = photo.getViewLink(photoID),
iconClass = 'ionicons';
let link = photo.getViewLink(photoID),
iconClass = 'ionicons'
if (photo.json.public==='2') link = location.href;
if (photo.json.public==='2') link = location.href
var items = [
{ type: 'item', title: '<input readonly id="link" value="' + link + '">', fn: function() {}, class: 'noHover' },
let items = [
{ type: 'item', title: `<input readonly id="link" value="${ link }">`, fn: () => {}, class: 'noHover' },
{ type: 'separator' },
{ type: 'item', title: build.iconic('twitter', iconClass) + 'Twitter', fn: function() { photo.share(photoID, 0) } },
{ type: 'item', title: build.iconic('facebook', iconClass) + 'Facebook', fn: function() { photo.share(photoID, 1) } },
{ type: 'item', title: build.iconic('envelope-closed') + 'Mail', fn: function() { photo.share(photoID, 2) } },
{ type: 'item', title: build.iconic('dropbox', iconClass) + 'Dropbox', fn: function() { photo.share(photoID, 3) } },
{ type: 'item', title: build.iconic('link-intact') + 'Direct Link', fn: function() { window.open(photo.getDirectLink()) } },
{ type: 'item', title: build.iconic('twitter', iconClass) + 'Twitter', fn: () => photo.share(photoID, 'twitter') },
{ type: 'item', title: build.iconic('facebook', iconClass) + 'Facebook', fn: () => photo.share(photoID, 'facebook') },
{ type: 'item', title: build.iconic('envelope-closed') + 'Mail', fn: () => photo.share(photoID, 'mail') },
{ type: 'item', title: build.iconic('dropbox', iconClass) + 'Dropbox', fn: () => photo.share(photoID, 'dropbox') },
{ type: 'item', title: build.iconic('link-intact') + 'Direct Link', fn: () => window.open(photo.getDirectLink()) },
{ type: 'separator' },
{ type: 'item', title: build.iconic('ban') + 'Make Private', fn: function() { photo.setPublic(photoID) } }
];
{ type: 'item', title: build.iconic('ban') + 'Make Private', fn: () => photo.setPublic(photoID) }
]
basicContext.show(items, e.originalEvent);
$('.basicContext input#link').focus().select();
basicContext.show(items, e.originalEvent)
$('.basicContext input#link').focus().select()
}
contextMenu.shareAlbum = function(albumID, e) {
var iconClass = 'ionicons';
let iconClass = 'ionicons'
var items = [
{ type: 'item', title: '<input readonly id="link" value="' + location.href + '">', fn: function() {}, class: 'noHover' },
let items = [
{ type: 'item', title: `<input readonly id="link" value="${ location.href }">`, fn: () => {}, class: 'noHover' },
{ type: 'separator' },
{ type: 'item', title: build.iconic('twitter', iconClass) + 'Twitter', fn: function() { album.share(0) } },
{ type: 'item', title: build.iconic('facebook', iconClass) + 'Facebook', fn: function() { album.share(1) } },
{ type: 'item', title: build.iconic('envelope-closed') + 'Mail', fn: function() { album.share(2) } },
{ type: 'item', title: build.iconic('twitter', iconClass) + 'Twitter', fn: () => album.share('twitter') },
{ type: 'item', title: build.iconic('facebook', iconClass) + 'Facebook', fn: () => album.share('facebook') },
{ type: 'item', title: build.iconic('envelope-closed') + 'Mail', fn: () => album.share('mail') },
{ type: 'separator' },
{ type: 'item', title: build.iconic('pencil') + 'Edit Sharing', fn: function() { album.setPublic(albumID, true, e) } },
{ type: 'item', title: build.iconic('ban') + 'Make Private', fn: function() { album.setPublic(albumID, false) } }
];
{ type: 'item', title: build.iconic('pencil') + 'Edit Sharing', fn: () => album.setPublic(albumID, true, e) },
{ type: 'item', title: build.iconic('ban') + 'Make Private', fn: () => album.setPublic(albumID, false) }
]
basicContext.show(items, e.originalEvent);
$('.basicContext input#link').focus().select();
basicContext.show(items, e.originalEvent)
$('.basicContext input#link').focus().select()
}
contextMenu.close = function() {
if (!visible.contextMenu()) return false;
if (!visible.contextMenu()) return false
basicContext.close();
basicContext.close()
$('.photo.active, .album.active').removeClass('active');
if (visible.multiselect()) multiselect.close();
$('.photo.active, .album.active').removeClass('active')
if (visible.multiselect()) multiselect.close()
}

@ -1,6 +1,6 @@
/**
* @description This module takes care of the header.
* @copyright 2015 by Tobias Reich
* @description This module takes care of the header.
* @copyright 2015 by Tobias Reich
*/
header = {
@ -11,174 +11,174 @@ header = {
header.dom = function(selector) {
if (selector===undefined||selector===null||selector==='') return header._dom;
return header._dom.find(selector);
if (selector==null || selector==='') return header._dom
return header._dom.find(selector)
}
header.bind = function() {
// Event Name
var eventName = ('ontouchend' in document.documentElement) ? 'touchend' : 'click';
let eventName = lychee.getEventName()
/* Buttons */
header.dom('#title').on(eventName, function(e) {
if (!$(this).hasClass('editable')) return false;
if (visible.photo()) contextMenu.photoTitle(album.getID(), photo.getID(), e);
else contextMenu.albumTitle(album.getID(), e);
});
if (!$(this).hasClass('editable')) return false
if (visible.photo()) contextMenu.photoTitle(album.getID(), photo.getID(), e)
else contextMenu.albumTitle(album.getID(), e)
})
header.dom('#button_share').on(eventName, function(e) {
if (photo.json.public==='1'||photo.json.public==='2') contextMenu.sharePhoto(photo.getID(), e);
else photo.setPublic(photo.getID(), e);
});
if (photo.json.public==='1' || photo.json.public==='2') contextMenu.sharePhoto(photo.getID(), e)
else photo.setPublic(photo.getID(), e)
})
header.dom('#button_share_album').on(eventName, function(e) {
if (album.json.public==='1') contextMenu.shareAlbum(album.getID(), e);
else album.setPublic(album.getID(), true, e);
});
header.dom('#button_signin') .on(eventName, lychee.loginDialog);
header.dom('#button_settings') .on(eventName, contextMenu.settings);
header.dom('#button_info_album') .on(eventName, sidebar.toggle);
header.dom('#button_info') .on(eventName, sidebar.toggle);
header.dom('.button_add') .on(eventName, contextMenu.add);
header.dom('#button_more') .on(eventName, function(e) { contextMenu.photoMore(photo.getID(), e) });
header.dom('#button_move') .on(eventName, function(e) { contextMenu.move([photo.getID()], e) });
header.dom('#hostedwith') .on(eventName, function() { window.open(lychee.website) });
header.dom('#button_trash_album') .on(eventName, function() { album.delete([album.getID()]) });
header.dom('#button_trash') .on(eventName, function() { photo.delete([photo.getID()]) });
header.dom('#button_archive') .on(eventName, function() { album.getArchive(album.getID()) });
header.dom('#button_star') .on(eventName, function() { photo.setStar([photo.getID()]) });
header.dom('#button_back_home') .on(eventName, function() { lychee.goto('') });
header.dom('#button_back') .on(eventName, function() { lychee.goto(album.getID()) });
if (album.json.public==='1') contextMenu.shareAlbum(album.getID(), e)
else album.setPublic(album.getID(), true, e)
})
header.dom('#button_signin') .on(eventName, lychee.loginDialog)
header.dom('#button_settings') .on(eventName, contextMenu.settings)
header.dom('#button_info_album') .on(eventName, sidebar.toggle)
header.dom('#button_info') .on(eventName, sidebar.toggle)
header.dom('.button_add') .on(eventName, contextMenu.add)
header.dom('#button_more') .on(eventName, function(e) { contextMenu.photoMore(photo.getID(), e) })
header.dom('#button_move') .on(eventName, function(e) { contextMenu.move([photo.getID()], e) })
header.dom('#hostedwith') .on(eventName, function() { window.open(lychee.website) })
header.dom('#button_trash_album') .on(eventName, function() { album.delete([album.getID()]) })
header.dom('#button_trash') .on(eventName, function() { photo.delete([photo.getID()]) })
header.dom('#button_archive') .on(eventName, function() { album.getArchive(album.getID()) })
header.dom('#button_star') .on(eventName, function() { photo.setStar([photo.getID()]) })
header.dom('#button_back_home') .on(eventName, function() { lychee.goto('') })
header.dom('#button_back') .on(eventName, function() { lychee.goto(album.getID()) })
/* Search */
header.dom('#search').on('keyup click', function() { search.find($(this).val()) });
header.dom('#search').on('keyup click', function() { search.find($(this).val()) })
header.dom('#clearSearch').on(eventName, function () {
header.dom('#search').focus();
search.reset();
});
header.dom('#search').focus()
search.reset()
})
return true;
return true
}
header.show = function() {
var newMargin = -1*($('#imageview #image').height()/2)+20;
let newMargin = (-1 * ($('#imageview #image').height()/2) + 20)
clearTimeout($(window).data('timeout'));
clearTimeout($(window).data('timeout'))
lychee.imageview.removeClass('full');
header.dom().removeClass('hidden');
lychee.imageview.removeClass('full')
header.dom().removeClass('hidden')
// Adjust position or size of photo
if ($('#imageview #image.small').length>0) $('#imageview #image').css('margin-top', newMargin);
else $('#imageview #image').removeClass('full');
if ($('#imageview #image.small').length>0) $('#imageview #image').css('margin-top', newMargin)
else $('#imageview #image').removeClass('full')
return true;
return true
}
header.hide = function(e, delay = 500) {
if (visible.photo()&&!visible.sidebar()&&!visible.contextMenu()&&!visible.message()) {
if (visible.photo() && !visible.sidebar() && !visible.contextMenu() && basicModal.visible()===false) {
var newMargin = -1*($('#imageview #image').height()/2);
clearTimeout($(window).data('timeout'));
clearTimeout($(window).data('timeout'))
$(window).data('timeout', setTimeout(function() {
lychee.imageview.addClass('full');
header.dom().addClass('hidden');
let newMargin = (-1 * ($('#imageview #image').height()/2))
lychee.imageview.addClass('full')
header.dom().addClass('hidden')
// Adjust position or size of photo
if ($('#imageview #image.small').length>0) $('#imageview #image').css('margin-top', newMargin);
else $('#imageview #image').addClass('full');
if ($('#imageview #image.small').length>0) $('#imageview #image').css('margin-top', newMargin)
else $('#imageview #image').addClass('full')
}, delay));
}, delay))
return true;
return true
}
return false;
return false
}
header.setTitle = function(title = 'Untitled') {
var $title = header.dom('#title');
let $title = header.dom('#title')
$title.html(title + build.iconic('caret-bottom'));
$title.html(title + build.iconic('caret-bottom'))
return true;
return true
}
header.setMode = function(mode) {
var albumID = album.getID();
let albumID = album.getID()
switch (mode) {
case 'albums':
header.dom().removeClass('view');
$('#tools_album, #tools_photo').hide();
$('#tools_albums').show();
header.dom().removeClass('view')
$('#tools_album, #tools_photo').hide()
$('#tools_albums').show()
return true;
break;
return true
break
case 'album':
header.dom().removeClass('view');
$('#tools_albums, #tools_photo').hide();
$('#tools_album').show();
header.dom().removeClass('view')
$('#tools_albums, #tools_photo').hide()
$('#tools_album').show()
// Hide download button when album empty
album.json.content === false ? $('#button_archive').hide() : $('#button_archive').show();
album.json.content === false ? $('#button_archive').hide() : $('#button_archive').show()
// Hide download button when not logged in and album not downloadable
if (lychee.publicMode===true&&album.json.downloadable==='0') $('#button_archive').hide();
if (lychee.publicMode===true && album.json.downloadable==='0') $('#button_archive').hide()
if (albumID==='s'||albumID==='f'||albumID==='r') {
$('#button_info_album, #button_trash_album, #button_share_album').hide();
if (albumID==='s' || albumID==='f' || albumID==='r') {
$('#button_info_album, #button_trash_album, #button_share_album').hide()
} else if (albumID==='0') {
$('#button_info_album, #button_share_album').hide();
$('#button_trash_album').show();
$('#button_info_album, #button_share_album').hide()
$('#button_trash_album').show()
} else {
$('#button_info_album, #button_trash_album, #button_share_album').show();
$('#button_info_album, #button_trash_album, #button_share_album').show()
}
return true;
break;
return true
break
case 'photo':
header.dom().addClass('view');
$('#tools_albums, #tools_album').hide();
$('#tools_photo').show();
header.dom().addClass('view')
$('#tools_albums, #tools_album').hide()
$('#tools_photo').show()
return true;
break;
return true
break
}
return false;
return false
}
header.setEditable = function(editable) {
var $title = header.dom('#title');
let $title = header.dom('#title')
// Hide editable icon when not logged in
if (lychee.publicMode===true) editable = false;
if (lychee.publicMode===true) editable = false
if (editable) $title.addClass('editable');
else $title.removeClass('editable');
if (editable) $title.addClass('editable')
else $title.removeClass('editable')
return true;
return true
}

@ -1,135 +1,128 @@
/**
* @description This module is used for bindings.
* @copyright 2015 by Tobias Reich
* @description This module is used for bindings.
* @copyright 2015 by Tobias Reich
*/
$(document).ready(function() {
/* Event Name */
var touchendSupport = (/Android|iPhone|iPad|iPod/i).test(navigator.userAgent || navigator.vendor || window.opera) && ('ontouchend' in document.documentElement),
eventName = (touchendSupport===true ? 'touchend' : 'click');
// Event Name
let eventName = lychee.getEventName()
/* Set API error handler */
api.onError = lychee.error;
// Set API error handler
api.onError = lychee.error
/* Multiselect */
multiselect.bind();
// Multiselect
multiselect.bind()
/* Header */
header.bind();
// Header
header.bind()
/* Image View */
// Image View
lychee.imageview
.on(eventName, '.arrow_wrapper--previous', photo.previous)
.on(eventName, '.arrow_wrapper--next', photo.next);
.on(eventName, '.arrow_wrapper--previous', photo.previous)
.on(eventName, '.arrow_wrapper--next', photo.next)
/* Keyboard */
// Keyboard
Mousetrap
.bind('left', function() { if (visible.photo()) $('#imageview a#previous').click() })
.bind('right', function() { if (visible.photo()) $('#imageview a#next').click() })
.bind('u', function() { $('#upload_files').click() })
.bind(['s', 'f'], function(e) {
if (visible.photo()) header.dom('#button_star').click();
else if (visible.albums()) header.dom('#search').focus();
return false;
.bind('left', function() {
if (visible.photo()) { $('#imageview a#previous').click(); return false }
})
.bind('r', function(e) {
e.preventDefault();
if (visible.album()) album.setTitle(album.getID());
else if (visible.photo()) photo.setTitle([photo.getID()]);
.bind('right', function() {
if (visible.photo()) { $('#imageview a#next').click(); return false }
})
.bind('d', function(e) {
e.preventDefault();
if (visible.photo()) photo.setDescription(photo.getID());
else if (visible.album()) album.setDescription(album.getID());
.bind('u', function() {
if (!visible.photo()) { $('#upload_files').click(); return false }
})
.bind('t', function(e) {
if (visible.photo()) {
e.preventDefault();
photo.editTags([photo.getID()]);
}
.bind(['s', 'f'], function() {
if (visible.photo()) { header.dom('#button_star').click(); return false }
else if (visible.albums()) { header.dom('#search').focus(); return false }
})
.bind('r', function() {
if (visible.album()) { album.setTitle(album.getID()); return false }
else if (visible.photo()) { photo.setTitle([photo.getID()]); return false }
})
.bind('d', function() {
if (visible.photo()) { photo.setDescription(photo.getID()); return false }
else if (visible.album()) { album.setDescription(album.getID()); return false }
})
.bind('t', function() {
if (visible.photo()) { photo.editTags([photo.getID()]); return false }
})
.bind('i', function() {
if (visible.multiselect()) return false;
else sidebar.toggle();
if (!visible.multiselect()) { sidebar.toggle(); return false }
})
.bind(['command+backspace', 'ctrl+backspace'], function() {
if (visible.photo()&&!visible.message()) photo.delete([photo.getID()]);
else if (visible.album()&&!visible.message()) album.delete([album.getID()]);
if (visible.photo() && basicModal.visible()===false) { photo.delete([photo.getID()]); return false }
else if (visible.album() && basicModal.visible()===false) { album.delete([album.getID()]); return false }
})
.bind(['command+a', 'ctrl+a'], function() {
if (visible.album()&&!visible.message()) multiselect.selectAll();
else if (visible.albums()&&!visible.message()) multiselect.selectAll();
});
if (visible.album() && basicModal.visible()===false) { multiselect.selectAll(); return false }
else if (visible.albums() && basicModal.visible()===false) { multiselect.selectAll(); return false }
})
Mousetrap.bindGlobal('enter', function() {
if (basicModal.visible()===true) basicModal.action();
});
Mousetrap.bindGlobal(['esc', 'command+up'], function(e) {
e.preventDefault();
if (basicModal.visible()===true) basicModal.cancel();
else if (visible.contextMenu()) contextMenu.close();
else if (visible.photo()) lychee.goto(album.getID());
else if (visible.album()) lychee.goto('');
else if (visible.albums()&&$('#search').val().length!==0) search.reset();
});
if (basicModal.visible()===true) basicModal.action()
})
Mousetrap.bindGlobal(['esc', 'command+up'], function() {
if (basicModal.visible()===true) basicModal.cancel()
else if (visible.contextMenu()) contextMenu.close()
else if (visible.photo()) lychee.goto(album.getID())
else if (visible.album()) lychee.goto('')
else if (visible.albums() && $('#search').val().length!==0) search.reset()
return false
})
if ('ontouchend' in document.documentElement) {
if (eventName==='touchend') {
$(document)
/* Fullscreen on mobile */
// Fullscreen on mobile
.on('touchend', '#image', function(e) {
if (swipe.obj===null||(swipe.offset>=-5&&swipe.offset<=5)) {
if (visible.header()) header.hide(e, 0);
else header.show();
if (swipe.obj==null || (swipe.offset>=-5&&swipe.offset<=5)) {
if (visible.header()) header.hide(e, 0)
else header.show()
}
})
/* Swipe on mobile */
.swipe().on('swipeStart', function() { if (visible.photo()) swipe.start($('#image')) })
.swipe().on('swipeMove', function(e) { if (visible.photo()) swipe.move(e.swipe) })
.swipe().on('swipeEnd', function(e) { if (visible.photo()) swipe.stop(e.swipe, photo.previous, photo.next) });
// Swipe on mobile
.swipe().on('swipeStart', function() { if (visible.photo()) swipe.start($('#image')) })
.swipe().on('swipeMove', function(e) { if (visible.photo()) swipe.move(e.swipe) })
.swipe().on('swipeEnd', function(e) { if (visible.photo()) swipe.stop(e.swipe, photo.previous, photo.next) })
}
/* Document */
// Document
$(document)
/* Login */
.on('keyup', '#password', function() { if ($(this).val().length>0) $(this).removeClass('error') })
/* Navigation */
// Navigation
.on('click', '.album', function() { lychee.goto($(this).attr('data-id')) })
.on('click', '.photo', function() { lychee.goto(album.getID() + '/' + $(this).attr('data-id')) })
/* Context Menu */
// Context Menu
.on('contextmenu', '.photo', function(e) { contextMenu.photo(photo.getID(), e) })
.on('contextmenu', '.album', function(e) { contextMenu.album(album.getID(), e) })
/* Upload */
.on('change', '#upload_files', function() { basicModal.close(); upload.start.local(this.files) })
.on('dragover', function(e) { e.preventDefault(); }, false)
.on('drop', function(e) {
// Upload
.on('change', '#upload_files', function() { basicModal.close(); upload.start.local(this.files) })
e.stopPropagation();
e.preventDefault();
// Drag and Drop upload
.on('dragover', function() { return false }, false)
.on('drop', function(e) {
// Close open overlays or views which are correlating with the upload
if (visible.photo()) lychee.goto(album.getID());
if (visible.contextMenu()) contextMenu.close();
if (visible.photo()) lychee.goto(album.getID())
if (visible.contextMenu()) contextMenu.close()
// Detect if dropped item is a file or a link
if (e.originalEvent.dataTransfer.files.length>0) upload.start.local(e.originalEvent.dataTransfer.files);
else if (e.originalEvent.dataTransfer.getData('Text').length>3) upload.start.url(e.originalEvent.dataTransfer.getData('Text'));
if (e.originalEvent.dataTransfer.files.length>0) upload.start.local(e.originalEvent.dataTransfer.files)
else if (e.originalEvent.dataTransfer.getData('Text').length>3) upload.start.url(e.originalEvent.dataTransfer.getData('Text'))
return true;
return false
});
})
/* Init */
lychee.init();
// Init
lychee.init()
});
})

@ -1,19 +1,19 @@
/**
* @description This module is used to show and hide the loading bar.
* @copyright 2015 by Tobias Reich
* @description This module is used to show and hide the loading bar.
* @copyright 2015 by Tobias Reich
*/
loadingBar = {
status: null,
_dom: $('#loading')
status : null,
_dom : $('#loading')
}
loadingBar.dom = function(selector) {
if (selector===undefined||selector===null||selector==='') return loadingBar._dom;
return loadingBar._dom.find(selector);
if (selector==null || selector==='') return loadingBar._dom
return loadingBar._dom.find(selector)
}
@ -22,56 +22,52 @@ loadingBar.show = function(status, errorText) {
if (status==='error') {
// Set status
loadingBar.status = 'error';
loadingBar.status = 'error'
// Parse text
if (errorText) errorText = errorText.replace('<br>', '');
if (!errorText) errorText = 'Whoops, it looks like something went wrong. Please reload the site and try again!';
if (errorText) errorText = errorText.replace('<br>', '')
if (!errorText) errorText = 'Whoops, it looks like something went wrong. Please reload the site and try again!'
// Move header down
if (visible.header()) header.dom().addClass('error');
if (visible.header()) header.dom().addClass('error')
// Modify loading
loadingBar.dom()
.removeClass('loading uploading error')
.html('<h1>Error: <span>' + errorText + '</span></h1>')
.html(`<h1>Error: <span>${ errorText }</span></h1>`)
.addClass(status)
.show();
.show()
// Set timeout
clearTimeout(loadingBar._timeout);
loadingBar._timeout = setTimeout(function() {
clearTimeout(loadingBar._timeout)
loadingBar._timeout = setTimeout(() => loadingBar.hide(true), 3000)
loadingBar.hide(true)
}, 3000);
return true;
return true
}
if (loadingBar.status===null) {
// Set status
loadingBar.status = 'loading';
loadingBar.status = 'loading'
// Set timeout
clearTimeout(loadingBar._timeout);
loadingBar._timeout = setTimeout(function() {
clearTimeout(loadingBar._timeout)
loadingBar._timeout = setTimeout(() => {
// Move header down
if (visible.header()) header.dom().addClass('loading');
if (visible.header()) header.dom().addClass('loading')
// Modify loading
loadingBar.dom()
.removeClass('loading uploading error')
.html('')
.addClass('loading')
.show();
.show()
}, 1000);
}, 1000)
return true;
return true
}
@ -79,17 +75,17 @@ loadingBar.show = function(status, errorText) {
loadingBar.hide = function(force) {
if ((loadingBar.status!=='error'&&loadingBar.status!==null)||force) {
if ((loadingBar.status!=='error' && loadingBar.status!=null) || force) {
// Remove status
loadingBar.status = null;
loadingBar.status = null
// Move header up
header.dom().removeClass('error loading');
header.dom().removeClass('error loading')
// Set timeout
clearTimeout(loadingBar._timeout);
setTimeout(function() { loadingBar.dom().hide() }, 300);
clearTimeout(loadingBar._timeout)
setTimeout(() => loadingBar.dom().hide(), 300)
}

@ -1,40 +1,38 @@
/**
* @description This module provides the basic functions of Lychee.
* @copyright 2015 by Tobias Reich
* @description This module provides the basic functions of Lychee.
* @copyright 2015 by Tobias Reich
*/
lychee = {
title: document.title,
version: '3.0.3',
version_code: '030003',
title : document.title,
version : '3.0.4',
version_code : '030004',
update_path: 'http://lychee.electerious.com/version/index.php',
updateURL: 'https://github.com/electerious/Lychee',
website: 'http://lychee.electerious.com',
update_path : 'http://lychee.electerious.com/version/index.php',
updateURL : 'https://github.com/electerious/Lychee',
website : 'http://lychee.electerious.com',
publicMode: false,
viewMode: false,
debugMode: false,
publicMode : false,
viewMode : false,
debugMode : false,
checkForUpdates:'1',
sortingPhotos: '',
sortingAlbums: '',
location: '',
checkForUpdates : '1',
sortingPhotos : '',
sortingAlbums : '',
location : '',
dropbox: false,
dropboxKey: '',
dropbox : false,
dropboxKey : '',
content: $('#content'),
imageview: $('#imageview')
content : $('#content'),
imageview : $('#imageview')
}
lychee.init = function() {
var params;
params = {
let params = {
version: lychee.version_code
}
@ -49,52 +47,51 @@ lychee.init = function() {
// Logged in
lychee.sortingPhotos = data.config.sortingPhotos || '';
lychee.sortingAlbums = data.config.sortingAlbums || '';
lychee.dropboxKey = data.config.dropboxKey || '';
lychee.location = data.config.location || '';
lychee.checkForUpdates = data.config.checkForUpdates || '1';
lychee.sortingPhotos = data.config.sortingPhotos || ''
lychee.sortingAlbums = data.config.sortingAlbums || ''
lychee.dropboxKey = data.config.dropboxKey || ''
lychee.location = data.config.location || ''
lychee.checkForUpdates = data.config.checkForUpdates || '1'
// Show dialog when there is no username and password
if (data.config.login===false) settings.createLogin();
if (data.config.login===false) settings.createLogin()
} else if (data.status===1) {
// Logged out
lychee.checkForUpdates = data.config.checkForUpdates || '1';
lychee.checkForUpdates = data.config.checkForUpdates || '1'
lychee.setMode('public');
lychee.setMode('public')
} else if (data.status===0) {
// No configuration
lychee.setMode('public');
lychee.setMode('public')
header.dom().hide();
lychee.content.hide();
$('body').append(build.no_content('cog'));
settings.createConfig();
header.dom().hide()
lychee.content.hide()
$('body').append(build.no_content('cog'))
settings.createConfig()
return true;
return true
}
$(window).bind('popstate', lychee.load);
lychee.load();
$(window).bind('popstate', lychee.load)
lychee.load()
});
})
}
lychee.login = function(data) {
var user = data.username,
password = data.password,
params;
let user = data.username,
password = data.password
params = {
let params = {
user,
password
}
@ -104,34 +101,31 @@ lychee.login = function(data) {
if (data===true) {
// Use 'try' to catch a thrown error when Safari is in private mode
try { localStorage.setItem('lychee_username', user); }
try { localStorage.setItem('lychee_username', user) }
catch (err) {}
window.location.reload();
window.location.reload()
} else {
// Show error and reactive button
basicModal.error('password');
basicModal.error('password')
}
});
})
}
lychee.loginDialog = function() {
var localUsername,
msg = '';
msg = `
<p class='signIn'>
<input class='text' name='username' autocomplete='username' data-name='username' type='text' value='' placeholder='username' autocapitalize='off' autocorrect='off'>
<input class='text' name='password' autocomplete='current-password' data-name='password' type='password' value='' placeholder='password'>
</p>
<p class='version'>Lychee ${ lychee.version }<span> &#8211; <a target='_blank' href='${ lychee.updateURL }'>Update available!</a><span></p>
`
let msg = `
<p class='signIn'>
<input class='text' name='username' autocomplete='username' type='text' value='' placeholder='username' autocapitalize='off' autocorrect='off'>
<input class='text' name='password' autocomplete='current-password' type='password' value='' placeholder='password'>
</p>
<p class='version'>Lychee ${ lychee.version }<span> &#8211; <a target='_blank' href='${ lychee.updateURL }'>Update available!</a><span></p>
`
basicModal.show({
body: msg,
@ -145,95 +139,94 @@ lychee.loginDialog = function() {
fn: basicModal.close
}
}
});
})
if (localStorage) {
localUsername = localStorage.getItem('lychee_username');
if (localUsername!==null) {
if (localUsername.length>0) $('.basicModal input[data-name="username"]').val(localUsername);
$('.basicModal input[data-name="password"]').focus();
let localUsername = localStorage.getItem('lychee_username')
if (localUsername!=null && localUsername.length>0) {
$('.basicModal input[name="username"]').val(localUsername)
$('.basicModal input[name="password"]').focus()
}
}
if (lychee.checkForUpdates==='1') lychee.getUpdate();
if (lychee.checkForUpdates==='1') lychee.getUpdate()
}
lychee.logout = function() {
api.post('Session::logout', {}, function() {
window.location.reload();
});
window.location.reload()
})
}
lychee.goto = function(url) {
if (url===undefined) url = '#';
else url = '#' + url;
if (url===undefined) url = '#'
else url = '#' + url
history.pushState(null, null, url);
lychee.load();
history.pushState(null, null, url)
lychee.load()
}
lychee.load = function() {
var albumID = '',
photoID = '',
hash = document.location.hash.replace('#', '').split('/');
let albumID = '',
photoID = '',
hash = document.location.hash.replace('#', '').split('/')
$('.no_content').remove();
contextMenu.close();
multiselect.close();
$('.no_content').remove()
contextMenu.close()
multiselect.close()
if (hash[0]!==undefined) albumID = hash[0];
if (hash[1]!==undefined) photoID = hash[1];
if (hash[0]!=null) albumID = hash[0]
if (hash[1]!=null) photoID = hash[1]
if (albumID&&photoID) {
if (albumID && photoID) {
// Trash data
photo.json = null;
photo.json = null
// Show Photo
if (lychee.content.html()===''||
($('#search').length&&$('#search').val().length!==0)) {
lychee.content.hide();
album.load(albumID, true);
if (lychee.content.html()==='' || ($('#search').length && $('#search').val().length!==0)) {
lychee.content.hide()
album.load(albumID, true)
}
photo.load(photoID, albumID);
photo.load(photoID, albumID)
} else if (albumID) {
// Trash data
photo.json = null;
photo.json = null
// Hide sidebar
if (visible.sidebar()) sidebar.toggle();
if (visible.sidebar()) sidebar.toggle()
// Show Album
if (visible.photo()) view.photo.hide();
if (album.json&&albumID==album.json.id) view.album.title();
else album.load(albumID);
if (visible.photo()) view.photo.hide()
if (album.json && albumID==album.json.id) view.album.title()
else album.load(albumID)
} else {
// Trash albums.json when filled with search results
if (search.hash!==null) {
albums.json = null;
search.hash = null;
if (search.hash!=null) {
albums.json = null
search.hash = null
}
// Trash data
album.json = null;
photo.json = null;
album.json = null
photo.json = null
// Hide sidebar
if (visible.sidebar()) sidebar.toggle();
if (visible.sidebar()) sidebar.toggle()
// Show Albums
if (visible.photo()) view.photo.hide();
albums.load();
if (visible.photo()) view.photo.hide()
albums.load()
}
@ -242,32 +235,32 @@ lychee.load = function() {
lychee.getUpdate = function() {
$.ajax({
url: lychee.update_path,
success: function(data) { if (parseInt(data)>parseInt(lychee.version_code)) $('.version span').show(); }
});
url : lychee.update_path,
success : function(data) { if (parseInt(data)>parseInt(lychee.version_code)) $('.version span').show() }
})
}
lychee.setTitle = function(title, editable) {
document.title = lychee.title + ' - ' + title;
document.title = lychee.title + ' - ' + title
header.setEditable(editable);
header.setTitle(title);
header.setEditable(editable)
header.setTitle(title)
}
lychee.setMode = function(mode) {
$('#button_settings, #button_settings, #button_search, #search, #button_trash_album, #button_share_album, .button_add, .button_divider').remove();
$('#button_trash, #button_move, #button_share, #button_star').remove();
$('#button_settings, #button_settings, #button_search, #search, #button_trash_album, #button_share_album, .button_add, .button_divider').remove()
$('#button_trash, #button_move, #button_share, #button_star').remove()
$(document)
.off('click', '#title.editable')
.off('touchend', '#title.editable')
.off('contextmenu', '.photo')
.off('contextmenu', '.album')
.off('drop');
.off('click', '#title.editable')
.off('touchend', '#title.editable')
.off('contextmenu', '.photo')
.off('contextmenu', '.album')
.off('drop')
Mousetrap
.unbind('u')
@ -277,21 +270,21 @@ lychee.setMode = function(mode) {
.unbind('d')
.unbind('t')
.unbind(['command+backspace', 'ctrl+backspace'])
.unbind(['command+a', 'ctrl+a']);
.unbind(['command+a', 'ctrl+a'])
if (mode==='public') {
header.dom('#button_signin, #hostedwith').show();
lychee.publicMode = true;
header.dom('#button_signin, #hostedwith').show()
lychee.publicMode = true
} else if (mode==='view') {
Mousetrap.unbind(['esc', 'command+up']);
$('#button_back, a#next, a#previous').remove();
$('.no_content').remove();
Mousetrap.unbind(['esc', 'command+up'])
$('#button_back, a#next, a#previous').remove()
$('.no_content').remove()
lychee.publicMode = true;
lychee.viewMode = true;
lychee.publicMode = true
lychee.viewMode = true
}
@ -299,110 +292,118 @@ lychee.setMode = function(mode) {
lychee.animate = function(obj, animation) {
var animations = [
let animations = [
['fadeIn', 'fadeOut'],
['contentZoomIn', 'contentZoomOut']
];
]
if (!obj.jQuery) obj = $(obj);
if (!obj.jQuery) obj = $(obj)
for (var i = 0; i < animations.length; i++) {
for (var x = 0; x < animations[i].length; x++) {
for (let i = 0; i < animations.length; i++) {
for (let x = 0; x < animations[i].length; x++) {
if (animations[i][x]==animation) {
obj.removeClass(animations[i][0] + ' ' + animations[i][1]).addClass(animation);
return true;
obj.removeClass(animations[i][0] + ' ' + animations[i][1]).addClass(animation)
return true
}
}
}
return false;
return false
}
lychee.escapeHTML = function(s) {
return s.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
.replace(/"/g, '&quot;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
}
lychee.retinize = function(path = '') {
var pixelRatio = window.devicePixelRatio,
extention = path.split('.').pop(),
hasRetina = extention!=='svg';
let pixelRatio = window.devicePixelRatio,
extention = path.split('.').pop(),
hasRetina = extention!=='svg'
if ((pixelRatio!==undefined&&pixelRatio>1)&&
(hasRetina===true)) {
if ((pixelRatio!=null && pixelRatio>1) && hasRetina===true) {
path = path.replace(/\.[^/.]+$/, '');
path = path + '@2x' + '.' + extention;
path = path.replace(/\.[^/.]+$/, '')
path = path + '@2x' + '.' + extention
}
return {
path,
hasRetina
};
}
}
lychee.loadDropbox = function(callback) {
if (!lychee.dropbox&&lychee.dropboxKey) {
if (!lychee.dropbox && lychee.dropboxKey) {
loadingBar.show();
loadingBar.show()
var g = document.createElement('script'),
s = document.getElementsByTagName('script')[0];
let g = document.createElement('script'),
s = document.getElementsByTagName('script')[0]
g.src = 'https://www.dropbox.com/static/api/1/dropins.js';
g.id = 'dropboxjs';
g.type = 'text/javascript';
g.async = 'true';
g.setAttribute('data-app-key', lychee.dropboxKey);
g.src = 'https://www.dropbox.com/static/api/1/dropins.js'
g.id = 'dropboxjs'
g.type = 'text/javascript'
g.async = 'true'
g.setAttribute('data-app-key', lychee.dropboxKey)
g.onload = g.onreadystatechange = function() {
var rs = this.readyState;
if (rs&&rs!=='complete'&&rs!=='loaded') return;
lychee.dropbox = true;
loadingBar.hide();
callback();
};
s.parentNode.insertBefore(g, s);
let rs = this.readyState
if (rs && rs!=='complete' && rs!=='loaded') return
lychee.dropbox = true
loadingBar.hide()
callback()
}
s.parentNode.insertBefore(g, s)
} else if (lychee.dropbox&&lychee.dropboxKey) {
callback();
callback()
} else {
settings.setDropboxKey(callback);
settings.setDropboxKey(callback)
}
}
lychee.getEventName = function() {
let touchendSupport = (/Android|iPhone|iPad|iPod/i).test(navigator.userAgent || navigator.vendor || window.opera) && ('ontouchend' in document.documentElement),
eventName = (touchendSupport===true ? 'touchend' : 'click')
return eventName
}
lychee.removeHTML = function(html = '') {
if (html==='') return html;
if (html==='') return html
var tmp = document.createElement('DIV');
tmp.innerHTML = html;
let tmp = document.createElement('DIV')
tmp.innerHTML = html
return tmp.textContent || tmp.innerText;
return (tmp.textContent || tmp.innerText)
}
lychee.error = function(errorThrown, params, data) {
console.error({
description: errorThrown,
params: params,
response: data
});
description : errorThrown,
params : params,
response : data
})
loadingBar.show('error', errorThrown);
loadingBar.show('error', errorThrown)
}

@ -1,217 +1,233 @@
/**
* @description Select multiple albums or photos.
* @copyright 2015 by Tobias Reich
* @description Select multiple albums or photos.
* @copyright 2015 by Tobias Reich
*/
multiselect = {}
multiselect.position = {
top: null,
right: null,
bottom: null,
left: null
top : null,
right : null,
bottom : null,
left : null
}
multiselect.bind = function() {
$('#content') .on('mousedown', function(e) { if (e.which===1) multiselect.show(e) });
$(document) .on('mouseup', function(e) { if (e.which===1) multiselect.getSelection(e) });
$('#content').on('mousedown', (e) => { if (e.which===1) multiselect.show(e) })
return true;
return true
}
multiselect.show = function(e) {
if (lychee.publicMode) return false;
if (visible.search()) return false;
if (!visible.albums()&&!visible.album) return false;
if ($('.album:hover, .photo:hover').length!==0) return false;
if (visible.multiselect()) $('#multiselect').remove();
if (lychee.publicMode) return false
if (!visible.albums() && !visible.album()) return false
if ($('.album:hover, .photo:hover').length!==0) return false
if (visible.search()) return false
if (visible.multiselect()) $('#multiselect').remove()
sidebar.setSelectable(false);
sidebar.setSelectable(false)
multiselect.position.top = e.pageY;
multiselect.position.right = -1 * (e.pageX - $(document).width());
multiselect.position.bottom = -1 * (multiselect.position.top - $(window).height());
multiselect.position.left = e.pageX;
multiselect.position.top = e.pageY
multiselect.position.right = -1 * (e.pageX - $(document).width())
multiselect.position.bottom = -1 * (multiselect.position.top - $(window).height())
multiselect.position.left = e.pageX
$('body').append(build.multiselect(multiselect.position.top, multiselect.position.left));
$(document).on('mousemove', multiselect.resize);
$('body').append(build.multiselect(multiselect.position.top, multiselect.position.left))
$(document)
.on('mousemove', multiselect.resize)
.on('mouseup', (e) => { if (e.which===1) multiselect.getSelection(e) })
}
multiselect.selectAll = function() {
var e,
newWidth,
newHeight;
if (lychee.publicMode) return false
if (visible.search()) return false
if (!visible.albums() && !visible.album) return false
if (visible.multiselect()) $('#multiselect').remove()
if (lychee.publicMode) return false;
if (visible.search()) return false;
if (!visible.albums()&&!visible.album) return false;
if (visible.multiselect()) $('#multiselect').remove();
sidebar.setSelectable(false)
sidebar.setSelectable(false);
multiselect.position.top = 70
multiselect.position.right = 40
multiselect.position.bottom = 90
multiselect.position.left = 20
multiselect.position.top = 70;
multiselect.position.right = 40;
multiselect.position.bottom = 90;
multiselect.position.left = 20;
$('body').append(build.multiselect(multiselect.position.top, multiselect.position.left))
$('body').append(build.multiselect(multiselect.position.top, multiselect.position.left));
let documentSize = {
width : $(document).width(),
height : $(document).height()
}
newWidth = $(document).width() - multiselect.position.right + 2;
newHeight = $(document).height() - multiselect.position.bottom;
let newSize = {
width : documentSize.width - multiselect.position.right + 2,
height : documentSize.height - multiselect.position.bottom
}
$('#multiselect').css({
width: newWidth,
height: newHeight
});
let e = {
pageX : documentSize.width - (multiselect.position.right / 2),
pageY : documentSize.height - multiselect.position.bottom
}
e = {
pageX: $(document).width() - (multiselect.position.right / 2),
pageY: $(document).height() - multiselect.position.bottom
};
$('#multiselect').css(newSize)
multiselect.getSelection(e);
multiselect.getSelection(e)
}
multiselect.resize = function(e) {
var mouse_x = e.pageX,
mouse_y = e.pageY,
newHeight,
newWidth;
if (multiselect.position.top === null ||
multiselect.position.right === null ||
multiselect.position.bottom === null ||
multiselect.position.left === null) return false
if (multiselect.position.top===null||
multiselect.position.right===null||
multiselect.position.bottom===null||
multiselect.position.left===null) return false;
let newSize = {},
documentSize = {}
if (mouse_y>=multiselect.position.top) {
// Get the position of the mouse
let mousePos = {
x : e.pageX,
y : e.pageY
}
// Default CSS
let newCSS = {
top : null,
bottom : null,
height : null,
left : null,
right : null,
width : null
}
if (mousePos.y>=multiselect.position.top) {
documentSize.height = $(document).height()
// Do not leave the screen
newHeight = mouse_y - multiselect.position.top;
if ((multiselect.position.top+newHeight)>=$(document).height())
newHeight -= (multiselect.position.top + newHeight) - $(document).height() + 2;
newSize.height = mousePos.y - multiselect.position.top
if ((multiselect.position.top+newSize.height)>=documentSize.height) {
newSize.height -= (multiselect.position.top + newSize.height) - documentSize.height + 2
}
$('#multiselect').css({
top: multiselect.position.top,
bottom: 'inherit',
height: newHeight
});
newCSS.top = multiselect.position.top
newCSS.bottom = 'inherit'
newCSS.height = newSize.height
} else {
$('#multiselect').css({
top: 'inherit',
bottom: multiselect.position.bottom,
height: multiselect.position.top - e.pageY
});
newCSS.top = 'inherit'
newCSS.bottom = multiselect.position.bottom
newCSS.height = multiselect.position.top - e.pageY
}
if (mouse_x>=multiselect.position.left) {
if (mousePos.x>=multiselect.position.left) {
documentSize.width = $(document).width()
// Do not leave the screen
newWidth = mouse_x - multiselect.position.left;
if ((multiselect.position.left+newWidth)>=$(document).width())
newWidth -= (multiselect.position.left + newWidth) - $(document).width() + 2;
newSize.width = mousePos.x - multiselect.position.left
if ((multiselect.position.left+newSize.width)>=documentSize.width) {
newSize.width -= (multiselect.position.left + newSize.width) - documentSize.width + 2
}
$('#multiselect').css({
right: 'inherit',
left: multiselect.position.left,
width: newWidth
});
newCSS.right = 'inherit'
newCSS.left = multiselect.position.left
newCSS.width = newSize.width
} else {
$('#multiselect').css({
right: multiselect.position.right,
left: 'inherit',
width: multiselect.position.left - e.pageX
});
newCSS.right = multiselect.position.right
newCSS.left = 'inherit'
newCSS.width = multiselect.position.left - e.pageX
}
// Updated all CSS properties at once
$('#multiselect').css(newCSS)
}
multiselect.stopResize = function() {
$(document).off('mousemove');
$(document).off('mousemove mouseup')
}
multiselect.getSize = function() {
if (!visible.multiselect()) return false;
if (!visible.multiselect()) return false
let $elem = $('#multiselect'),
offset = $elem.offset()
return {
top: $('#multiselect').offset().top,
left: $('#multiselect').offset().left,
width: parseInt($('#multiselect').css('width').replace('px', '')),
height: parseInt($('#multiselect').css('height').replace('px', ''))
};
top : offset.top,
left : offset.left,
width : parseInt($elem.css('width').replace('px', '')),
height : parseInt($elem.css('height').replace('px', ''))
}
}
multiselect.getSelection = function(e) {
var tolerance = 150,
id,
ids = [],
offset,
size = multiselect.getSize();
let tolerance = 150,
ids = [],
size = multiselect.getSize()
if (visible.contextMenu()) return false;
if (!visible.multiselect()) return false;
if (visible.contextMenu()) return false
if (!visible.multiselect()) return false
$('.photo, .album').each(function() {
offset = $(this).offset();
let offset = $(this).offset()
if (offset.top>=(size.top-tolerance)&&
offset.left>=(size.left-tolerance)&&
(offset.top+206)<=(size.top+size.height+tolerance)&&
if (offset.top>=(size.top-tolerance) &&
offset.left>=(size.left-tolerance) &&
(offset.top+206)<=(size.top+size.height+tolerance) &&
(offset.left+206)<=(size.left+size.width+tolerance)) {
id = $(this).data('id');
let id = $(this).data('id')
if (id!=='0'&&id!==0&&id!=='f'&&id!=='s'&&id!=='r'&&id!==null&&id!==undefined) {
if (id!=='0' && id!==0 && id!=='f' && id!=='s' && id!=='r' && id!=null) {
ids.push(id);
$(this).addClass('active');
ids.push(id)
$(this).addClass('active')
}
}
}
});
})
if (ids.length!==0&&visible.album()) contextMenu.photoMulti(ids, e);
else if (ids.length!==0&&visible.albums()) contextMenu.albumMulti(ids, e);
else multiselect.close();
if (ids.length!==0 && visible.album()) contextMenu.photoMulti(ids, e)
else if (ids.length!==0 && visible.albums()) contextMenu.albumMulti(ids, e)
else multiselect.close()
}
multiselect.close = function() {
sidebar.setSelectable(true);
sidebar.setSelectable(true)
multiselect.stopResize();
multiselect.stopResize()
multiselect.position.top = null;
multiselect.position.right = null;
multiselect.position.bottom = null;
multiselect.position.left = null;
multiselect.position.top = null
multiselect.position.right = null
multiselect.position.bottom = null
multiselect.position.left = null
lychee.animate('#multiselect', 'fadeOut');
setTimeout(function() {
$('#multiselect').remove();
}, 300);
lychee.animate('#multiselect', 'fadeOut')
setTimeout(() => $('#multiselect').remove(), 300)
}

@ -1,5 +1,5 @@
/**
* @description Controls the access to password-protected albums and photos.
* @description Controls the access to password-protected albums and photos.
* @copyright 2015 by Tobias Reich
*/
@ -9,30 +9,27 @@ password = {
}
password.get = function(albumID, callback) {
password.get = function(albumID, callback, passwd) {
var passwd = $('.basicModal input.text').val(),
params;
if (lychee.publicMode===false) callback();
else if (album.json&&album.json.password==='0') callback();
else if (albums.json&&albums.getByID(albumID).password==='0') callback();
else if (!albums.json&&!album.json) {
if (lychee.publicMode===false) callback()
else if (album.json && album.json.password==='0') callback()
else if (albums.json && albums.getByID(albumID).password==='0') callback()
else if (!albums.json && !album.json) {
// Continue without password
album.json = {password: true};
callback('');
album.json = { password: true }
callback('')
} else if (passwd==undefined) {
} else if (passwd==null) {
// Request password
password.getDialog(albumID, callback);
password.getDialog(albumID, callback)
} else {
// Check password
params = {
let params = {
albumID,
password: passwd
}
@ -40,14 +37,14 @@ password.get = function(albumID, callback) {
api.post('Album::getPublic', params, function(data) {
if (data===true) {
basicModal.close();
password.value = passwd;
callback();
basicModal.close()
password.value = passwd
callback()
} else {
basicModal.error('password');
basicModal.error('password')
}
});
})
}
@ -55,23 +52,19 @@ password.get = function(albumID, callback) {
password.getDialog = function(albumID, callback) {
var action,
cancel,
msg = '';
action = function() { password.get(albumID, callback) }
const action = (data) => password.get(albumID, callback, data.password)
cancel = function() {
basicModal.close();
if (visible.albums()===false) lychee.goto();
const cancel = () => {
basicModal.close()
if (visible.albums()===false) lychee.goto()
}
msg = `
<p>
This album is protected by a password. Enter the password below to view the photos of this album:
<input data-name='password' class='text' type='password' placeholder='password' value=''>
</p>
`
let msg = `
<p>
This album is protected by a password. Enter the password below to view the photos of this album:
<input name='password' class='text' type='password' placeholder='password' value=''>
</p>
`
basicModal.show({
body: msg,
@ -85,6 +78,6 @@ password.getDialog = function(albumID, callback) {
fn: cancel
}
}
});
})
}

@ -1,33 +1,30 @@
/**
* @description Takes care of every action a photo can handle and execute.
* @copyright 2015 by Tobias Reich
* @description Takes care of every action a photo can handle and execute.
* @copyright 2015 by Tobias Reich
*/
photo = {
json: null,
cache: null
json : null,
cache : null
}
photo.getID = function() {
var id = null;
let id = null
if (photo.json) id = photo.json.id;
else id = $('.photo:hover, .photo.active').attr('data-id');
if (photo.json) id = photo.json.id
else id = $('.photo:hover, .photo.active').attr('data-id')
if ($.isNumeric(id)===true) return id;
else return false;
if ($.isNumeric(id)===true) return id
else return false
}
photo.load = function(photoID, albumID) {
var params,
checkPasswd;
params = {
let params = {
photoID,
albumID,
password: password.value
@ -35,42 +32,40 @@ photo.load = function(photoID, albumID) {
api.post('Photo::get', params, function(data) {
const checkPasswd = function() {
if (password.value!=='') photo.load(photoID, albumID)
else setTimeout(checkPasswd, 250)
}
if (data==='Warning: Photo private!') {
lychee.content.show();
lychee.goto('');
return false;
lychee.content.show()
lychee.goto('')
return false
}
if (data==='Warning: Wrong password!') {
checkPasswd = function() {
if (password.value!=='') photo.load(photoID, albumID);
else setTimeout(checkPasswd, 250);
};
checkPasswd();
return false;
checkPasswd()
return false
}
photo.json = data;
photo.json = data
if (!visible.photo()) view.photo.show();
view.photo.init();
lychee.imageview.show();
if (!visible.photo()) view.photo.show()
view.photo.init()
lychee.imageview.show()
setTimeout(function() {
lychee.content.show();
setTimeout(() => {
lychee.content.show()
//photo.preloadNext(photoID, albumID);
}, 300);
}, 300)
});
})
}
// Preload the next photo for better response time
photo.preloadNext = function(photoID) {
var nextPhoto,
url;
// Never preload on mobile devices with bare RAM and
// mostly mobile internet
// {{ code }}
@ -80,12 +75,12 @@ photo.preloadNext = function(photoID) {
album.json.content[photoID] &&
album.json.content[photoID].nextPhoto!='') {
nextPhoto = album.json.content[photoID].nextPhoto;
url = album.json.content[nextPhoto].url;
let nextPhoto = album.json.content[photoID].nextPhoto,
url = album.json.content[nextPhoto].url
photo.cache = new Image();
photo.cache.src = url;
photo.cache.onload = function() { photo.cache = null };
photo.cache = new Image()
photo.cache.src = url
photo.cache.onload = () => photo.cache = null
}
@ -93,36 +88,36 @@ photo.preloadNext = function(photoID) {
photo.parse = function() {
if (!photo.json.title) photo.json.title = 'Untitled';
if (!photo.json.title) photo.json.title = 'Untitled'
}
photo.previous = function(animate) {
var delay = 0;
if (photo.getID()!==false &&
album.json &&
album.json.content[photo.getID()] &&
album.json.content[photo.getID()].previousPhoto!=='') {
if (photo.getID()!==false&&
album.json&&
album.json.content[photo.getID()]&&
album.json.content[photo.getID()].previousPhoto!=='') {
let delay = 0
if (animate===true) {
if (animate===true) {
delay = 200;
delay = 200
$('#image').css({
WebkitTransform: 'translateX(100%)',
MozTransform: 'translateX(100%)',
transform: 'translateX(100%)',
opacity: 0
});
$('#image').css({
WebkitTransform : 'translateX(100%)',
MozTransform : 'translateX(100%)',
transform : 'translateX(100%)',
opacity : 0
})
}
}
setTimeout(function() {
if (photo.getID()===false) return false;
lychee.goto(album.getID() + '/' + album.json.content[photo.getID()].previousPhoto)
}, delay);
setTimeout(() => {
if (photo.getID()===false) return false
lychee.goto(album.getID() + '/' + album.json.content[photo.getID()].previousPhoto)
}, delay)
}
@ -130,30 +125,30 @@ photo.previous = function(animate) {
photo.next = function(animate) {
var delay = 0;
if (photo.getID()!==false &&
album.json &&
album.json.content[photo.getID()] &&
album.json.content[photo.getID()].nextPhoto!=='') {
if (photo.getID()!==false&&
album.json&&
album.json.content[photo.getID()]&&
album.json.content[photo.getID()].nextPhoto!=='') {
let delay = 0
if (animate===true) {
if (animate===true) {
delay = 200;
delay = 200
$('#image').css({
WebkitTransform: 'translateX(-100%)',
MozTransform: 'translateX(-100%)',
transform: 'translateX(-100%)',
opacity: 0
});
$('#image').css({
WebkitTransform : 'translateX(-100%)',
MozTransform : 'translateX(-100%)',
transform : 'translateX(-100%)',
opacity : 0
})
}
}
setTimeout(function() {
if (photo.getID()===false) return false;
lychee.goto(album.getID() + '/' + album.json.content[photo.getID()].nextPhoto);
}, delay);
setTimeout(() => {
if (photo.getID()===false) return false
lychee.goto(album.getID() + '/' + album.json.content[photo.getID()].nextPhoto)
}, delay)
}
@ -161,105 +156,102 @@ photo.next = function(animate) {
photo.duplicate = function(photoIDs) {
var params;
if (!photoIDs) return false;
if (photoIDs instanceof Array===false) photoIDs = [photoIDs];
if (!photoIDs) return false
if (photoIDs instanceof Array===false) photoIDs = [photoIDs]
albums.refresh();
albums.refresh()
params = {
let params = {
photoIDs: photoIDs.join()
}
api.post('Photo::duplicate', params, function(data) {
if (data!==true) lychee.error(null, params, data);
else album.load(album.getID());
if (data!==true) lychee.error(null, params, data)
else album.load(album.getID())
});
})
}
photo.delete = function(photoIDs) {
var action = {},
cancel = {},
msg = '',
photoTitle = '';
let action = {},
cancel = {},
msg = '',
photoTitle = ''
if (!photoIDs) return false;
if (photoIDs instanceof Array===false) photoIDs = [photoIDs];
if (!photoIDs) return false
if (photoIDs instanceof Array===false) photoIDs = [photoIDs]
if (photoIDs.length===1) {
// Get title if only one photo is selected
if (visible.photo()) photoTitle = photo.json.title;
else photoTitle = album.json.content[photoIDs].title;
if (visible.photo()) photoTitle = photo.json.title
else photoTitle = album.json.content[photoIDs].title
// Fallback for photos without a title
if (photoTitle==='') photoTitle = 'Untitled';
if (photoTitle==='') photoTitle = 'Untitled'
}
action.fn = function() {
var params = '',
nextPhoto = '',
previousPhoto = '';
let nextPhoto,
previousPhoto
basicModal.close();
basicModal.close()
photoIDs.forEach(function(id, index, array) {
// Change reference for the next and previous photo
if (album.json.content[id].nextPhoto!==''||album.json.content[id].previousPhoto!=='') {
if (album.json.content[id].nextPhoto!=='' || album.json.content[id].previousPhoto!=='') {
nextPhoto = album.json.content[id].nextPhoto;
previousPhoto = album.json.content[id].previousPhoto;
nextPhoto = album.json.content[id].nextPhoto
previousPhoto = album.json.content[id].previousPhoto
album.json.content[previousPhoto].nextPhoto = nextPhoto;
album.json.content[nextPhoto].previousPhoto = previousPhoto;
album.json.content[previousPhoto].nextPhoto = nextPhoto
album.json.content[nextPhoto].previousPhoto = previousPhoto
}
delete album.json.content[id];
view.album.content.delete(id);
delete album.json.content[id]
view.album.content.delete(id)
});
})
albums.refresh();
albums.refresh()
// Go to next photo if there is a next photo and
// next photo is not the current one. Show album otherwise.
if (visible.photo()&&nextPhoto!==''&&nextPhoto!==photo.getID()) lychee.goto(album.getID() + '/' + nextPhoto);
else if (!visible.albums()) lychee.goto(album.getID());
if (visible.photo() && nextPhoto!=='' && nextPhoto!==photo.getID()) lychee.goto(album.getID() + '/' + nextPhoto)
else if (!visible.albums()) lychee.goto(album.getID())
params = {
let params = {
photoIDs: photoIDs.join()
}
api.post('Photo::delete', params, function(data) {
if (data!==true) lychee.error(null, params, data);
if (data!==true) lychee.error(null, params, data)
});
})
}
if (photoIDs.length===1) {
action.title = 'Delete Photo';
cancel.title = 'Keep Photo';
action.title = 'Delete Photo'
cancel.title = 'Keep Photo'
msg = "<p>Are you sure you want to delete the photo '" + photoTitle + "'? This action can't be undone!</p>";
msg = `<p>Are you sure you want to delete the photo '${ photoTitle }'? This action can't be undone!</p>`
} else {
action.title = 'Delete Photo';
cancel.title = 'Keep Photo';
action.title = 'Delete Photo'
cancel.title = 'Keep Photo'
msg = "<p>Are you sure you want to delete all " + photoIDs.length + " selected photo? This action can't be undone!</p>";
msg = `<p>Are you sure you want to delete all ${ photoIDs.length } selected photo? This action can't be undone!</p>`
}
@ -276,64 +268,63 @@ photo.delete = function(photoIDs) {
fn: basicModal.close
}
}
});
})
}
photo.setTitle = function(photoIDs) {
var oldTitle = '',
input = '',
msg = '',
action;
let oldTitle = '',
msg = ''
if (!photoIDs) return false;
if (photoIDs instanceof Array===false) photoIDs = [photoIDs];
if (!photoIDs) return false
if (photoIDs instanceof Array===false) photoIDs = [photoIDs]
if (photoIDs.length===1) {
// Get old title if only one photo is selected
if (photo.json) oldTitle = photo.json.title;
else if (album.json) oldTitle = album.json.content[photoIDs].title;
oldTitle = oldTitle.replace(/'/g, '&apos;');
if (photo.json) oldTitle = photo.json.title
else if (album.json) oldTitle = album.json.content[photoIDs].title
oldTitle = oldTitle.replace(/'/g, '&apos;')
}
action = function(data) {
const action = function(data) {
var params,
newTitle = data.title;
basicModal.close()
basicModal.close();
let newTitle = data.title
// Remove html from input
newTitle = lychee.removeHTML(newTitle);
newTitle = lychee.removeHTML(newTitle)
if (visible.photo()) {
photo.json.title = (newTitle==='') ? 'Untitled' : newTitle;
view.photo.title();
photo.json.title = (newTitle==='' ? 'Untitled' : newTitle)
view.photo.title()
}
photoIDs.forEach(function(id, index, array) {
album.json.content[id].title = newTitle;
view.album.content.title(id);
});
album.json.content[id].title = newTitle
view.album.content.title(id)
})
params = {
let params = {
photoIDs: photoIDs.join(),
title: newTitle
}
api.post('Photo::setTitle', params, function(data) {
if (data!==true) lychee.error(null, params, data);
if (data!==true) lychee.error(null, params, data)
});
})
}
input = "<input class='text' data-name='title' type='text' maxlength='50' placeholder='Title' value='" + oldTitle + "'>";
let input = `<input class='text' name='title' type='text' maxlength='50' placeholder='Title' value='${ oldTitle }'>`
if (photoIDs.length===1) msg = "<p>Enter a new title for this photo: " + input + "</p>";
else msg = "<p>Enter a title for all " + photoIDs.length + " selected photos: " + input + "</p>";
if (photoIDs.length===1) msg = `<p>Enter a new title for this photo: ${ input }</p>`
else msg = `<p>Enter a title for all ${ photoIDs.length } selected photos: ${ input }</p>`
basicModal.show({
body: msg,
@ -347,79 +338,78 @@ photo.setTitle = function(photoIDs) {
fn: basicModal.close
}
}
});
})
}
photo.setAlbum = function(photoIDs, albumID) {
var params,
nextPhoto,
previousPhoto;
let nextPhoto,
previousPhoto
if (!photoIDs) return false;
if (visible.photo) lychee.goto(album.getID());
if (photoIDs instanceof Array===false) photoIDs = [photoIDs];
if (!photoIDs) return false
if (photoIDs instanceof Array===false) photoIDs = [photoIDs]
if (visible.photo) lychee.goto(album.getID())
photoIDs.forEach(function(id, index, array) {
// Change reference for the next and previous photo
if (album.json.content[id].nextPhoto!==''||album.json.content[id].previousPhoto!=='') {
nextPhoto = album.json.content[id].nextPhoto;
previousPhoto = album.json.content[id].previousPhoto;
nextPhoto = album.json.content[id].nextPhoto
previousPhoto = album.json.content[id].previousPhoto
album.json.content[previousPhoto].nextPhoto = nextPhoto;
album.json.content[nextPhoto].previousPhoto = previousPhoto;
album.json.content[previousPhoto].nextPhoto = nextPhoto
album.json.content[nextPhoto].previousPhoto = previousPhoto
}
album.json.content[id] = null;
view.album.content.delete(id);
delete album.json.content[id]
view.album.content.delete(id)
});
})
albums.refresh();
albums.refresh()
params = {
let params = {
photoIDs: photoIDs.join(),
albumID
}
api.post('Photo::setAlbum', params, function(data) {
if (data!==true) lychee.error(null, params, data);
if (data!==true) lychee.error(null, params, data)
});
})
}
photo.setStar = function(photoIDs) {
var params;
if (!photoIDs) return false
if (!photoIDs) return false;
if (visible.photo()) {
photo.json.star = (photo.json.star==='0') ? '1' : '0';
view.photo.star();
photo.json.star = (photo.json.star==='0' ? '1' : '0')
view.photo.star()
}
photoIDs.forEach(function(id, index, array) {
album.json.content[id].star = (album.json.content[id].star==='0') ? '1' : '0';
view.album.content.star(id);
});
album.json.content[id].star = (album.json.content[id].star==='0' ? '1' : '0')
view.album.content.star(id)
})
albums.refresh();
albums.refresh()
params = {
let params = {
photoIDs: photoIDs.join()
}
api.post('Photo::setStar', params, function(data) {
if (data!==true) lychee.error(null, params, data);
if (data!==true) lychee.error(null, params, data)
});
})
}
@ -427,12 +417,10 @@ photo.setPublic = function(photoID, e) {
if (photo.json.public==='2') {
var action;
action = function() {
const action = function() {
basicModal.close();
lychee.goto(photo.json.original_album);
basicModal.close()
lychee.goto(photo.json.original_album)
}
@ -448,68 +436,66 @@ photo.setPublic = function(photoID, e) {
fn: basicModal.close
}
}
});
})
return false;
return false
}
if (visible.photo()) {
photo.json.public = (photo.json.public==='0') ? '1' : '0';
view.photo.public();
if (photo.json.public==='1') contextMenu.sharePhoto(photoID, e);
photo.json.public = (photo.json.public==='0' ? '1' : '0')
view.photo.public()
if (photo.json.public==='1') contextMenu.sharePhoto(photoID, e)
}
album.json.content[photoID].public = (album.json.content[photoID].public==='0') ? '1' : '0';
view.album.content.public(photoID);
album.json.content[photoID].public = (album.json.content[photoID].public==='0' ? '1' : '0')
view.album.content.public(photoID)
albums.refresh();
albums.refresh()
api.post('Photo::setPublic', { photoID }, function(data) {
if (data!==true) lychee.error(null, params, data);
if (data!==true) lychee.error(null, params, data)
});
})
}
photo.setDescription = function(photoID) {
var oldDescription = photo.json.description.replace(/'/g, '&apos;'),
action;
let oldDescription = photo.json.description.replace(/'/g, '&apos;')
action = function(data) {
const action = function(data) {
var params,
description = data.description;
basicModal.close()
basicModal.close();
let description = data.description
// Remove html from input
description = lychee.removeHTML(description);
description = lychee.removeHTML(description)
if (visible.photo()) {
photo.json.description = description;
view.photo.description();
photo.json.description = description
view.photo.description()
}
params = {
let params = {
photoID,
description
}
api.post('Photo::setDescription', params, function(data) {
if (data!==true) lychee.error(null, params, data);
if (data!==true) lychee.error(null, params, data)
});
})
}
basicModal.show({
body: "<p>Enter a description for this photo: <input class='text' data-name='description' type='text' maxlength='800' placeholder='Description' value='" + oldDescription + "'></p>",
body: `<p>Enter a description for this photo: <input class='text' name='description' type='text' maxlength='800' placeholder='Description' value='${ oldDescription }'></p>`,
buttons: {
action: {
title: 'Set Description',
@ -520,46 +506,45 @@ photo.setDescription = function(photoID) {
fn: basicModal.close
}
}
});
})
}
photo.editTags = function(photoIDs) {
var oldTags = '',
msg = '',
input = '';
let oldTags = '',
msg = ''
if (!photoIDs) return false;
if (photoIDs instanceof Array===false) photoIDs = [photoIDs];
if (!photoIDs) return false
if (photoIDs instanceof Array===false) photoIDs = [photoIDs]
// Get tags
if (visible.photo()) oldTags = photo.json.tags;
if (visible.album()&&photoIDs.length===1) oldTags = album.json.content[photoIDs].tags;
if (visible.search()&&photoIDs.length===1) oldTags = album.json.content[photoIDs].tags;
if (visible.album()&&photoIDs.length>1) {
let same = true;
if (visible.photo()) oldTags = photo.json.tags
else if (visible.album() && photoIDs.length===1) oldTags = album.json.content[photoIDs].tags
else if (visible.search() && photoIDs.length===1) oldTags = album.json.content[photoIDs].tags
else if (visible.album() && photoIDs.length>1) {
let same = true
photoIDs.forEach(function(id, index, array) {
if(album.json.content[id].tags===album.json.content[photoIDs[0]].tags&&same===true) same = true;
else same = false;
});
if (same) oldTags = album.json.content[photoIDs[0]].tags;
if (album.json.content[id].tags===album.json.content[photoIDs[0]].tags && same===true) same = true
else same = false
})
if (same===true) oldTags = album.json.content[photoIDs[0]].tags
}
// Improve tags
oldTags = oldTags.replace(/,/g, ', ');
oldTags = oldTags.replace(/,/g, ', ')
action = function(data) {
const action = function(data) {
basicModal.close();
photo.setTags(photoIDs, data.tags);
basicModal.close()
photo.setTags(photoIDs, data.tags)
}
input = "<input class='text' data-name='tags' type='text' maxlength='800' placeholder='Tags' value='" + oldTags + "'>";
let input = `<input class='text' name='tags' type='text' maxlength='800' placeholder='Tags' value='${ oldTags }'>`
if (photoIDs.length===1) msg = "<p>Enter your tags for this photo. You can add multiple tags by separating them with a comma: " + input + "</p>";
else msg = "<p>Enter your tags for all " + photoIDs.length + " selected photos. Existing tags will be overwritten. You can add multiple tags by separating them with a comma: " + input + "</p>";
if (photoIDs.length===1) msg = `<p>Enter your tags for this photo. You can add multiple tags by separating them with a comma: ${ input }</p>`
else msg = `<p>Enter your tags for all ${ photoIDs.length } selected photos. Existing tags will be overwritten. You can add multiple tags by separating them with a comma: ${ input }</p>`
basicModal.show({
body: msg,
@ -573,88 +558,85 @@ photo.editTags = function(photoIDs) {
fn: basicModal.close
}
}
});
})
}
photo.setTags = function(photoIDs, tags) {
var params;
if (!photoIDs) return false;
if (photoIDs instanceof Array===false) photoIDs = [photoIDs];
if (!photoIDs) return false
if (photoIDs instanceof Array===false) photoIDs = [photoIDs]
// Parse tags
tags = tags.replace(/(\ ,\ )|(\ ,)|(,\ )|(,{1,}\ {0,})|(,$|^,)/g, ',');
tags = tags.replace(/,$|^,|(\ ){0,}$/g, '');
tags = tags.replace(/(\ ,\ )|(\ ,)|(,\ )|(,{1,}\ {0,})|(,$|^,)/g, ',')
tags = tags.replace(/,$|^,|(\ ){0,}$/g, '')
// Remove html from input
tags = lychee.removeHTML(tags);
tags = lychee.removeHTML(tags)
if (visible.photo()) {
photo.json.tags = tags;
view.photo.tags();
photo.json.tags = tags
view.photo.tags()
}
photoIDs.forEach(function(id, index, array) {
album.json.content[id].tags = tags;
});
album.json.content[id].tags = tags
})
params = {
let params = {
photoIDs: photoIDs.join(),
tags
}
api.post('Photo::setTags', params, function(data) {
if (data!==true) lychee.error(null, params, data);
if (data!==true) lychee.error(null, params, data)
});
})
}
photo.deleteTag = function(photoID, index) {
var tags;
let tags
// Remove
tags = photo.json.tags.split(',');
tags.splice(index, 1);
tags = photo.json.tags.split(',')
tags.splice(index, 1)
// Save
photo.json.tags = tags.toString();
photo.setTags([photoID], photo.json.tags);
photo.json.tags = tags.toString()
photo.setTags([photoID], photo.json.tags)
}
photo.share = function(photoID, service) {
var link = '',
url = photo.getViewLink(photoID),
filename = 'unknown';
let link = '',
url = photo.getViewLink(photoID)
switch (service) {
case 0:
link = 'https://twitter.com/share?url=' + encodeURI(url);
break;
case 1:
link = 'http://www.facebook.com/sharer.php?u=' + encodeURI(url) + '&t=' + encodeURI(photo.json.title);
break;
case 2:
link = 'mailto:?subject=' + encodeURI(photo.json.title) + '&body=' + encodeURI(url);
break;
case 3:
case 'twitter':
link = `https://twitter.com/share?url=${ encodeURI(url) }`
break
case 'facebook':
link = `http://www.facebook.com/sharer.php?u=${ encodeURI(url) }&t=${ encodeURI(photo.json.title) }`
break
case 'mail':
link = `mailto:?subject=${ encodeURI(photo.json.title) }&body=${ encodeURI(url) }`
break
case 'dropbox':
lychee.loadDropbox(function() {
filename = photo.json.title + '.' + photo.getDirectLink().split('.').pop();
Dropbox.save(photo.getDirectLink(), filename);
});
break;
let filename = photo.json.title + '.' + photo.getDirectLink().split('.').pop()
Dropbox.save(photo.getDirectLink(), filename)
})
break
default:
link = '';
break;
link = ''
break
}
if (link.length>5) location.href = link;
if (link.length!=='') location.href = link
}
@ -663,72 +645,66 @@ photo.getSize = function() {
// Size can be 'big', 'medium' or 'small'
// Default is big
// Small is centered in the middle of the screen
var size = 'big',
scaled = false,
hasMedium = photo.json.medium!=='',
pixelRatio = window.devicePixelRatio,
view = {
width: $(window).width()-60,
height: $(window).height()-100
};
let size = 'big',
scaled = false,
hasMedium = photo.json.medium!=='',
pixelRatio = window.devicePixelRatio,
view = {
width : $(window).width() - 60,
height : $(window).height() - 100
}
// Detect if the photo will be shown scaled,
// because the screen size is smaller than the photo
if (photo.json.width>view.width||
photo.json.height>view.height) scaled = true;
if (photo.json.width>view.width || photo.json.height>view.height) scaled = true
// Calculate pixel ratio of screen
if (pixelRatio!==undefined&&pixelRatio>1) {
view.width = view.width * pixelRatio;
view.height = view.height * pixelRatio;
if (pixelRatio!=null && pixelRatio>1) {
view.width = view.width * pixelRatio
view.height = view.height * pixelRatio
}
// Medium available and
// Medium still bigger than screen
if (hasMedium===true&&
(1920>view.width&&1080>view.height)) size = 'medium';
if (hasMedium===true && (1920>view.width && 1080>view.height)) size = 'medium'
// Photo not scaled
// Photo smaller then screen
if (scaled===false&&
(photo.json.width<view.width&&
photo.json.width<view.height)) size = 'small';
if (scaled===false && (photo.json.width<view.width&& photo.json.width<view.height)) size = 'small'
return size;
return size
}
photo.getArchive = function(photoID) {
var link,
url = api.path + '?function=Photo::getArchive&photoID=' + photoID;
let link,
url = `${ api.path }?function=Photo::getArchive&photoID=${ photoID }`
if (location.href.indexOf('index.html')>0) link = location.href.replace(location.hash, '').replace('index.html', url);
else link = location.href.replace(location.hash, '') + url;
if (location.href.indexOf('index.html')>0) link = location.href.replace(location.hash, '').replace('index.html', url)
else link = location.href.replace(location.hash, '') + url
if (lychee.publicMode===true) link += '&password=' + password.value;
if (lychee.publicMode===true) link += '&password=' + password.value
location.href = link;
location.href = link
}
photo.getDirectLink = function() {
var url = '';
let url = ''
if (photo.json&&
photo.json.url&&
photo.json.url!=='') url = photo.json.url;
if (photo.json && photo.json.url && photo.json.url!=='') url = photo.json.url
return url;
return url
}
photo.getViewLink = function(photoID) {
var url = 'view.php?p=' + photoID;
let url = 'view.php?p=' + photoID
if (location.href.indexOf('index.html')>0) return location.href.replace('index.html' + location.hash, url);
else return location.href.replace(location.hash, url);
if (location.href.indexOf('index.html')>0) return location.href.replace('index.html' + location.hash, url)
else return location.href.replace(location.hash, url)
}

@ -1,6 +1,6 @@
/**
* @description Searches through your photos and albums.
* @copyright 2015 by Tobias Reich
* @description Searches through your photos and albums.
* @copyright 2015 by Tobias Reich
*/
search = {
@ -11,89 +11,90 @@ search = {
search.find = function(term) {
var albumsData = '',
photosData = '',
html = '';
clearTimeout($(window).data('timeout'))
clearTimeout($(window).data('timeout'));
$(window).data('timeout', setTimeout(function() {
if ($('#search').val().length!==0) {
api.post('search', { term }, function(data) {
let html = '',
albumsData = '',
photosData = ''
// Build albums
if (data&&data.albums) {
albums.json = { albums: data.albums };
if (data && data.albums) {
albums.json = { albums: data.albums }
$.each(albums.json.albums, function() {
albums.parse(this);
albumsData += build.album(this);
});
albums.parse(this)
albumsData += build.album(this)
})
}
// Build photos
if (data&&data.photos) {
album.json = { content: data.photos };
if (data && data.photos) {
album.json = { content: data.photos }
$.each(album.json.content, function() {
photosData += build.photo(this);
});
photosData += build.photo(this)
})
}
// 1. No albums and photos found
// 2. Only photos found
// 3. Only albums found
// 4. Albums and photos found
if (albumsData===''&&photosData==='') html = 'error';
else if (albumsData==='') html = build.divider('Photos') + photosData;
else if (photosData==='') html = build.divider('Albums') + albumsData;
else html = build.divider('Photos') + photosData + build.divider('Albums') + albumsData;
// 1. No albums and photos
// 2. Only photos
// 3. Only albums
// 4. Albums and photos
if (albumsData==='' && photosData==='') html = 'error'
else if (albumsData==='') html = build.divider('Photos') + photosData
else if (photosData==='') html = build.divider('Albums') + albumsData
else html = build.divider('Photos') + photosData + build.divider('Albums') + albumsData
// Only refresh view when search results are different
if (search.hash!==data.hash) {
$('.no_content').remove();
$('.no_content').remove()
lychee.animate('#content', 'contentZoomOut');
lychee.animate('#content', 'contentZoomOut')
search.hash = data.hash;
search.hash = data.hash
setTimeout(function() {
setTimeout(() => {
if (html==='error') {
lychee.content.html('');
$('body').append(build.no_content('magnifying-glass'));
lychee.content.html('')
$('body').append(build.no_content('magnifying-glass'))
} else {
lychee.content.html(html);
lychee.animate('#content', 'contentZoomIn');
lychee.content.html(html)
lychee.animate('#content', 'contentZoomIn')
}
}, 300);
}, 300)
}
});
})
} else search.reset();
} else search.reset()
}, 250));
}, 250))
}
search.reset = function() {
$('#search').val('');
$('.no_content').remove();
$('#search').val('')
$('.no_content').remove()
if (search.hash!==null) {
if (search.hash!=null) {
// Trash data
albums.json = null;
album.json = null;
photo.json = null;
search.hash = null;
albums.json = null
album.json = null
photo.json = null
search.hash = null
lychee.animate('.divider', 'fadeOut');
albums.load();
lychee.animate('.divider', 'fadeOut')
albums.load()
}

@ -1,33 +1,29 @@
/**
* @description Lets you change settings.
* @copyright 2015 by Tobias Reich
* @description Lets you change settings.
* @copyright 2015 by Tobias Reich
*/
settings = {}
settings.createConfig = function() {
var msg = '',
action;
const action = function(data) {
action = function(data) {
var dbName = data.dbName || '',
dbUser = data.dbUser || '',
dbPassword = data.dbPassword || '',
dbHost = data.dbHost || '',
dbTablePrefix = data.dbTablePrefix || '',
params;
let dbName = data.dbName || '',
dbUser = data.dbUser || '',
dbPassword = data.dbPassword || '',
dbHost = data.dbHost || '',
dbTablePrefix = data.dbTablePrefix || ''
if (dbUser.length<1) {
basicModal.error('dbUser');
return false;
basicModal.error('dbUser')
return false
}
if (dbHost.length<1) dbHost = 'localhost';
if (dbName.length<1) dbName = 'lychee';
if (dbHost.length<1) dbHost = 'localhost'
if (dbName.length<1) dbName = 'lychee'
params = {
let params = {
dbName,
dbUser,
dbPassword,
@ -50,9 +46,9 @@ settings.createConfig = function() {
fn: settings.createConfig
}
}
});
})
return false;
return false
}
@ -67,9 +63,9 @@ settings.createConfig = function() {
fn: settings.createConfig
}
}
});
})
return false;
return false
}
@ -84,9 +80,9 @@ settings.createConfig = function() {
fn: settings.createConfig
}
}
});
})
return false;
return false
}
@ -99,34 +95,34 @@ settings.createConfig = function() {
fn: settings.createConfig
}
}
});
})
return false;
return false
} else {
// Configuration successful
window.location.reload();
window.location.reload()
}
});
})
}
msg = `
<p>
Enter your database connection details below:
<input data-name='dbHost' class='text' type='text' placeholder='Database Host (optional)' value=''>
<input data-name='dbUser' class='text' type='text' placeholder='Database Username' value=''>
<input data-name='dbPassword' class='text' type='password' placeholder='Database Password' value=''>
</p>
<p>
Lychee will create its own database. If required, you can enter the name of an existing database instead:
<input data-name='dbName' class='text' type='text' placeholder='Database Name (optional)' value=''>
<input data-name='dbTablePrefix' class='text' type='text' placeholder='Table prefix (optional)' value=''>
</p>
`
let msg = `
<p>
Enter your database connection details below:
<input name='dbHost' class='text' type='text' placeholder='Database Host (optional)' value=''>
<input name='dbUser' class='text' type='text' placeholder='Database Username' value=''>
<input name='dbPassword' class='text' type='password' placeholder='Database Password' value=''>
</p>
<p>
Lychee will create its own database. If required, you can enter the name of an existing database instead:
<input name='dbName' class='text' type='text' placeholder='Database Name (optional)' value=''>
<input name='dbTablePrefix' class='text' type='text' placeholder='Table prefix (optional)' value=''>
</p>
`
basicModal.show({
body: msg,
@ -136,34 +132,30 @@ settings.createConfig = function() {
fn: action
}
}
});
})
}
settings.createLogin = function() {
var action,
msg = '';
action = function(data) {
const action = function(data) {
var params,
username = data.username,
password = data.password;
let username = data.username,
password = data.password
if (username.length<1) {
basicModal.error('username');
return false;
basicModal.error('username')
return false
}
if (password.length<1) {
basicModal.error('password');
return false;
basicModal.error('password')
return false
}
basicModal.close();
basicModal.close()
params = {
let params = {
username,
password
}
@ -180,21 +172,21 @@ settings.createLogin = function() {
fn: settings.createLogin
}
}
});
})
}
});
})
}
msg = `
<p>
Enter a username and password for your installation:
<input data-name='username' class='text' type='text' placeholder='New Username' value=''>
<input data-name='password' class='text' type='password' placeholder='New Password' value=''>
</p>
`
let msg = `
<p>
Enter a username and password for your installation:
<input name='username' class='text' type='text' placeholder='New Username' value=''>
<input name='password' class='text' type='password' placeholder='New Password' value=''>
</p>
`
basicModal.show({
body: msg,
@ -204,40 +196,36 @@ settings.createLogin = function() {
fn: action
}
}
});
})
}
settings.setLogin = function() {
var msg = '',
action;
action = function(data) {
const action = function(data) {
var oldPassword = data.oldPassword || '',
username = data.username || '',
password = data.password || '',
params;
let oldPassword = data.oldPassword || '',
username = data.username || '',
password = data.password || ''
if (oldPassword.length<1) {
basicModal.error('oldPassword');
return false;
basicModal.error('oldPassword')
return false
}
if (username.length<1) {
basicModal.error('username');
return false;
basicModal.error('username')
return false
}
if (password.length<1) {
basicModal.error('password');
return false;
basicModal.error('password')
return false
}
basicModal.close();
basicModal.close()
params = {
let params = {
oldPassword,
username,
password
@ -245,23 +233,23 @@ settings.setLogin = function() {
api.post('Settings::setLogin', params, function(data) {
if (data!==true) lychee.error(null, params, data);
if (data!==true) lychee.error(null, params, data)
});
})
}
msg = `
<p>
Enter your current password:
<input data-name='oldPassword' class='text' type='password' placeholder='Current Password' value=''>
</p>
<p>
Your username and password will be changed to the following:
<input data-name='username' class='text' type='text' placeholder='New Username' value=''>
<input data-name='password' class='text' type='password' placeholder='New Password' value=''>
</p>
`
let msg = `
<p>
Enter your current password:
<input name='oldPassword' class='text' type='password' placeholder='Current Password' value=''>
</p>
<p>
Your username and password will be changed to the following:
<input name='username' class='text' type='text' placeholder='New Username' value=''>
<input name='password' class='text' type='password' placeholder='New Password' value=''>
</p>
`
basicModal.show({
body: msg,
@ -275,92 +263,88 @@ settings.setLogin = function() {
fn: basicModal.close
}
}
});
})
}
settings.setSorting = function() {
var sortingPhotos = [],
sortingAlbums = [],
action,
msg = '';
let sortingPhotos = [],
sortingAlbums = []
action = function() {
const action = function() {
var params;
sortingAlbums[0] = $('.basicModal select#settings_albums_type').val()
sortingAlbums[1] = $('.basicModal select#settings_albums_order').val()
sortingAlbums[0] = $('.basicModal select#settings_albums_type').val();
sortingAlbums[1] = $('.basicModal select#settings_albums_order').val();
sortingPhotos[0] = $('.basicModal select#settings_photos_type').val()
sortingPhotos[1] = $('.basicModal select#settings_photos_order').val()
sortingPhotos[0] = $('.basicModal select#settings_photos_type').val();
sortingPhotos[1] = $('.basicModal select#settings_photos_order').val();
basicModal.close()
albums.refresh()
basicModal.close();
albums.refresh();
params = {
typeAlbums: sortingAlbums[0],
orderAlbums: sortingAlbums[1],
typePhotos: sortingPhotos[0],
orderPhotos: sortingPhotos[1]
let params = {
typeAlbums : sortingAlbums[0],
orderAlbums : sortingAlbums[1],
typePhotos : sortingPhotos[0],
orderPhotos : sortingPhotos[1]
}
api.post('Settings::setSorting', params, function(data) {
if (data===true) {
lychee.sortingAlbums = 'ORDER BY ' + sortingAlbums[0] + ' ' + sortingAlbums[1];
lychee.sortingPhotos = 'ORDER BY ' + sortingPhotos[0] + ' ' + sortingPhotos[1];
lychee.load();
} else lychee.error(null, params, data);
lychee.sortingAlbums = 'ORDER BY ' + sortingAlbums[0] + ' ' + sortingAlbums[1]
lychee.sortingPhotos = 'ORDER BY ' + sortingPhotos[0] + ' ' + sortingPhotos[1]
lychee.load()
} else lychee.error(null, params, data)
});
})
}
msg = `
<p>
Sort albums by
<span class="select">
<select id='settings_albums_type'>
<option value='id'>Creation Time</option>
<option value='title'>Title</option>
<option value='description'>Description</option>
<option value='public'>Public</option>
</select>
</span>
in an
<span class="select">
<select id='settings_albums_order'>
<option value='ASC'>Ascending</option>
<option value='DESC'>Descending</option>
</select>
</span>
order.
</p>
<p>
Sort photos by
<span class="select">
<select id='settings_photos_type'>
<option value='id'>Upload Time</option>
<option value='takestamp'>Take Date</option>
<option value='title'>Title</option>
<option value='description'>Description</option>
<option value='public'>Public</option>
<option value='star'>Star</option>
<option value='type'>Photo Format</option>
</select>
</span>
in an
<span class="select">
<select id='settings_photos_order'>
<option value='ASC'>Ascending</option>
<option value='DESC'>Descending</option>
</select>
</span>
order.
</p>
`
let msg = `
<p>
Sort albums by
<span class="select">
<select id='settings_albums_type'>
<option value='id'>Creation Time</option>
<option value='title'>Title</option>
<option value='description'>Description</option>
<option value='public'>Public</option>
</select>
</span>
in an
<span class="select">
<select id='settings_albums_order'>
<option value='ASC'>Ascending</option>
<option value='DESC'>Descending</option>
</select>
</span>
order.
</p>
<p>
Sort photos by
<span class="select">
<select id='settings_photos_type'>
<option value='id'>Upload Time</option>
<option value='takestamp'>Take Date</option>
<option value='title'>Title</option>
<option value='description'>Description</option>
<option value='public'>Public</option>
<option value='star'>Star</option>
<option value='type'>Photo Format</option>
</select>
</span>
in an
<span class="select">
<select id='settings_photos_order'>
<option value='ASC'>Ascending</option>
<option value='DESC'>Descending</option>
</select>
</span>
order.
</p>
`
basicModal.show({
body: msg,
@ -374,23 +358,23 @@ settings.setSorting = function() {
fn: basicModal.close
}
}
});
})
if (lychee.sortingAlbums!=='') {
sortingAlbums = lychee.sortingAlbums.replace('ORDER BY ', '').split(' ');
sortingAlbums = lychee.sortingAlbums.replace('ORDER BY ', '').split(' ')
$('.basicModal select#settings_albums_type').val(sortingAlbums[0]);
$('.basicModal select#settings_albums_order').val(sortingAlbums[1]);
$('.basicModal select#settings_albums_type').val(sortingAlbums[0])
$('.basicModal select#settings_albums_order').val(sortingAlbums[1])
}
if (lychee.sortingPhotos!=='') {
sortingPhotos = lychee.sortingPhotos.replace('ORDER BY ', '').split(' ');
sortingPhotos = lychee.sortingPhotos.replace('ORDER BY ', '').split(' ')
$('.basicModal select#settings_photos_type').val(sortingPhotos[0]);
$('.basicModal select#settings_photos_order').val(sortingPhotos[1]);
$('.basicModal select#settings_photos_type').val(sortingPhotos[0])
$('.basicModal select#settings_photos_order').val(sortingPhotos[1])
}
@ -398,37 +382,34 @@ settings.setSorting = function() {
settings.setDropboxKey = function(callback) {
var action,
msg = "";
action = function(data) {
const action = function(data) {
var key = data.key;
let key = data.key
if (data.key.length<1) {
basicModal.error('key');
return false;
basicModal.error('key')
return false
}
basicModal.close();
basicModal.close()
api.post('Settings::setDropboxKey', { key }, function(data) {
if (data===true) {
lychee.dropboxKey = key;
if (callback) lychee.loadDropbox(callback);
} else lychee.error(null, params, data);
lychee.dropboxKey = key
if (callback) lychee.loadDropbox(callback)
} else lychee.error(null, params, data)
});
})
}
msg = `
<p>
In order to import photos from your Dropbox, you need a valid drop-ins app key from <a href='https://www.dropbox.com/developers/apps/create'>their website</a>. Generate yourself a personal key and enter it below:
<input class='text' data-name='key' type='text' placeholder='Dropbox API Key' value='${ lychee.dropboxKey }'>
</p>
`
let msg = `
<p>
In order to import photos from your Dropbox, you need a valid drop-ins app key from <a href='https://www.dropbox.com/developers/apps/create'>their website</a>. Generate yourself a personal key and enter it below:
<input class='text' name='key' type='text' placeholder='Dropbox API Key' value='${ lychee.dropboxKey }'>
</p>
`
basicModal.show({
body: msg,
@ -442,6 +423,6 @@ settings.setDropboxKey = function(callback) {
fn: basicModal.close
}
}
});
})
}

@ -1,14 +1,14 @@
/**
* @description This module takes care of the sidebar.
* @copyright 2015 by Tobias Reich
* @description This module takes care of the sidebar.
* @copyright 2015 by Tobias Reich
*/
sidebar = {
_dom: $('#sidebar'),
types: {
DEFAULT: 0,
TAGS: 1
DEFAULT : 0,
TAGS : 1
},
createStructure: {}
@ -16,8 +16,9 @@ sidebar = {
sidebar.dom = function(selector) {
if (selector===undefined||selector===null||selector==='') return sidebar._dom;
return sidebar._dom.find(selector);
if (selector==null || selector==='') return sidebar._dom
return sidebar._dom.find(selector)
}
@ -29,56 +30,55 @@ sidebar.bind = function() {
// event handlers should be removed before binding a new one.
// Event Name
var eventName = ('ontouchend' in document.documentElement) ? 'touchend' : 'click';
let eventName = lychee.getEventName();
sidebar
.dom('#edit_title')
.off(eventName)
.on(eventName, function() {
if (visible.photo()) photo.setTitle([photo.getID()]);
else if (visible.album()) album.setTitle([album.getID()]);
});
if (visible.photo()) photo.setTitle([photo.getID()])
else if (visible.album()) album.setTitle([album.getID()])
})
sidebar
.dom('#edit_description')
.off(eventName)
.on(eventName, function() {
if (visible.photo()) photo.setDescription(photo.getID());
else if (visible.album()) album.setDescription(album.getID());
});
if (visible.photo()) photo.setDescription(photo.getID())
else if (visible.album()) album.setDescription(album.getID())
})
sidebar
.dom('#edit_tags')
.off(eventName)
.on(eventName, function() {
photo.editTags([photo.getID()])
});
})
sidebar
.dom('#tags .tag span')
.off(eventName)
.on(eventName, function() {
photo.deleteTag(photo.getID(), $(this).data('index'))
});
})
return true;
return true
}
sidebar.toggle = function() {
if (visible.sidebar()||
visible.sidebarbutton()) {
if (visible.sidebar() || visible.sidebarbutton()) {
header.dom('.button--info').toggleClass('active');
lychee.content.toggleClass('sidebar');
sidebar.dom().toggleClass('active');
header.dom('.button--info').toggleClass('active')
lychee.content.toggleClass('sidebar')
sidebar.dom().toggleClass('active')
return true;
return true
}
return false;
return false
}
@ -88,67 +88,67 @@ sidebar.setSelectable = function(selectable = true) {
// Selection needs to be deactivated to prevent an unwanted selection
// while using multiselect.
if (selectable===true) sidebar.dom().removeClass('notSelectable');
else sidebar.dom().addClass('notSelectable');
if (selectable===true) sidebar.dom().removeClass('notSelectable')
else sidebar.dom().addClass('notSelectable')
}
sidebar.changeAttr = function(attr, value = '-') {
if (attr===undefined||attr===null||attr==='') return false;
if (attr==null || attr==='') return false
// Set a default for the value
if (value===''||value===null) value = '-';
if (value==null || value==='') value = '-'
sidebar.dom('.attr_' + attr).html(value);
sidebar.dom('.attr_' + attr).html(value)
return true;
return true
}
sidebar.createStructure.photo = function(data) {
if (data===undefined||data===null||data==='') return false;
if (data==null || data==='') return false
var editable = false,
exifHash = data.takestamp + data.make + data.model + data.shutter + data.aperture + data.focal + data.iso,
structure = {},
_public = '';
let editable = false,
exifHash = data.takestamp + data.make + data.model + data.shutter + data.aperture + data.focal + data.iso,
structure = {},
_public = ''
// Enable editable when user logged in
if (lychee.publicMode===false) editable = true;
if (lychee.publicMode===false) editable = true
// Set value for public
switch (data.public) {
case '0': _public = 'No';
break;
case '1': _public = 'Yes';
break;
case '2': _public = 'Yes (Album)';
break;
default: _public = '-';
break;
case '0' : _public = 'No'
break
case '1' : _public = 'Yes'
break
case '2' : _public = 'Yes (Album)'
break
default : _public = '-'
break
}
structure.basics = {
title: 'Basics',
type: sidebar.types.DEFAULT,
rows: [
{ title: 'Title', value: data.title, editable },
{ title: 'Uploaded', value: data.sysdate },
{ title: 'Description', value: data.description, editable },
title : 'Basics',
type : sidebar.types.DEFAULT,
rows : [
{ title: 'Title', value: data.title, editable },
{ title: 'Uploaded', value: data.sysdate },
{ title: 'Description', value: data.description, editable }
]
}
structure.image = {
title: 'Image',
type: sidebar.types.DEFAULT,
rows: [
{ title: 'Size', value: data.size },
{ title: 'Format', value: data.type },
{ title: 'Resolution', value: data.width + ' x ' + data.height }
title : 'Image',
type : sidebar.types.DEFAULT,
rows : [
{ title: 'Size', value: data.size },
{ title: 'Format', value: data.type },
{ title: 'Resolution', value: data.width + ' x ' + data.height }
]
}
@ -156,9 +156,9 @@ sidebar.createStructure.photo = function(data) {
if (lychee.publicMode===false) {
structure.tags = {
title: 'Tags',
type: sidebar.types.TAGS,
value: build.tags(data.tags),
title : 'Tags',
type : sidebar.types.TAGS,
value : build.tags(data.tags),
editable
}
@ -172,16 +172,16 @@ sidebar.createStructure.photo = function(data) {
if (exifHash!=='0') {
structure.exif = {
title: 'Camera',
type: sidebar.types.DEFAULT,
rows: [
{ title: 'Captured', value: data.takedate },
{ title: 'Make', value: data.make },
{ title: 'Type/Model', value: data.model },
{ title: 'Shutter Speed', value: data.shutter },
{ title: 'Aperture', value: data.aperture },
{ title: 'Focal Length', value: data.focal },
{ title: 'ISO', value: data.iso }
title : 'Camera',
type : sidebar.types.DEFAULT,
rows : [
{ title: 'Captured', value: data.takedate },
{ title: 'Make', value: data.make },
{ title: 'Type/Model', value: data.model },
{ title: 'Shutter Speed', value: data.shutter },
{ title: 'Aperture', value: data.aperture },
{ title: 'Focal Length', value: data.focal },
{ title: 'ISO', value: data.iso }
]
}
@ -192,10 +192,10 @@ sidebar.createStructure.photo = function(data) {
}
structure.sharing = {
title: 'Sharing',
type: sidebar.types.DEFAULT,
rows: [
{ title: 'Public', value: _public },
title : 'Sharing',
type : sidebar.types.DEFAULT,
rows : [
{ title: 'Public', value: _public }
]
}
@ -208,98 +208,98 @@ sidebar.createStructure.photo = function(data) {
structure.sharing
]
return structure;
return structure
}
sidebar.createStructure.album = function(data) {
if (data===undefined||data===null||data==='') return false;
if (data==null || data==='') return false
var editable = false,
structure = {},
_public = '',
visible = '',
downloadable = '',
password = '';
let editable = false,
structure = {},
_public = '',
visible = '',
downloadable = '',
password = ''
// Enable editable when user logged in
if (lychee.publicMode===false) editable = true;
if (lychee.publicMode===false) editable = true
// Set value for public
switch (data.public) {
case '0': _public = 'No';
break;
case '1': _public = 'Yes';
break;
default: _public = '-';
break;
case '0' : _public = 'No'
break
case '1' : _public = 'Yes'
break
default : _public = '-'
break
}
// Set value for visible
switch (data.visible) {
case '0': visible = 'No';
break;
case '1': visible = 'Yes';
break;
default: visible = '-';
break;
case '0' : visible = 'No'
break
case '1' : visible = 'Yes'
break
default : visible = '-'
break
}
// Set value for downloadable
switch (data.downloadable) {
case '0': downloadable = 'No';
break;
case '1': downloadable = 'Yes';
break;
default: downloadable = '-';
break;
case '0' : downloadable = 'No'
break
case '1' : downloadable = 'Yes'
break
default : downloadable = '-'
break
}
// Set value for password
switch (data.password) {
case '0': password = 'No';
break;
case '1': password = 'Yes';
break;
default: password = '-';
break;
case '0' : password = 'No'
break
case '1' : password = 'Yes'
break
default : password = '-'
break
}
structure.basics = {
title: 'Basics',
type: sidebar.types.DEFAULT,
rows: [
{ title: 'Title', value: data.title, editable },
{ title: 'Description', value: data.description, editable }
title : 'Basics',
type : sidebar.types.DEFAULT,
rows : [
{ title: 'Title', value: data.title, editable },
{ title: 'Description', value: data.description, editable }
]
}
structure.album = {
title: 'Album',
type: sidebar.types.DEFAULT,
rows: [
{ title: 'Created', value: data.sysdate },
{ title: 'Images', value: data.num }
title : 'Album',
type : sidebar.types.DEFAULT,
rows : [
{ title: 'Created', value: data.sysdate },
{ title: 'Images', value: data.num }
]
}
structure.share = {
title: 'Share',
type: sidebar.types.DEFAULT,
rows: [
{ title: 'Public', value: _public },
{ title: 'Visible', value: visible },
{ title: 'Downloadable', value: downloadable },
{ title: 'Password', value: password }
title : 'Share',
type : sidebar.types.DEFAULT,
rows : [
{ title: 'Public', value: _public },
{ title: 'Visible', value: visible },
{ title: 'Downloadable', value: downloadable },
{ title: 'Password', value: password }
]
}
@ -310,87 +310,87 @@ sidebar.createStructure.album = function(data) {
structure.share
]
return structure;
return structure
}
sidebar.render = function(structure) {
if (structure===undefined||structure===null||structure==='') return false;
if (structure==null || structure==='') return false
var html = '';
let html = ''
var renderDefault = function(section) {
let renderDefault = function(section) {
let _html = '';
let _html = ''
_html += `
<div class='divider'>
<h1>${ section.title }</h1>
</div>
<table>
`
_html += `
<div class='divider'>
<h1>${ section.title }</h1>
</div>
<table>
`
section.rows.forEach(function(row) {
let value = row.value;
let value = row.value
// Set a default for the value
if (value===''||value===null||value===undefined) value = '-';
if (value==='' || value==null) value = '-'
// Wrap span-element around value for easier selecting on change
value = `<span class='attr_${ row.title.toLowerCase() }'>${ value }</span>`;
value = `<span class='attr_${ row.title.toLowerCase() }'>${ value }</span>`
// Add edit-icon to the value when editable
if (row.editable===true) value += ' ' + build.editIcon('edit_' + row.title.toLowerCase());
if (row.editable===true) value += ' ' + build.editIcon('edit_' + row.title.toLowerCase())
_html += `
<tr>
<td>${ row.title }</td>
<td>${ value }</td>
</tr>
`
_html += `
<tr>
<td>${ row.title }</td>
<td>${ value }</td>
</tr>
`
});
})
_html += `
</table>
`
_html += `
</table>
`
return _html;
return _html
};
}
var renderTags = function(section) {
let renderTags = function(section) {
let _html = '';
let _html = ''
_html += `
<div class='divider'>
<h1>${ section.title }</h1>
</div>
<div id='tags'>
<div class='attr_${ section.title.toLowerCase() }'>${ section.value }</div>
`
_html += `
<div class='divider'>
<h1>${ section.title }</h1>
</div>
<div id='tags'>
<div class='attr_${ section.title.toLowerCase() }'>${ section.value }</div>
`
// Add edit-icon to the value when editable
if (section.editable===true) _html += build.editIcon('edit_tags');
if (section.editable===true) _html += build.editIcon('edit_tags')
_html += `
</div>
`
_html += `
</div>
`
return _html;
return _html
}
structure.forEach(function(section) {
if (section.type===sidebar.types.DEFAULT) html += renderDefault(section);
else if (section.type===sidebar.types.TAGS) html += renderTags(section);
if (section.type===sidebar.types.DEFAULT) html += renderDefault(section)
else if (section.type===sidebar.types.TAGS) html += renderTags(section)
});
})
return html;
return html
}

@ -1,52 +1,52 @@
/**
* @description Swipes and moves an object.
* @copyright 2015 by Tobias Reich
* @description Swipes and moves an object.
* @copyright 2015 by Tobias Reich
*/
swipe = {
obj: null,
tolerance: 150,
offset: 0
obj : null,
tolerance : 150,
offset : 0
}
swipe.start = function(obj, tolerance) {
if (obj) swipe.obj = obj;
if (tolerance) swipe.tolerance = tolerance;
if (obj) swipe.obj = obj
if (tolerance) swipe.tolerance = tolerance
return true;
return true
}
swipe.move = function(e) {
if (swipe.obj===null) return false;
if (swipe.obj===null) return false
swipe.offset = -1 * e.x;
swipe.offset = -1 * e.x
swipe.obj.css({
WebkitTransform: 'translateX(' + swipe.offset + 'px)',
MozTransform: 'translateX(' + swipe.offset + 'px)',
transform: 'translateX(' + swipe.offset + 'px)'
});
WebkitTransform : 'translateX(' + swipe.offset + 'px)',
MozTransform : 'translateX(' + swipe.offset + 'px)',
transform : 'translateX(' + swipe.offset + 'px)'
})
}
swipe.stop = function(e, left, right) {
if (e.x<=-swipe.tolerance) left(true);
else if (e.x>=swipe.tolerance) right(true);
if (e.x<=-swipe.tolerance) left(true)
else if (e.x>=swipe.tolerance) right(true)
else if (swipe.obj!==null) {
swipe.obj.css({
WebkitTransform: 'translateX(0px)',
MozTransform: 'translateX(0px)',
transform: 'translateX(0px)'
});
WebkitTransform : 'translateX(0px)',
MozTransform : 'translateX(0px)',
transform : 'translateX(0px)'
})
}
swipe.obj = null;
swipe.offset = 0;
swipe.obj = null
swipe.offset = 0
}

@ -1,6 +1,6 @@
/**
* @description Takes care of every action an album can handle and execute.
* @copyright 2015 by Tobias Reich
* @description Takes care of every action an album can handle and execute.
* @copyright 2015 by Tobias Reich
*/
upload = {}
@ -17,23 +17,21 @@ upload.show = function(title, files, callback) {
}
},
callback
});
})
}
upload.notify = function(title, text) {
var popup;
if (text==null||text==='') text = 'You can now manage your new photo(s).'
if (!text||text==='') text = 'You can now manage your new photo(s).';
if (!window.webkitNotifications) return false
if (!window.webkitNotifications) return false;
if (window.webkitNotifications.checkPermission()!==0) window.webkitNotifications.requestPermission()
if (window.webkitNotifications.checkPermission()!==0) window.webkitNotifications.requestPermission();
if (window.webkitNotifications.checkPermission()===0&&title) {
popup = window.webkitNotifications.createNotification('', title, text);
popup.show();
if (window.webkitNotifications.checkPermission()===0 && title) {
let popup = window.webkitNotifications.createNotification('', title, text)
popup.show()
}
}
@ -42,287 +40,305 @@ upload.start = {
local: function(files) {
var albumID = album.getID(),
error = false,
warning = false,
process = function(files, file) {
let albumID = album.getID(),
error = false,
warning = false
var formData = new FormData(),
xhr = new XMLHttpRequest(),
pre_progress = 0,
progress = 0,
finish = function() {
const process = function(files, file) {
window.onbeforeunload = null;
let formData = new FormData(),
xhr = new XMLHttpRequest(),
pre_progress = 0,
progress = 0
$('#upload_files').val('');
const finish = function() {
if (error===false&&warning===false) {
window.onbeforeunload = null
// Success
basicModal.close();
upload.notify('Upload complete');
$('#upload_files').val('')
} else if (error===false&&warning===true) {
if (error===false && warning===false) {
// Warning
$('.basicModal #basicModal__action.hidden').show();
upload.notify('Upload complete');
// Success
basicModal.close()
upload.notify('Upload complete')
} else {
} else if (error===false && warning===true) {
// Error
$('.basicModal #basicModal__action.hidden').show();
upload.notify('Upload complete', 'Failed to upload one or more photos.');
// Warning
$('.basicModal #basicModal__action.hidden').show()
upload.notify('Upload complete')
}
} else {
albums.refresh();
// Error
$('.basicModal #basicModal__action.hidden').show()
upload.notify('Upload complete', 'Failed to upload one or more photos.')
}
if (album.getID()===false) lychee.goto('0');
else album.load(albumID);
albums.refresh()
};
if (album.getID()===false) lychee.goto('0')
else album.load(albumID)
// Check if file is supported
if (file.supported===false) {
}
// Skip file
if (file.next!==null) process(files, file.next);
else {
// Check if file is supported
if (file.supported===false) {
// Look for supported files
// If zero files are supported, hide the upload after a delay
// Skip file
if (file.next!=null) process(files, file.next)
else {
var hasSupportedFiles = false;
// Look for supported files
// If zero files are supported, hide the upload after a delay
for (var i = 0; i < files.length; i++) {
let hasSupportedFiles = false
if (files[i].supported===true) {
hasSupportedFiles = true;
break;
}
for (let i = 0; i < files.length; i++) {
if (files[i].supported===true) {
hasSupportedFiles = true
break
}
if (hasSupportedFiles===false) setTimeout(finish, 2000);
}
return false;
if (hasSupportedFiles===false) setTimeout(finish, 2000)
}
formData.append('function', 'Photo::add');
formData.append('albumID', albumID);
formData.append('tags', '');
formData.append(0, file);
return false
xhr.open('POST', api.path);
xhr.onload = function() {
}
var wait = false,
errorText = '';
formData.append('function', 'Photo::add')
formData.append('albumID', albumID)
formData.append('tags', '')
formData.append(0, file)
file.ready = true;
xhr.open('POST', api.path)
// Set status
if (xhr.status===200&&xhr.responseText==='1') {
xhr.onload = function() {
// Success
$('.basicModal .rows .row:nth-child(' + (file.num+1) + ') .status')
.html('Finished')
.addClass('success');
let wait = false,
errorText = ''
} else {
file.ready = true
if (xhr.responseText.substr(0, 6)==='Error:') {
// Set status
if (xhr.status===200 && xhr.responseText==='1') {
errorText = xhr.responseText.substr(6) + ' Please take a look at the console of your browser for further details.';
error = true;
// Success
$('.basicModal .rows .row:nth-child(' + (file.num+1) + ') .status')
.html('Finished')
.addClass('success')
// Error Status
$('.basicModal .rows .row:nth-child(' + (file.num+1) + ') .status')
.html('Failed')
.addClass('error');
} else {
} else if (xhr.responseText.substr(0, 8)==='Warning:') {
if (xhr.responseText.substr(0, 6)==='Error:') {
errorText = xhr.responseText.substr(8);
warning = true;
errorText = xhr.responseText.substr(6) + ' Please take a look at the console of your browser for further details.'
error = true
// Warning Status
$('.basicModal .rows .row:nth-child(' + (file.num+1) + ') .status')
.html('Skipped')
.addClass('warning');
// Error Status
$('.basicModal .rows .row:nth-child(' + (file.num+1) + ') .status')
.html('Failed')
.addClass('error')
} else {
} else if (xhr.responseText.substr(0, 8)==='Warning:') {
errorText = 'Server returned an unknown response. Please take a look at the console of your browser for further details.';
error = true;
errorText = xhr.responseText.substr(8)
warning = true
// Error Status
$('.basicModal .rows .row:nth-child(' + (file.num+1) + ') .status')
.html('Failed')
.addClass('error');
// Warning Status
$('.basicModal .rows .row:nth-child(' + (file.num+1) + ') .status')
.html('Skipped')
.addClass('warning')
}
} else {
$('.basicModal .rows .row:nth-child(' + (file.num+1) + ') p.notice')
.html(errorText)
.show();
errorText = 'Server returned an unknown response. Please take a look at the console of your browser for further details.'
error = true
// Throw error
if (error===true) lychee.error('Upload failed. Server returned the status code ' + xhr.status + '!', xhr, xhr.responseText);
// Error Status
$('.basicModal .rows .row:nth-child(' + (file.num+1) + ') .status')
.html('Failed')
.addClass('error')
}
// Check if there are file which are not finished
for (var i = 0; i < files.length; i++) {
$('.basicModal .rows .row:nth-child(' + (file.num+1) + ') p.notice')
.html(errorText)
.show()
if (files[i].ready===false) {
wait = true;
break;
}
// Throw error
if (error===true) lychee.error('Upload failed. Server returned the status code ' + xhr.status + '!', xhr, xhr.responseText)
}
// Check if there are file which are not finished
for (let i = 0; i < files.length; i++) {
if (files[i].ready===false) {
wait = true
break
}
// Finish upload when all files are finished
if (wait===false) finish();
}
};
// Finish upload when all files are finished
if (wait===false) finish()
xhr.upload.onprogress = function(e) {
}
if (e.lengthComputable) {
xhr.upload.onprogress = function(e) {
// Calculate progress
progress = (e.loaded / e.total * 100 | 0);
if (e.lengthComputable!==true) return false
// Set progress when progress has changed
if (progress>pre_progress) {
$('.basicModal .rows .row:nth-child(' + (file.num+1) + ') .status').html(progress + '%');
pre_progress = progress;
}
// Calculate progress
progress = (e.loaded / e.total * 100 | 0)
if (progress>=100) {
// Set progress when progress has changed
if (progress>pre_progress) {
$('.basicModal .rows .row:nth-child(' + (file.num+1) + ') .status').html(progress + '%')
pre_progress = progress
}
// Scroll to the uploading file
var scrollPos = 0;
if ((file.num+1)>4) scrollPos = (file.num + 1 - 4) * 40
$('.basicModal .rows').scrollTop(scrollPos);
if (progress>=100) {
// Set status to processing
$('.basicModal .rows .row:nth-child(' + (file.num+1) + ') .status').html('Processing');
// Scroll to the uploading file
let scrollPos = 0
if ((file.num+1)>4) scrollPos = (file.num + 1 - 4) * 40
$('.basicModal .rows').scrollTop(scrollPos)
// Upload next file
if (file.next!==null) process(files, file.next);
// Set status to processing
$('.basicModal .rows .row:nth-child(' + (file.num+1) + ') .status').html('Processing')
}
// Upload next file
if (file.next!=null) process(files, file.next)
}
}
};
}
xhr.send(formData);
xhr.send(formData)
};
}
if (files.length<=0) return false;
if (albumID===false||visible.albums()===true) albumID = 0;
if (files.length<=0) return false
if (albumID===false || visible.albums()===true) albumID = 0
for (var i = 0; i < files.length; i++) {
for (let i = 0; i < files.length; i++) {
files[i].num = i;
files[i].ready = false;
files[i].supported = true;
files[i].num = i
files[i].ready = false
files[i].supported = true
if (i < files.length-1) files[i].next = files[i+1];
else files[i].next = null;
if (i < files.length-1) files[i].next = files[i+1]
else files[i].next = null
// Check if file is supported
if (files[i].type!=='image/jpeg'&&files[i].type!=='image/jpg'&&files[i].type!=='image/png'&&files[i].type!=='image/gif') {
if (files[i].type!=='image/jpeg' && files[i].type!=='image/jpg' && files[i].type!=='image/png' && files[i].type!=='image/gif') {
files[i].ready = true;
files[i].supported = false;
files[i].ready = true
files[i].supported = false
}
}
window.onbeforeunload = function() { return 'Lychee is currently uploading!'; };
window.onbeforeunload = function() { return 'Lychee is currently uploading!' }
upload.show('Uploading', files, function() {
// Upload first file
process(files, files[0]);
process(files, files[0])
});
})
},
url: function(url = '') {
var albumID = album.getID(),
url = (typeof url === 'string' ? url : ''),
action;
let albumID = album.getID()
url = (typeof url === 'string' ? url : '')
if (albumID===false) albumID = 0;
if (albumID===false) albumID = 0
action = function(data) {
const action = function(data) {
var extension,
files = [];
let files = []
if (data.link&&data.link.length>3) {
if (data.link && data.link.length>3) {
basicModal.close();
basicModal.close()
extension = data.link.split('.').pop();
if (extension!=='jpeg'&&extension!=='jpg'&&extension!=='png'&&extension!=='gif'&&extension!=='webp') {
loadingBar.show('error', 'The file format of this link is not supported.');
return false;
let extension = data.link.split('.').pop()
if (extension!=='jpeg' && extension!=='jpg' && extension!=='png' && extension!=='gif' && extension!=='webp') {
loadingBar.show('error', 'File format of link not supported.')
return false
}
files[0] = {
name: data.link,
supported: true
name : data.link,
supported : true
}
upload.show('Importing URL', files, function() {
var params;
$('.basicModal .rows .row .status').html('Importing')
$('.basicModal .rows .row .status').html('Importing');
params = {
let params = {
url: data.link,
albumID
}
api.post('Import::url', params, function(data) {
basicModal.close();
upload.notify('Import complete');
// Same code as in import.dropbox()
if (data!==true) {
$('.basicModal .rows .row p.notice')
.html('The import has been finished, but returned warnings or errors. Please take a look at the log (Settings -> Show Log) for further details.')
.show()
$('.basicModal .rows .row .status')
.html('Finished')
.addClass('warning')
// Show close button
$('.basicModal #basicModal__action.hidden').show()
// Log error
lychee.error(null, params, data)
} else {
basicModal.close()
albums.refresh();
}
if (album.getID()===false) lychee.goto('0');
else album.load(albumID);
upload.notify('Import complete')
if (data!==true) lychee.error(null, params, data);
albums.refresh()
});
if (album.getID()===false) lychee.goto('0')
else album.load(albumID)
});
})
} else basicModal.error('link');
})
} else basicModal.error('link')
}
basicModal.show({
body: "<p>Please enter the direct link to a photo to import it: <input class='text' data-name='link' type='text' placeholder='http://' value='" + url + "'></p>",
body: `<p>Please enter the direct link to a photo to import it: <input class='text' name='link' type='text' placeholder='http://' value='${ url }'></p>`,
buttons: {
action: {
title: 'Import',
@ -333,63 +349,102 @@ upload.start = {
fn: basicModal.close
}
}
});
})
},
server: function() {
var albumID = album.getID(),
action;
if (albumID===false) albumID = 0;
let albumID = album.getID()
if (albumID===false) albumID = 0
action = function(data) {
const action = function(data) {
var files = [];
let files = []
files[0] = {
name: data.path,
supported: true
};
name : data.path,
supported : true
}
upload.show('Importing from server', files, function() {
var params;
$('.basicModal .rows .row .status').html('Importing');
$('.basicModal .rows .row .status').html('Importing')
params = {
let params = {
albumID,
path: data.path
}
api.post('Import::server', params, function(data) {
basicModal.close();
upload.notify('Import complete');
albums.refresh()
upload.notify('Import complete')
if (data==='Notice: Import only contained albums!') {
// No error, but the folder only contained albums
// Go back to the album overview to show the imported albums
if (visible.albums()) lychee.load()
else lychee.goto('')
basicModal.close()
return true
} else if (data==='Warning: Folder empty or no readable files to process!') {
// Error because the import could not start
$('.basicModal .rows .row p.notice')
.html('Folder empty or no readable files to process. Please take a look at the log (Settings -> Show Log) for further details.')
.show()
$('.basicModal .rows .row .status')
.html('Failed')
.addClass('error')
// Log error
lychee.error('Could not start import because the folder was empty!', params, data)
} else if (data!==true) {
// Maybe an error, maybe just some skipped photos
$('.basicModal .rows .row p.notice')
.html('The import has been finished, but returned warnings or errors. Please take a look at the log (Settings -> Show Log) for further details.')
.show()
albums.refresh();
$('.basicModal .rows .row .status')
.html('Finished')
.addClass('warning')
// Log error
lychee.error(null, params, data)
} else {
// No error, everything worked fine
basicModal.close()
if (data==='Notice: Import only contains albums!') {
if (visible.albums()) lychee.load();
else lychee.goto('');
}
else if (album.getID()===false) lychee.goto('0');
else album.load(albumID);
if (data==='Notice: Import only contains albums!') return true;
else if (data==='Warning: Folder empty!') lychee.error('Folder empty. No photos imported!', params, data);
else if (data!==true) lychee.error(null, params, data);
if (album.getID()===false) lychee.goto('0')
else album.load(albumID)
// Show close button
$('.basicModal #basicModal__action.hidden').show()
});
})
});
})
}
basicModal.show({
body: "<p>This action will import all photos, folders and sub-folders which are located in the following directory. The <b>original files will be deleted</b> after the import when possible. <input class='text' data-name='path' type='text' maxlength='100' placeholder='Absolute path to directory' value='" + lychee.location + "uploads/import/'></p>",
body: `<p>This action will import all photos, folders and sub-folders which are located in the following directory. The <b>original files will be deleted</b> after the import when possible. <input class='text' name='path' type='text' maxlength='100' placeholder='Absolute path to directory' value='${ lychee.location }uploads/import/'></p>`,
buttons: {
action: {
title: 'Import',
@ -400,60 +455,78 @@ upload.start = {
fn: basicModal.close
}
}
});
})
},
dropbox: function() {
var albumID = album.getID(),
links = '',
success;
let albumID = album.getID()
if (albumID===false) albumID = 0
if (albumID===false) albumID = 0;
const success = function(files) {
success = function(files) {
let links = ''
for (var i = 0; i < files.length; i++) {
for (let i = 0; i < files.length; i++) {
links += files[i].link + ',';
links += files[i].link + ','
files[i] = {
name: files[i].link,
supported: true
};
name : files[i].link,
supported : true
}
}
// Remove last comma
links = links.substr(0, links.length-1);
links = links.substr(0, links.length-1)
upload.show('Importing from Dropbox', files, function() {
var params;
$('.basicModal .rows .row .status').html('Importing')
$('.basicModal .rows .row .status').html('Importing');
params = {
let params = {
url: links,
albumID
}
api.post('Import::url', params, function(data) {
basicModal.close();
upload.notify('Import complete');
// Same code as in import.url()
if (data!==true) {
$('.basicModal .rows .row p.notice')
.html('The import has been finished, but returned warnings or errors. Please take a look at the log (Settings -> Show Log) for further details.')
.show()
$('.basicModal .rows .row .status')
.html('Finished')
.addClass('warning')
// Show close button
$('.basicModal #basicModal__action.hidden').show()
// Log error
lychee.error(null, params, data)
} else {
basicModal.close()
}
albums.refresh();
upload.notify('Import complete')
if (album.getID()===false) lychee.goto('0');
else album.load(albumID);
albums.refresh()
if (data!==true) lychee.error(null, params, data);
if (album.getID()===false) lychee.goto('0')
else album.load(albumID)
});
})
});
})
}
@ -462,8 +535,8 @@ upload.start = {
linkType: 'direct',
multiselect: true,
success
});
});
})
})
}

@ -1,6 +1,6 @@
/**
* @description Responsible to reflect data changes to the UI.
* @copyright 2015 by Tobias Reich
* @description Responsible to reflect data changes to the UI.
* @copyright 2015 by Tobias Reich
*/
view = {}
@ -9,14 +9,14 @@ view.albums = {
init: function() {
view.albums.title();
view.albums.content.init();
view.albums.title()
view.albums.content.init()
},
title: function() {
lychee.setTitle('Albums', false);
lychee.setTitle('Albums', false)
},
@ -26,68 +26,67 @@ view.albums = {
init: function() {
var smartData = '',
albumsData = '';
let smartData = '',
albumsData = ''
/* Smart Albums */
// Smart Albums
if (lychee.publicMode===false) {
albums.parse(albums.json.smartalbums.unsorted);
albums.parse(albums.json.smartalbums.public);
albums.parse(albums.json.smartalbums.starred);
albums.parse(albums.json.smartalbums.recent);
albums.parse(albums.json.smartalbums.unsorted)
albums.parse(albums.json.smartalbums.public)
albums.parse(albums.json.smartalbums.starred)
albums.parse(albums.json.smartalbums.recent)
smartData = build.divider('Smart Albums') + build.album(albums.json.smartalbums.unsorted) + build.album(albums.json.smartalbums.public) + build.album(albums.json.smartalbums.starred) + build.album(albums.json.smartalbums.recent);
smartData = build.divider('Smart Albums') + build.album(albums.json.smartalbums.unsorted) + build.album(albums.json.smartalbums.public) + build.album(albums.json.smartalbums.starred) + build.album(albums.json.smartalbums.recent)
}
/* Albums */
if (albums.json.albums&&albums.json.num!==0) {
// Albums
if (albums.json.albums && albums.json.num!==0) {
$.each(albums.json.albums, function() {
albums.parse(this);
albumsData += build.album(this);
});
albums.parse(this)
albumsData += build.album(this)
})
// Add divider
if (lychee.publicMode===false) albumsData = build.divider('Albums') + albumsData;
if (lychee.publicMode===false) albumsData = build.divider('Albums') + albumsData
}
if (smartData===''&&albumsData==='') {
lychee.content.html('');
$('body').append(build.no_content('eye'));
if (smartData==='' && albumsData==='') {
lychee.content.html('')
$('body').append(build.no_content('eye'))
} else {
lychee.content.html(smartData + albumsData);
lychee.content.html(smartData + albumsData)
}
// Restore scroll position
if (view.albums.content.scrollPosition!==null&&
view.albums.content.scrollPosition!==0) {
$(document).scrollTop(view.albums.content.scrollPosition);
if (view.albums.content.scrollPosition!=null && view.albums.content.scrollPosition!==0) {
$(document).scrollTop(view.albums.content.scrollPosition)
}
},
title: function(albumID) {
var title = albums.getByID(albumID).title;
let title = albums.getByID(albumID).title
$('.album[data-id="' + albumID + '"] .overlay h1')
.html(title)
.attr('title', title);
.attr('title', title)
},
delete: function(albumID) {
$('.album[data-id="' + albumID + '"]').css('opacity', 0).animate({
width: 0,
marginLeft: 0
width : 0,
marginLeft : 0
}, 300, function() {
$(this).remove();
if (albums.json.num<=0) lychee.content.find('.divider:last-child').remove();
});
$(this).remove()
if (albums.json.num<=0) lychee.content.find('.divider:last-child').remove()
})
}
@ -99,38 +98,38 @@ view.album = {
init: function() {
album.parse();
album.parse()
view.album.sidebar();
view.album.title();
view.album.public();
view.album.content.init();
view.album.sidebar()
view.album.title()
view.album.public()
view.album.content.init()
album.json.init = 1;
album.json.init = 1
},
title: function() {
if ((visible.album()||!album.json.init)&&!visible.photo()) {
if ((visible.album() || !album.json.init) && !visible.photo()) {
switch (album.getID()) {
case 'f':
lychee.setTitle('Starred', false);
break;
lychee.setTitle('Starred', false)
break
case 's':
lychee.setTitle('Public', false);
break;
lychee.setTitle('Public', false)
break
case 'r':
lychee.setTitle('Recent', false);
break;
lychee.setTitle('Recent', false)
break
case '0':
lychee.setTitle('Unsorted', false);
break;
lychee.setTitle('Unsorted', false)
break
default:
if (album.json.init) sidebar.changeAttr('title', album.json.title);
lychee.setTitle(album.json.title, true);
break;
if (album.json.init) sidebar.changeAttr('title', album.json.title)
lychee.setTitle(album.json.title, true)
break
}
}
@ -141,64 +140,68 @@ view.album = {
init: function() {
var photosData = '';
let photosData = ''
// Save and reset scroll position
view.albums.content.scrollPosition = $(document).scrollTop();
$('html, body').scrollTop(0);
if (album.json.content&&album.json.content!==false) {
if (album.json.content && album.json.content!==false) {
// Build photos
$.each(album.json.content, function() {
photosData += build.photo(this);
});
photosData += build.photo(this)
})
}
// Save and reset scroll position
view.albums.content.scrollPosition = $(document).scrollTop()
requestAnimationFrame(() => $(document).scrollTop(0))
// Add photos to view
lychee.content.html(photosData);
lychee.content.html(photosData)
},
title: function(photoID) {
var title = album.json.content[photoID].title;
let title = album.json.content[photoID].title
$('.photo[data-id="' + photoID + '"] .overlay h1')
.html(title)
.attr('title', title);
.attr('title', title)
},
star: function(photoID) {
$('.photo[data-id="' + photoID + '"] .iconic-star').remove();
if (album.json.content[photoID].star==='1') $('.photo[data-id="' + photoID + '"]').append("<a class='badge iconic-star'>" + build.iconic('star') + "</a>");
let $badge = $('.photo[data-id="' + photoID + '"] .icn-star')
if (album.json.content[photoID].star==='1') $badge.addClass('badge--visible')
else $badge.removeClass('badge--visible')
},
public: function(photoID) {
$('.photo[data-id="' + photoID + '"] .iconic-share').remove();
if (album.json.content[photoID].public==='1') $('.photo[data-id="' + photoID + '"]').append("<a class='badge iconic-share'>" + build.iconic('eye') + "</a>");
let $badge = $('.photo[data-id="' + photoID + '"] .icn-share')
if (album.json.content[photoID].public==='1') $badge.addClass('badge--visible')
else $badge.removeClass('badge--visible')
},
delete: function(photoID) {
$('.photo[data-id="' + photoID + '"]').css('opacity', 0).animate({
width: 0,
marginLeft: 0
width : 0,
marginLeft : 0
}, 300, function() {
$(this).remove();
$(this).remove()
// Only when search is not active
if (!visible.albums()) {
album.json.num--;
view.album.num();
view.album.title();
album.json.num--
view.album.num()
view.album.title()
}
});
})
}
@ -206,13 +209,13 @@ view.album = {
description: function() {
sidebar.changeAttr('description', album.json.description);
sidebar.changeAttr('description', album.json.description)
},
num: function() {
sidebar.changeAttr('images', album.json.num);
sidebar.changeAttr('images', album.json.num)
},
@ -222,53 +225,54 @@ view.album = {
$('#button_share_album')
.addClass('active')
.attr('title', 'Share Album');
.attr('title', 'Share Album')
$('.photo .iconic-share').remove();
$('.photo .iconic-share').remove()
if (album.json.init) sidebar.changeAttr('public', 'Yes');
if (album.json.init) sidebar.changeAttr('public', 'Yes')
} else {
$('#button_share_album')
.removeClass('active')
.attr('title', 'Make Public');
.attr('title', 'Make Public')
if (album.json.init) sidebar.changeAttr('public', 'No')
if (album.json.init) sidebar.changeAttr('public', 'No');
}
},
visible: function() {
if (album.json.visible==='1') sidebar.changeAttr('visible', 'Yes');
else sidebar.changeAttr('visible', 'No');
if (album.json.visible==='1') sidebar.changeAttr('visible', 'Yes')
else sidebar.changeAttr('visible', 'No')
},
downloadable: function() {
if (album.json.downloadable==='1') sidebar.changeAttr('downloadable', 'Yes');
else sidebar.changeAttr('downloadable', 'No');
if (album.json.downloadable==='1') sidebar.changeAttr('downloadable', 'Yes')
else sidebar.changeAttr('downloadable', 'No')
},
password: function() {
if (album.json.password==='1') sidebar.changeAttr('password', 'Yes');
else sidebar.changeAttr('password', 'No');
if (album.json.password==='1') sidebar.changeAttr('password', 'Yes')
else sidebar.changeAttr('password', 'No')
},
sidebar: function() {
if ((visible.album()||!album.json.init)&&!visible.photo()) {
if ((visible.album() || !album.json.init) && !visible.photo()) {
let structure = sidebar.createStructure.album(album.json),
html = sidebar.render(structure);
let structure = sidebar.createStructure.album(album.json),
html = sidebar.render(structure)
sidebar.dom('.wrapper').html(html);
sidebar.bind();
sidebar.dom('.wrapper').html(html)
sidebar.bind()
}
@ -280,151 +284,161 @@ view.photo = {
init: function() {
photo.parse();
photo.parse()
view.photo.sidebar();
view.photo.title();
view.photo.star();
view.photo.public();
view.photo.photo();
view.photo.sidebar()
view.photo.title()
view.photo.star()
view.photo.public()
view.photo.photo()
photo.json.init = 1;
photo.json.init = 1
},
show: function() {
// Change header
lychee.content.addClass('view');
header.setMode('photo');
lychee.content.addClass('view')
header.setMode('photo')
// Make body not scrollable
$('body').css('overflow', 'hidden');
$('body').css('overflow', 'hidden')
// Fullscreen
$(document)
.bind('mouseenter', header.show)
.bind('mouseleave', header.hide);
.bind('mouseleave', header.hide)
lychee.animate(lychee.imageview, 'fadeIn');
lychee.animate(lychee.imageview, 'fadeIn')
},
hide: function() {
header.show();
header.show()
lychee.content.removeClass('view');
header.setMode('album');
lychee.content.removeClass('view')
header.setMode('album')
// Make body scrollable
$('body').css('overflow', 'auto');
$('body').css('overflow', 'auto')
// Disable Fullscreen
$(document)
.unbind('mouseenter')
.unbind('mouseleave');
.unbind('mouseleave')
// Hide Photo
lychee.animate(lychee.imageview, 'fadeOut');
setTimeout(function() {
lychee.imageview.hide();
view.album.sidebar();
}, 300);
lychee.animate(lychee.imageview, 'fadeOut')
setTimeout(() => {
lychee.imageview.hide()
view.album.sidebar()
}, 300)
},
title: function() {
if (photo.json.init) sidebar.changeAttr('title', photo.json.title);
lychee.setTitle(photo.json.title, true);
if (photo.json.init) sidebar.changeAttr('title', photo.json.title)
lychee.setTitle(photo.json.title, true)
},
description: function() {
if (photo.json.init) sidebar.changeAttr('description', photo.json.description);
if (photo.json.init) sidebar.changeAttr('description', photo.json.description)
},
star: function() {
if (photo.json.star==1) {
if (photo.json.star==='1') {
// Starred
$('#button_star')
.addClass('active')
.attr('title', 'Unstar Photo');
.attr('title', 'Unstar Photo')
} else {
// Unstarred
$('#button_star').removeClass('active');
$('#button_star').attr('title', 'Star Photo');
$('#button_star').removeClass('active')
$('#button_star').attr('title', 'Star Photo')
}
},
public: function() {
if (photo.json.public==='1'||photo.json.public==='2') {
if (photo.json.public==='1' || photo.json.public==='2') {
// Photo public
$('#button_share')
.addClass('active')
.attr('title', 'Share Photo');
if (photo.json.init) sidebar.changeAttr('public', 'Yes');
.attr('title', 'Share Photo')
if (photo.json.init) sidebar.changeAttr('public', 'Yes')
} else {
// Photo private
$('#button_share')
.removeClass('active')
.attr('title', 'Make Public');
if (photo.json.init) sidebar.changeAttr('public', 'No');
.attr('title', 'Make Public')
if (photo.json.init) sidebar.changeAttr('public', 'No')
}
},
tags: function() {
sidebar.changeAttr('tags', build.tags(photo.json.tags));
sidebar.bind();
sidebar.changeAttr('tags', build.tags(photo.json.tags))
sidebar.bind()
},
photo: function() {
lychee.imageview.html(build.imageview(photo.json, photo.getSize(), visible.header()));
lychee.imageview.html(build.imageview(photo.json, photo.getSize(), visible.header()))
var $nextArrow = lychee.imageview.find('a#next'),
$previousArrow = lychee.imageview.find('a#previous'),
hasNext = album.json&&album.json.content&&album.json.content[photo.getID()]&&album.json.content[photo.getID()].nextPhoto==='',
hasPrevious = album.json&&album.json.content&&album.json.content[photo.getID()]&&album.json.content[photo.getID()].previousPhoto==='';
let $nextArrow = lychee.imageview.find('a#next'),
$previousArrow = lychee.imageview.find('a#previous'),
hasNext = album.json && album.json.content && album.json.content[photo.getID()] && album.json.content[photo.getID()].nextPhoto==='',
hasPrevious = album.json && album.json.content && album.json.content[photo.getID()] && album.json.content[photo.getID()].previousPhoto===''
if (hasNext||lychee.viewMode) { $nextArrow.hide(); }
if (hasNext || lychee.viewMode) { $nextArrow.hide() }
else {
var nextPhotoID = album.json.content[photo.getID()].nextPhoto,
nextPhoto = album.json.content[nextPhotoID];
let nextPhotoID = album.json.content[photo.getID()].nextPhoto,
nextPhoto = album.json.content[nextPhotoID]
$nextArrow.css('background-image', 'linear-gradient(to bottom, rgba(0, 0, 0, .4), rgba(0, 0, 0, .4)), url("' + nextPhoto.thumbUrl + '")');
$nextArrow.css('background-image', `linear-gradient(to bottom, rgba(0, 0, 0, .4), rgba(0, 0, 0, .4)), url("${ nextPhoto.thumbUrl }")`)
}
if (hasPrevious||lychee.viewMode) { $previousArrow.hide(); }
if (hasPrevious || lychee.viewMode) { $previousArrow.hide() }
else {
var previousPhotoID = album.json.content[photo.getID()].previousPhoto,
previousPhoto = album.json.content[previousPhotoID];
let previousPhotoID = album.json.content[photo.getID()].previousPhoto,
previousPhoto = album.json.content[previousPhotoID]
$previousArrow.css('background-image', 'linear-gradient(to bottom, rgba(0, 0, 0, .4), rgba(0, 0, 0, .4)), url("' + previousPhoto.thumbUrl + '")');
$previousArrow.css('background-image', `linear-gradient(to bottom, rgba(0, 0, 0, .4), rgba(0, 0, 0, .4)), url("${ previousPhoto.thumbUrl }")`)
};
}
},
sidebar: function() {
var structure = sidebar.createStructure.photo(photo.json),
html = sidebar.render(structure);
let structure = sidebar.createStructure.photo(photo.json),
html = sidebar.render(structure)
sidebar.dom('.wrapper').html(html);
sidebar.bind();
sidebar.dom('.wrapper').html(html)
sidebar.bind()
}

@ -1,79 +1,75 @@
/**
* @description Used to view single photos with view.php
* @copyright 2015 by Tobias Reich
* @description Used to view single photos with view.php
* @copyright 2015 by Tobias Reich
*/
var lychee = { content: $('#content') },
loadingBar = { show() {}, hide() {} }
imageview = $('#imageview');
let lychee = { content: $('#content') },
loadingBar = { show() {}, hide() {} }
imageview = $('#imageview')
$(document).ready(function() {
/* Event Name */
if ('ontouchend' in document.documentElement) eventName = 'touchend';
else eventName = 'click';
// Event Name
let touchendSupport = (/Android|iPhone|iPad|iPod/i).test(navigator.userAgent || navigator.vendor || window.opera) && ('ontouchend' in document.documentElement),
eventName = (touchendSupport===true ? 'touchend' : 'click')
/* Set API error handler */
api.onError = error;
// Set API error handler
api.onError = error
/* Infobox */
header.dom('#button_info').on(eventName, sidebar.toggle);
// Infobox
header.dom('#button_info').on(eventName, sidebar.toggle)
/* Direct Link */
// Direct Link
header.dom('#button_direct').on(eventName, function() {
var link = $('#imageview #image').css('background-image').replace(/"/g,'').replace(/url\(|\)$/ig, '');
window.open(link, '_newtab');
let link = $('#imageview #image').css('background-image').replace(/"/g,'').replace(/url\(|\)$/ig, '')
window.open(link, '_newtab')
});
})
loadPhotoInfo(gup('p'));
loadPhotoInfo(gup('p'))
});
})
getPhotoSize = function(photo) {
const getPhotoSize = function(photo) {
// Size can be 'big', 'medium' or 'small'
// Default is big
// Small is centered in the middle of the screen
var size = 'big',
scaled = false,
hasMedium = photo.medium!=='',
pixelRatio = window.devicePixelRatio,
view = {
width: $(window).width()-60,
height: $(window).height()-100
};
let size = 'big',
scaled = false,
hasMedium = photo.medium!=='',
pixelRatio = window.devicePixelRatio,
view = {
width: $(window).width() - 60,
height: $(window).height() - 100
}
// Detect if the photo will be shown scaled,
// because the screen size is smaller than the photo
if (photo.width>view.width||
photo.width>view.height) scaled = true;
if (photo.json.width>view.width || photo.json.height>view.height) scaled = true
// Calculate pixel ratio of screen
if (pixelRatio!==undefined&&pixelRatio>1) {
view.width = view.width * pixelRatio;
view.height = view.height * pixelRatio;
if (pixelRatio!=null && pixelRatio>1) {
view.width = view.width * pixelRatio
view.height = view.height * pixelRatio
}
// Medium available and
// Medium still bigger than screen
if (hasMedium===true&&
(1920>view.width&&1080>view.height)) size = 'medium';
if (hasMedium===true && (1920>view.width && 1080>view.height)) size = 'medium'
// Photo not scaled
// Photo smaller then screen
if (scaled===false&&
(photo.width<view.width&&
photo.width<view.height)) size = 'small';
if (scaled===false && (photo.json.width<view.width&& photo.json.width<view.height)) size = 'small'
return size;
return size
}
loadPhotoInfo = function(photoID) {
const loadPhotoInfo = function(photoID) {
var params = {
let params = {
photoID,
albumID: 0,
password: ''
@ -81,50 +77,46 @@ loadPhotoInfo = function(photoID) {
api.post('Photo::get', params, function(data) {
if (data==='Warning: Photo private!'||
data==='Warning: Wrong password!') {
if (data==='Warning: Photo private!' || data==='Warning: Wrong password!') {
$('body').append(build.no_content('question-mark'));
$('body').removeClass('view');
header.dom().remove();
return false;
$('body').append(build.no_content('question-mark'))
$('body').removeClass('view')
header.dom().remove()
return false
}
/* Set title */
// Set title
if (!data.title) data.title = 'Untitled'
document.title = 'Lychee - ' + data.title
header.dom('#title').html(data.title)
if (!data.title) data.title = 'Untitled';
document.title = 'Lychee - ' + data.title;
header.dom('#title').html(data.title);
let size = getPhotoSize(data)
/* Render HTML */
// Render HTML
imageview.html(build.imageview(data, size, true))
imageview.find('.arrow_wrapper').remove()
imageview.addClass('fadeIn').show()
var size = getPhotoSize(data);
// Render Sidebar
let structure = sidebar.createStructure.photo(data),
html = sidebar.render(structure)
imageview.html(build.imageview(data, size, true));
imageview.find('.arrow_wrapper').remove();
imageview.addClass('fadeIn').show();
sidebar.dom('.wrapper').html(html)
sidebar.bind()
/* Render Sidebar */
var structure = sidebar.createStructure.photo(data),
html = sidebar.render(structure);
sidebar.dom('.wrapper').html(html);
sidebar.bind();
});
})
}
error = function(errorThrown, params, data) {
const error = function(errorThrown, params, data) {
console.error({
description: errorThrown,
params: params,
response: data
});
description : errorThrown,
params : params,
response : data
})
loadingBar.show('error', errorThrown);
loadingBar.show('error', errorThrown)
}

@ -1,62 +1,51 @@
/**
* @description This module is used to check if elements are visible or not.
* @copyright 2015 by Tobias Reich
* @description This module is used to check if elements are visible or not.
* @copyright 2015 by Tobias Reich
*/
visible = {}
visible.albums = function() {
if ($('#tools_albums').css('display')==='block') return true;
return false;
if (header.dom('#tools_albums').css('display')==='block') return true
return false
}
visible.album = function() {
if ($('#tools_album').css('display')==='block') return true;
return false;
if (header.dom('#tools_album').css('display')==='block') return true
return false
}
visible.photo = function() {
if ($('#imageview.fadeIn').length>0) return true;
return false;
if ($('#imageview.fadeIn').length>0) return true
return false
}
visible.search = function() {
if (search.hash!==null) return true;
return false;
if (search.hash!=null) return true
return false
}
visible.sidebar = function() {
if (sidebar.dom().hasClass('active')===true) return true;
return false;
if (sidebar.dom().hasClass('active')===true) return true
return false
}
visible.sidebarbutton = function() {
if (visible.albums()) return false;
if (visible.photo()) return true;
if (visible.album()&&$('#button_info_album:visible').length>0) return true;
return false;
if (visible.photo()) return true
if (visible.album() && $('#button_info_album:visible').length>0) return true
return false
}
visible.header = function() {
if (header.dom().hasClass('hidden')===true) return false;
return true;
}
visible.message = function() {
if ($('.message').length>0) return true;
return false;
}
visible.signin = function() {
if ($('.message .sign_in').length>0) return true;
return false;
if (header.dom().hasClass('hidden')===true) return false
return true
}
visible.contextMenu = function() {
return basicContext.visible();
return basicContext.visible()
}
visible.multiselect = function() {
if ($('#multiselect').length>0) return true;
return false;
if ($('#multiselect').length>0) return true
return false
}

@ -0,0 +1,54 @@
// Default Theme ----------------------------------------------------- //
.basicContext {
padding: 5px 0 6px;
background: linear-gradient(to bottom, #333, #252525);
box-shadow: 0 1px 4px black(.2), inset 0 1px 0 white(.05);
border-radius: 5px;
border: 1px solid black(.7);
border-bottom: 1px solid black(.8);
&__item {
margin-bottom: 2px;
font-size: 14px;
text-shadow: $shadowLight;
&--separator {
margin: 4px 0;
height: 2px;
background: black(.2);
border-bottom: 1px solid white(.06);
}
&--disabled {
opacity: .5;
}
&:last-child {
margin-bottom: 0;
}
}
&__data {
min-width: auto;
padding: 6px 25px 7px 12px;
color: white(1);
transition: none;
cursor: default;
}
&__item:not(.basicContext__item--disabled):hover &__data {
background: linear-gradient(to bottom, $colorBlue, darken($colorBlue, 5%));
}
&__item:not(.basicContext__item--disabled):active &__data {
background: linear-gradient(to bottom, darken($colorBlue, 10%), darken($colorBlue, 15%));
}
&__icon {
margin-right: 10px;
width: 12px;
text-align: center;
}
}

@ -0,0 +1,56 @@
/**
* @copyright 2015 by Tobias Reich
*/
/* Context ------------------------------------------------*/
.basicContext {
&__data .cover {
position: absolute;
background-color: #222;
border-radius: 2px;
box-shadow: 0 0 0 1px black(.5);
}
&__data .title {
display: inline-block;
margin: 0 0 3px 26px;
}
&__data .iconic {
display: inline-block;
margin: 0 10px 0 1px;
width: 11px;
height: 10px;
fill: white(1);
}
&__data .iconic.ionicons {
margin: 0 8px -2px 0;
width: 14px;
height: 14px;
}
/* Link ------------------------------------------------*/
&__data input#link {
margin: -2px 0;
padding: 5px 7px 6px;
width: 100%;
background: #333;
color: #fff;
box-shadow: 0px 1px 0px white(.05);
border: 1px solid black(.4);
border-radius: 3px;
outline: none;
}
/* No Hover ------------------------------------------------*/
&__item.noHover &__data {
padding-right: 12px;
}
&__item.noHover:hover &__data {
background: none;
}
}

@ -70,6 +70,7 @@
box-shadow: 0 2px 5px black(.5);
border: 1px solid white(.5);
transition: opacity .3s ease-out, transform .3s ease-out, border-color .3s ease-out;
will-change: transform;
}
&:hover img,
@ -157,21 +158,29 @@
.album img[data-retina='false'] + .overlay a { text-shadow: none; }
/* Badges ------------------------------------------------*/
.album .badges,
.photo .badges {
position: relative;
margin: -1px 0 0 6px;
}
.album .badge,
.photo .badge {
position: absolute;
margin: -1px 0 0 12px;
display: none;
margin: 0 0 0 6px;
padding: 12px 8px 6px;
box-shadow: 0 0 2px black(.6);
width: 18px;
background: $colorRed;
box-shadow: 0 0 2px black(.6);
border-radius: 0 0 5px 5px;
border: 1px solid #fff;
border-top: none;
color: #fff;
text-align: center;
text-shadow: 0 1px 0 black(.4);
opacity: .9;
&:nth-child(2n) { margin-left: 54px; }
&--visible { display: inline-block; }
.iconic {
fill: #fff;

@ -1,85 +0,0 @@
/**
* @copyright 2015 by Tobias Reich
*/
/* Context ------------------------------------------------*/
.basicContext {
padding: 5px 0 6px;
background: linear-gradient(to bottom, #333, #252525);
border: 1px solid black(.7);
border-bottom: 1px solid black(.8);
border-radius: 5px;
box-shadow: 0 1px 4px black(.2), inset 0 1px 0 white(.05);
/* Item ------------------------------------------------*/
tr {
margin-bottom: 2px;
color: white(.9);
font-size: 14px;
text-shadow: $shadowLight;
&.separator {
background: black(.2);
border-bottom: 1px solid white(.06);
}
}
tr td {
padding: 6px 25px 7px 12px;
min-width: auto;
color: white(1);
border-radius: 0;
transition: none;
cursor: default;
&:hover { background: linear-gradient(to bottom, $colorBlue, darken($colorBlue, 5%)); }
&:active { background: linear-gradient(to bottom, darken($colorBlue, 10%), darken($colorBlue, 15%)); }
}
tr td .cover {
background-color: #222;
border-radius: 2px;
box-shadow: 0 0 0 1px black(.5);
position: absolute;
}
tr td .title {
display: inline-block;
margin: 0 0 3px 26px;
}
tr td .iconic {
display: inline-block;
margin: 0 10px 0 1px;
width: 11px;
height: 10px;
fill: white(1);
}
tr td .iconic.ionicons {
margin: 0 8px -2px 0;
width: 14px;
height: 14px;
}
/* Link ------------------------------------------------*/
input#link {
width: 100%;
margin: -2px 0 -1px -2px;
padding: 5px 7px 6px 7px;
background: #333;
color: #fff;
border: 1px solid black(.4);
box-shadow: 0px 1px 0px white(.05);
outline: none;
border-radius: 3px;
}
/* No Hover ------------------------------------------------*/
tr.noHover td:hover {
background: none;
}
}

@ -32,6 +32,7 @@
animation-name: zoomIn;
animation-duration: .3s;
animation-timing-function: $timingBounce;
will-change: transform;
&.small {
top: 50%;
@ -73,6 +74,7 @@
opacity: .6;
z-index: 2;
transition: transform .2s ease-out, opacity .2s ease-out;
will-change: transform;
&#previous {
left: -1px;

@ -9,12 +9,15 @@
.basicModal {
background: linear-gradient(to bottom, #444, #333);
border: 1px solid black(.7);
border-bottom: 1px solid black(.8);
box-shadow: 0 1px 4px black(.2), inset 0 1px 0 white(.05);
/* Reset default styles ------------------------------------------------*/
.basicModal__content { padding: 0; }
.basicModal__content p { margin: 0; }
.basicModal__buttons { box-shadow: none; }
/* Text ------------------------------------------------*/
p {
display: block;
padding: 10px 30px;
color: white(.9);
font-size: 14px;
@ -70,13 +73,12 @@
&#basicModal__action.red { color: $colorRed; }
&.hidden { display: none; }
}
/* Inputs ------------------------------------------------*/
input.text {
width: calc(100% - 4px);
padding: 9px 2px;
width: 100%;
background-color: transparent;
color: #fff;
text-shadow: $shadow;
@ -100,7 +102,7 @@
/* Radio Buttons ------------------------------------------------*/
.choice {
padding: 0 30px 15px;
width: calc(100% - 60px);
width: 100%;
color: #fff;
&:last-child { padding-bottom: 40px; }
@ -159,7 +161,7 @@
clear: both;
padding: 2px 0 0 35px;
margin: 0;
width: calc(100% - 35px);
width: 100%;
color: white(.6);
font-size: 13px;
}
@ -168,7 +170,7 @@
display: none;
margin-top: 5px;
margin-left: 35px;
width: calc(100% - 39px);
width: calc(100% - 35px);
}
}
@ -280,7 +282,7 @@
a.name {
float: left;
padding: 5px 10px;
width: calc(70% - 20px);
width: 70%;
color: #fff;
font-size: 14px;
white-space: nowrap;
@ -290,7 +292,7 @@
a.status {
float: left;
padding: 5px 10px;
width: calc(30% - 20px);
width: 30%;
color: white(.5);
font-size: 14px;
text-align: right;
@ -315,7 +317,7 @@
display: none;
float: left;
padding: 2px 10px 5px;
width: calc(100% - 20px);
width: 100%;
color: white(.5);
font-size: 12px;
overflow: hidden;

@ -48,7 +48,8 @@ input {
@import 'animations';
@import 'content';
@import 'contextmenu';
@import 'basicContext.custom';
@import 'basicContext.extended';
@import 'header';
@import 'imageview';
@import 'sidebar';

Loading…
Cancel
Save