BIN
dist/main.css
vendored
BIN
dist/main.js
vendored
Executable file → Normal file
BIN
dist/view.js
vendored
@ -1,3 +1,17 @@
|
||||
## v3.1.1
|
||||
|
||||
Released April 30, 2016
|
||||
|
||||
- `New` share button when logged out (#473)
|
||||
- `New` Import of IPTC photo tags (Thanks @qligier, #514)
|
||||
- `New` Added reset username and password to FAQ (#500 #128)
|
||||
- `Improved` Removed will-change from the main image to improve the image rendering in Chrome (#501)
|
||||
- `Improved ` scroll and rendering performance by removing will-change
|
||||
- `Improved` Open Facebook and Twitter sharing sheet in new window
|
||||
- `Improved` EXIF and IPTC extraction (Thanks @qligier, #518)
|
||||
- `Fixed` broken URL in Update.md (#516)
|
||||
- `Fixed` error 500 on database connect error (Thanks @tribut, #530)
|
||||
|
||||
## v3.1.0
|
||||
|
||||
Released March 29, 2016
|
||||
|
@ -2,10 +2,10 @@
|
||||
If Lychee is not working properly, try to open `plugins/Diagnostics/index.php`. This script will display all errors it can find.
|
||||
|
||||
#### What do I need to run Lychee on my server?
|
||||
To run Lychee, everything you need is a web-server with PHP 5.3 or later and a MySQL-Database.
|
||||
To run Lychee, everything you need is a web-server with PHP 5.5 or later and a MySQL-Database.
|
||||
|
||||
#### I can't upload photos
|
||||
If you experience problems uploading large photos, you might want to change the PHP parameters in `.htaccess` (if you are using the PHP Apache module) or in `.user.ini` (if you are using PHP >= 5.3 with CGI or FastCGI).
|
||||
If you experience problems uploading large photos, you might want to change the PHP parameters in `.htaccess` (if you are using the PHP Apache module) or in `.user.ini` (if you are using PHP >= 5.5 with CGI or FastCGI).
|
||||
|
||||
If possible, change these settings directly in your `php.ini`. We recommend to increase the values of the following properties:
|
||||
|
||||
@ -55,4 +55,7 @@ Yes. Lychee uses ImageMagick when available.
|
||||
There's a problem with images compressed by ImageOptim. [Read more.](https://github.com/electerious/Lychee/issues/175#issuecomment-47403992)
|
||||
|
||||
#### How to change the title of the site?
|
||||
[#455](https://github.com/electerious/Lychee/issues/455)
|
||||
[#455](https://github.com/electerious/Lychee/issues/455)
|
||||
|
||||
#### How to reset username and password?
|
||||
Simply delete the whole `lychee_settings` table from the database. Lychee will regenerate it and ask you to enter a new username and password.
|
@ -11,7 +11,7 @@ Updating Lychee with `git` is the easiest way:
|
||||
|
||||
### Update manually
|
||||
|
||||
1. Download the [newest Version](https://github.com/electerious/Lychee/release)
|
||||
1. Download the [newest Version](https://github.com/electerious/Lychee/releases)
|
||||
2. Replace all existing files, excluding `uploads/` and `data/`
|
||||
3. Open Lychee (and enter your database details)
|
||||
|
||||
|
@ -56,7 +56,7 @@ final class Database {
|
||||
$connection = self::connect($host, $user, $password);
|
||||
|
||||
// Check if the connection was successful
|
||||
if ($connection===false) Response::error($connection->connect_error);
|
||||
if ($connection===false) Response::error(self::connect_error());
|
||||
|
||||
if (self::setCharset($connection)===false) Response::error('Could not set database charset!');
|
||||
|
||||
@ -90,6 +90,15 @@ final class Database {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Returns the string description of the last connect error
|
||||
*/
|
||||
private static function connect_error() {
|
||||
|
||||
return mysqli_connect_error();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean Returns true when successful.
|
||||
*/
|
||||
|
@ -239,7 +239,7 @@ final class Photo {
|
||||
}
|
||||
|
||||
// Save to DB
|
||||
$values = array(LYCHEE_TABLE_PHOTOS, $id, $info['title'], $photo_name, $info['description'], '', $info['type'], $info['width'], $info['height'], $info['size'], $info['iso'], $info['aperture'], $info['make'], $info['model'], $info['shutter'], $info['focal'], $info['takestamp'], $path_thumb, $albumID, $public, $star, $checksum, $medium);
|
||||
$values = array(LYCHEE_TABLE_PHOTOS, $id, $info['title'], $photo_name, $info['description'], $info['tags'], $info['type'], $info['width'], $info['height'], $info['size'], $info['iso'], $info['aperture'], $info['make'], $info['model'], $info['shutter'], $info['focal'], $info['takestamp'], $path_thumb, $albumID, $public, $star, $checksum, $medium);
|
||||
$query = Database::prepare(Database::get(), "INSERT INTO ? (id, title, url, description, tags, type, width, height, size, iso, aperture, make, model, shutter, focal, takestamp, thumbUrl, album, public, star, checksum, medium) VALUES ('?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?')", $values);
|
||||
$result = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
|
||||
|
||||
@ -307,7 +307,7 @@ final class Photo {
|
||||
$newUrl2x = LYCHEE_UPLOADS_THUMB . $photoName[0] . '@2x.jpeg';
|
||||
|
||||
// Create thumbnails with Imagick
|
||||
if(extension_loaded('imagick')&&Settings::get()['imagick']==='1') {
|
||||
if(Settings::hasImagick()) {
|
||||
|
||||
// Read image
|
||||
$thumb = new Imagick();
|
||||
@ -727,39 +727,11 @@ final class Photo {
|
||||
$info = getimagesize($url, $iptcArray);
|
||||
|
||||
// General information
|
||||
$return['type'] = $info['mime'];
|
||||
$return['width'] = $info[0];
|
||||
$return['height'] = $info[1];
|
||||
|
||||
// Size
|
||||
$size = filesize($url)/1024;
|
||||
if ($size>=1024) $return['size'] = round($size/1024, 1) . ' MB';
|
||||
else $return['size'] = round($size, 1) . ' KB';
|
||||
|
||||
// IPTC Metadata Fallback
|
||||
$return['type'] = $info['mime'];
|
||||
$return['width'] = $info[0];
|
||||
$return['height'] = $info[1];
|
||||
$return['title'] = '';
|
||||
$return['description'] = '';
|
||||
|
||||
// IPTC Metadata
|
||||
if(isset($iptcArray['APP13'])) {
|
||||
|
||||
$iptcInfo = iptcparse($iptcArray['APP13']);
|
||||
if (is_array($iptcInfo)) {
|
||||
|
||||
$temp = @$iptcInfo['2#105'][0];
|
||||
if (isset($temp)&&strlen($temp)>0) $return['title'] = $temp;
|
||||
|
||||
$temp = @$iptcInfo['2#120'][0];
|
||||
if (isset($temp)&&strlen($temp)>0) $return['description'] = $temp;
|
||||
|
||||
$temp = @$iptcInfo['2#005'][0];
|
||||
if (isset($temp)&&strlen($temp)>0&&$return['title']==='') $return['title'] = $temp;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// EXIF Metadata Fallback
|
||||
$return['orientation'] = '';
|
||||
$return['iso'] = '';
|
||||
$return['aperture'] = '';
|
||||
@ -768,45 +740,95 @@ final class Photo {
|
||||
$return['shutter'] = '';
|
||||
$return['focal'] = '';
|
||||
$return['takestamp'] = 0;
|
||||
$return['lens'] = '';
|
||||
$return['tags'] = '';
|
||||
$return['position'] = '';
|
||||
$return['latitude'] = '';
|
||||
$return['longitude'] = '';
|
||||
$return['altitude'] = '';
|
||||
|
||||
// Size
|
||||
$size = filesize($url)/1024;
|
||||
if ($size>=1024) $return['size'] = round($size/1024, 1) . ' MB';
|
||||
else $return['size'] = round($size, 1) . ' KB';
|
||||
|
||||
// IPTC Metadata
|
||||
// See https://www.iptc.org/std/IIM/4.2/specification/IIMV4.2.pdf for mapping
|
||||
if(isset($iptcArray['APP13'])) {
|
||||
|
||||
$iptcInfo = iptcparse($iptcArray['APP13']);
|
||||
if (is_array($iptcInfo)) {
|
||||
|
||||
// Title
|
||||
if (!empty($iptcInfo['2#105'][0])) $return['title'] = $iptcInfo['2#105'][0];
|
||||
else if (!empty($iptcInfo['2#005'][0])) $return['title'] = $iptcInfo['2#005'][0];
|
||||
|
||||
// Description
|
||||
if (!empty($iptcInfo['2#120'][0])) $return['description'] = $iptcInfo['2#120'][0];
|
||||
|
||||
// Tags
|
||||
if (!empty($iptcInfo['2#025'])) $return['tags'] = implode(',', $iptcInfo['2#025']);
|
||||
|
||||
// Position
|
||||
$fields = array();
|
||||
if (!empty($iptcInfo['2#090'])) $fields[] = trim($iptcInfo['2#090'][0]);
|
||||
if (!empty($iptcInfo['2#092'])) $fields[] = trim($iptcInfo['2#092'][0]);
|
||||
if (!empty($iptcInfo['2#095'])) $fields[] = trim($iptcInfo['2#095'][0]);
|
||||
if (!empty($iptcInfo['2#101'])) $fields[] = trim($iptcInfo['2#101'][0]);
|
||||
|
||||
if (!empty($fields)) $return['position'] = implode(', ', $fields);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Read EXIF
|
||||
if ($info['mime']=='image/jpeg') $exif = @exif_read_data($url, 'EXIF', 0);
|
||||
if ($info['mime']=='image/jpeg') $exif = @exif_read_data($url, 'EXIF', false, false);
|
||||
else $exif = false;
|
||||
|
||||
// EXIF Metadata
|
||||
if ($exif!==false) {
|
||||
|
||||
// Orientation
|
||||
if (isset($exif['Orientation'])) $return['orientation'] = $exif['Orientation'];
|
||||
else if (isset($exif['IFD0']['Orientation'])) $return['orientation'] = $exif['IFD0']['Orientation'];
|
||||
|
||||
$temp = @$exif['ISOSpeedRatings'];
|
||||
if (isset($temp)) $return['iso'] = $temp;
|
||||
// ISO
|
||||
if (!empty($exif['ISOSpeedRatings'])) $return['iso'] = $exif['ISOSpeedRatings'];
|
||||
|
||||
$temp = @$exif['COMPUTED']['ApertureFNumber'];
|
||||
if (isset($temp)) $return['aperture'] = $temp;
|
||||
// Aperture
|
||||
if (!empty($exif['COMPUTED']['ApertureFNumber'])) $return['aperture'] = $exif['COMPUTED']['ApertureFNumber'];
|
||||
|
||||
$temp = @$exif['Make'];
|
||||
if (isset($temp)) $return['make'] = trim($temp);
|
||||
// Make
|
||||
if (!empty($exif['Make'])) $return['make'] = trim($exif['Make']);
|
||||
|
||||
$temp = @$exif['Model'];
|
||||
if (isset($temp)) $return['model'] = trim($temp);
|
||||
// Model
|
||||
if (!empty($exif['Model'])) $return['model'] = trim($exif['Model']);
|
||||
|
||||
$temp = @$exif['ExposureTime'];
|
||||
if (isset($temp)) $return['shutter'] = $exif['ExposureTime'] . ' s';
|
||||
// Exposure
|
||||
if (!empty($exif['ExposureTime'])) $return['shutter'] = $exif['ExposureTime'] . ' s';
|
||||
|
||||
$temp = @$exif['FocalLength'];
|
||||
if (isset($temp)) {
|
||||
if (strpos($temp, '/')!==FALSE) {
|
||||
$temp = explode('/', $temp, 2);
|
||||
// Focal Length
|
||||
if (!empty($exif['FocalLength'])) {
|
||||
if (strpos($exif['FocalLength'], '/')!==false) {
|
||||
$temp = explode('/', $exif['FocalLength'], 2);
|
||||
$temp = $temp[0] / $temp[1];
|
||||
$temp = round($temp, 1);
|
||||
$return['focal'] = $temp . ' mm';
|
||||
} else {
|
||||
$return['focal'] = $exif['FocalLength'] . ' mm';
|
||||
}
|
||||
$return['focal'] = $temp . ' mm';
|
||||
}
|
||||
|
||||
$temp = @$exif['DateTimeOriginal'];
|
||||
if (isset($temp)) $return['takestamp'] = strtotime($temp);
|
||||
// Takestamp
|
||||
if (!empty($exif['DateTimeOriginal'])) $return['takestamp'] = strtotime($exif['DateTimeOriginal']);
|
||||
|
||||
// Lens field from Lightroom
|
||||
if (!empty($exif['UndefinedTag:0xA434'])) $return['lens'] = trim($exif['UndefinedTag:0xA434']);
|
||||
|
||||
// Deal with GPS coordinates
|
||||
if (!empty($exif['GPSLatitude']) && !empty($exif['GPSLatitudeRef'])) $return['latitude'] = getGPSCoordinate($exif['GPSLatitude'], $exif['GPSLatitudeRef']);
|
||||
if (!empty($exif['GPSLongitude']) && !empty($exif['GPSLongitudeRef'])) $return['longitude'] = getGPSCoordinate($exif['GPSLongitude'], $exif['GPSLongitudeRef']);
|
||||
|
||||
}
|
||||
|
||||
|
@ -221,6 +221,13 @@ final class Settings {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Returns the Imagick setting.
|
||||
*/
|
||||
public static function hasImagick() {
|
||||
return (bool)(extension_loaded('imagick') && self::get()['imagick'] === '1');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
30
php/helpers/getGPSCoordinate.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Returns the normalized coordinate from EXIF array.
|
||||
* @return string Normalized coordinate as float number (degrees).
|
||||
*/
|
||||
function getGPSCoordinate($coordinate, $ref) {
|
||||
|
||||
$degrees = count($coordinate) > 0 ? formattedToFloatGPS($coordinate[0]) : 0;
|
||||
$minutes = count($coordinate) > 1 ? formattedToFloatGPS($coordinate[1]) : 0;
|
||||
$seconds = count($coordinate) > 2 ? formattedToFloatGPS($coordinate[2]) : 0;
|
||||
|
||||
$flip = ($ref == 'W' || $ref == 'S') ? -1 : 1;
|
||||
|
||||
return $flip * ($degrees + (float)$minutes / 60 + (float)$seconds / 3600);
|
||||
|
||||
}
|
||||
|
||||
function formattedToFloatGPS($coordinate) {
|
||||
|
||||
$parts = explode('/', $coordinate, 2);
|
||||
|
||||
if (count($parts) <= 0) return 0;
|
||||
if (count($parts) == 1) return $parts[0];
|
||||
|
||||
return (float)$parts[0] / $parts[1];
|
||||
|
||||
}
|
||||
|
||||
?>
|
@ -22,6 +22,7 @@ require(__DIR__ . '/autoload.php');
|
||||
require(__DIR__ . '/helpers/fastImageCopyResampled.php');
|
||||
require(__DIR__ . '/helpers/generateID.php');
|
||||
require(__DIR__ . '/helpers/getExtension.php');
|
||||
require(__DIR__ . '/helpers/getGPSCoordinate.php');
|
||||
require(__DIR__ . '/helpers/getGraphHeader.php');
|
||||
require(__DIR__ . '/helpers/getHashedString.php');
|
||||
require(__DIR__ . '/helpers/hasPermissions.php');
|
||||
|
@ -28,6 +28,7 @@ paths.view = {
|
||||
],
|
||||
scripts: [
|
||||
'node_modules/jquery/dist/jquery.min.js',
|
||||
'node_modules/basiccontext/dist/basicContext.min.js',
|
||||
'../dist/_view--javascript.js'
|
||||
],
|
||||
svg: [
|
||||
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
@ -1 +1 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"><title>Group</title><desc>Created with Sketch.</desc><g sketch:type="MSShapeGroup" fill="none"><path d="M0 0h16v16h-16v-16z" id="Shape" fill="#444"/><path id="Rectangle-path" stroke="#aaa" d="M4 4h5v5h-5z"/><path d="M6 6h8v7h-8v-7z" fill="#444"/><path stroke="#aaa" d="M7 7h5v5h-5z"/></g></svg>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="none"><path d="M0 0h16v16h-16v-16z" fill="#444"/><path stroke="#aaa" d="M4 4h5v5h-5z"/><path d="M6 6h8v7h-8v-7z" fill="#444"/><path stroke="#aaa" d="M7 7h5v5h-5z"/></g></svg>
|
Before Width: | Height: | Size: 432 B After Width: | Height: | Size: 266 B |
@ -1 +1 @@
|
||||
<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"><title>Group</title><desc>Created with Sketch.</desc><g sketch:type="MSLayerGroup" fill="none"><path d="M0 0h200v200h-200v-200z" id="Shape" fill="#222" sketch:type="MSShapeGroup"/><g sketch:type="MSShapeGroup"><path id="Rectangle-path" stroke="#B4B4B4" stroke-width="4" d="M68 52h50v42h-50z"/><path d="M79 63h44v36h-44v-36z" fill="#222"/><path stroke="#B4B4B4" stroke-width="4" d="M88 72h50v42h-50z"/></g></g></svg>
|
||||
<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg"><g fill="none"><path d="M0 0h200v200h-200v-200z" fill="#222"/><g><path stroke="#B4B4B4" stroke-width="4" d="M68 52h50v42h-50z"/><path d="M79 63h44v36h-44v-36z" fill="#222"/><path stroke="#B4B4B4" stroke-width="4" d="M88 72h50v42h-50z"/></g></g></svg>
|
Before Width: | Height: | Size: 557 B After Width: | Height: | Size: 337 B |
@ -1 +1 @@
|
||||
<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"><title>Group</title><desc>Created with Sketch.</desc><g sketch:type="MSLayerGroup" fill="none"><path d="M0 0h200v200h-200v-200z" id="Shape" fill="#222" sketch:type="MSShapeGroup"/><path d="M100.5 49c-10.45 0-19 8.614-19 19.143v9.571h-9.5v38.286h57v-38.286h-9.5v-9.571c0-10.529-8.55-19.143-19-19.143zm0 9.571c5.32 0 9.5 4.211 9.5 9.571v9.571h-19v-9.571c0-5.36 4.18-9.571 9.5-9.571z" fill="#B4B4B4" sketch:type="MSShapeGroup"/></g></svg>
|
||||
<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg"><g fill="none"><path d="M0 0h200v200h-200v-200z" id="Shape" fill="#222"/><path d="M100.5 49c-10.45 0-19 8.614-19 19.143v9.571h-9.5v38.286h57v-38.286h-9.5v-9.571c0-10.529-8.55-19.143-19-19.143zm0 9.571c5.32 0 9.5 4.211 9.5 9.571v9.571h-19v-9.571c0-5.36 4.18-9.571 9.5-9.571z" fill="#B4B4B4"/></g></svg>
|
Before Width: | Height: | Size: 577 B After Width: | Height: | Size: 388 B |
70
src/npm-shrinkwrap.json
generated
@ -32,6 +32,11 @@
|
||||
"from": "ansi-styles@>=2.1.0 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.0.tgz"
|
||||
},
|
||||
"any-promise": {
|
||||
"version": "1.1.0",
|
||||
"from": "any-promise@>=1.1.0 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.1.0.tgz"
|
||||
},
|
||||
"archy": {
|
||||
"version": "1.0.0",
|
||||
"from": "archy@>=1.0.0 <2.0.0",
|
||||
@ -42,6 +47,11 @@
|
||||
"from": "are-we-there-yet@>=1.1.2 <1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz"
|
||||
},
|
||||
"arr-flatten": {
|
||||
"version": "1.0.1",
|
||||
"from": "arr-flatten@>=1.0.1 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.0.1.tgz"
|
||||
},
|
||||
"array-differ": {
|
||||
"version": "1.0.0",
|
||||
"from": "array-differ@>=1.0.0 <2.0.0",
|
||||
@ -67,6 +77,11 @@
|
||||
"from": "array-uniq@>=1.0.2 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.2.tgz"
|
||||
},
|
||||
"arrify": {
|
||||
"version": "1.0.1",
|
||||
"from": "arrify@>=1.0.1 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz"
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.2.3",
|
||||
"from": "asn1@>=0.2.3 <0.3.0",
|
||||
@ -341,8 +356,8 @@
|
||||
"from": "basiccontext@>=3.5.1 <4.0.0"
|
||||
},
|
||||
"basicmodal": {
|
||||
"version": "3.3.3",
|
||||
"from": "basicmodal@>=3.3.3 <4.0.0"
|
||||
"version": "3.3.4",
|
||||
"from": "basicmodal@>=3.3.4 <4.0.0"
|
||||
},
|
||||
"beeper": {
|
||||
"version": "1.1.0",
|
||||
@ -751,6 +766,18 @@
|
||||
"from": "get-stdin@>=4.0.1 <5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz"
|
||||
},
|
||||
"get-value": {
|
||||
"version": "1.3.1",
|
||||
"from": "get-value@>=1.1.5 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/get-value/-/get-value-1.3.1.tgz",
|
||||
"dependencies": {
|
||||
"lazy-cache": {
|
||||
"version": "0.2.7",
|
||||
"from": "lazy-cache@>=0.2.4 <0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
"glob": {
|
||||
"version": "5.0.15",
|
||||
"from": "glob@>=5.0.0 <5.1.0",
|
||||
@ -840,6 +867,18 @@
|
||||
"from": "graceful-readlink@>=1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz"
|
||||
},
|
||||
"group-array": {
|
||||
"version": "0.3.0",
|
||||
"from": "group-array@>=0.3.0 <0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/group-array/-/group-array-0.3.0.tgz",
|
||||
"dependencies": {
|
||||
"kind-of": {
|
||||
"version": "2.0.1",
|
||||
"from": "kind-of@>=2.0.0 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
"gulp": {
|
||||
"version": "3.9.1",
|
||||
"from": "gulp@>=3.9.1 <4.0.0",
|
||||
@ -880,9 +919,9 @@
|
||||
}
|
||||
},
|
||||
"gulp-inject": {
|
||||
"version": "3.0.0",
|
||||
"from": "gulp-inject@>=3.0.0 <4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/gulp-inject/-/gulp-inject-3.0.0.tgz"
|
||||
"version": "4.0.0",
|
||||
"from": "gulp-inject@>=4.0.0 <5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/gulp-inject/-/gulp-inject-4.0.0.tgz"
|
||||
},
|
||||
"gulp-load-plugins": {
|
||||
"version": "1.2.0",
|
||||
@ -1074,6 +1113,11 @@
|
||||
"from": "is-builtin-module@>=1.0.0 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz"
|
||||
},
|
||||
"is-extendable": {
|
||||
"version": "0.1.1",
|
||||
"from": "is-extendable@>=0.1.1 <0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz"
|
||||
},
|
||||
"is-finite": {
|
||||
"version": "1.0.1",
|
||||
"from": "is-finite@>=1.0.0 <2.0.0",
|
||||
@ -1140,9 +1184,9 @@
|
||||
"resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz"
|
||||
},
|
||||
"jquery": {
|
||||
"version": "2.2.1",
|
||||
"from": "jquery@>=2.2.1 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jquery/-/jquery-2.2.1.tgz"
|
||||
"version": "2.2.3",
|
||||
"from": "jquery@>=2.2.3 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jquery/-/jquery-2.2.3.tgz"
|
||||
},
|
||||
"js-base64": {
|
||||
"version": "2.1.9",
|
||||
@ -1466,6 +1510,11 @@
|
||||
"from": "node-uuid@>=1.4.7 <1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz"
|
||||
},
|
||||
"noncharacters": {
|
||||
"version": "1.1.0",
|
||||
"from": "noncharacters@>=1.1.0 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/noncharacters/-/noncharacters-1.1.0.tgz"
|
||||
},
|
||||
"nopt": {
|
||||
"version": "3.0.6",
|
||||
"from": "nopt@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0",
|
||||
@ -1856,6 +1905,11 @@
|
||||
"from": "stream-consume@>=0.1.0 <0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.0.tgz"
|
||||
},
|
||||
"stream-to-array": {
|
||||
"version": "2.3.0",
|
||||
"from": "stream-to-array@>=2.3.0 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/stream-to-array/-/stream-to-array-2.3.0.tgz"
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "0.10.31",
|
||||
"from": "string_decoder@>=0.10.0 <0.11.0",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Lychee",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Self-hosted photo-management done right.",
|
||||
"authors": "Tobias Reich <tobias@electerious.com>",
|
||||
"license": "MIT",
|
||||
@ -16,18 +16,18 @@
|
||||
"dependencies": {
|
||||
"babel-preset-es2015": "^6.6.0",
|
||||
"basiccontext": "^3.5.1",
|
||||
"basicmodal": "^3.3.3",
|
||||
"basicmodal": "^3.3.5",
|
||||
"gulp": "^3.9.1",
|
||||
"gulp-autoprefixer": "3.1.0",
|
||||
"gulp-babel": "^6.1.2",
|
||||
"gulp-concat": "^2.6.0",
|
||||
"gulp-inject": "^3.0.0",
|
||||
"gulp-load-plugins": "^1.2.0",
|
||||
"gulp-inject": "^4.0.0",
|
||||
"gulp-load-plugins": "^1.2.2",
|
||||
"gulp-minify-css": "^1.2.4",
|
||||
"gulp-rimraf": "^0.2.0",
|
||||
"gulp-sass": "^2.2.0",
|
||||
"gulp-sass": "^2.3.1",
|
||||
"gulp-uglify": "^1.5.3",
|
||||
"jquery": "^2.2.1",
|
||||
"jquery": "^2.2.3",
|
||||
"mousetrap": "^1.5.3"
|
||||
}
|
||||
}
|
||||
|
@ -509,26 +509,20 @@ album.setPublic = function(albumID, modal, e) {
|
||||
|
||||
album.share = function(service) {
|
||||
|
||||
let link = ''
|
||||
let url = location.href
|
||||
|
||||
switch (service) {
|
||||
case 'twitter':
|
||||
link = `https://twitter.com/share?url=${ encodeURI(url) }`
|
||||
window.open(`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) }`
|
||||
window.open(`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 = ''
|
||||
location.href = `mailto:?subject=${ encodeURI(album.json.title) }&body=${ encodeURI(url) }`
|
||||
break
|
||||
}
|
||||
|
||||
if (link!=='') location.href = link
|
||||
|
||||
}
|
||||
|
||||
album.getArchive = function(albumID) {
|
||||
|
@ -304,12 +304,14 @@ contextMenu.sharePhoto = function(photoID, e) {
|
||||
{ title: build.iconic('twitter', iconClass) + 'Twitter', fn: () => photo.share(photoID, 'twitter') },
|
||||
{ title: build.iconic('facebook', iconClass) + 'Facebook', fn: () => photo.share(photoID, 'facebook') },
|
||||
{ title: build.iconic('envelope-closed') + 'Mail', fn: () => photo.share(photoID, 'mail') },
|
||||
{ title: build.iconic('dropbox', iconClass) + 'Dropbox', fn: () => photo.share(photoID, 'dropbox') },
|
||||
{ title: build.iconic('dropbox', iconClass) + 'Dropbox', visible: lychee.publicMode===false, fn: () => photo.share(photoID, 'dropbox') },
|
||||
{ title: build.iconic('link-intact') + 'Direct Link', fn: () => window.open(photo.getDirectLink()) },
|
||||
{ },
|
||||
{ title: build.iconic('ban') + 'Make Private', fn: () => photo.setPublic(photoID) }
|
||||
{ title: build.iconic('ban') + 'Make Private', visible: lychee.publicMode===false, fn: () => photo.setPublic(photoID) }
|
||||
]
|
||||
|
||||
if (lychee.publicMode===true) items.splice(7, 1)
|
||||
|
||||
basicContext.show(items, e.originalEvent)
|
||||
$('.basicContext input#link').focus().select()
|
||||
|
||||
@ -326,10 +328,12 @@ contextMenu.shareAlbum = function(albumID, e) {
|
||||
{ title: build.iconic('facebook', iconClass) + 'Facebook', fn: () => album.share('facebook') },
|
||||
{ title: build.iconic('envelope-closed') + 'Mail', fn: () => album.share('mail') },
|
||||
{ },
|
||||
{ title: build.iconic('pencil') + 'Edit Sharing', fn: () => album.setPublic(albumID, true, e) },
|
||||
{ title: build.iconic('ban') + 'Make Private', fn: () => album.setPublic(albumID, false) }
|
||||
{ title: build.iconic('pencil') + 'Edit Sharing', visible: lychee.publicMode===false, fn: () => album.setPublic(albumID, true, e) },
|
||||
{ title: build.iconic('ban') + 'Make Private', visible: lychee.publicMode===false, fn: () => album.setPublic(albumID, false) }
|
||||
]
|
||||
|
||||
if (lychee.publicMode===true) items.splice(5, 1)
|
||||
|
||||
basicContext.show(items, e.originalEvent)
|
||||
$('.basicContext input#link').focus().select()
|
||||
|
||||
|
@ -6,8 +6,8 @@
|
||||
lychee = {
|
||||
|
||||
title : document.title,
|
||||
version : '3.1.0',
|
||||
versionCode : '030100',
|
||||
version : '3.1.1',
|
||||
versionCode : '030101',
|
||||
|
||||
updatePath : '//update.electerious.com/index.json',
|
||||
updateURL : 'https://github.com/electerious/Lychee',
|
||||
@ -236,8 +236,14 @@ lychee.setTitle = function(title, editable) {
|
||||
|
||||
lychee.setMode = function(mode) {
|
||||
|
||||
$('#button_settings, #button_trash_album, #button_share_album, .button_add, .header__divider').remove()
|
||||
$('#button_trash, #button_move, #button_share, #button_star').remove()
|
||||
$('#button_settings, #button_trash_album, .button_add, .header__divider').remove()
|
||||
$('#button_trash, #button_move, #button_star').remove()
|
||||
|
||||
$('#button_share, #button_share_album')
|
||||
.removeClass('button--eye')
|
||||
.addClass('button--share')
|
||||
.find('use')
|
||||
.attr('xlink:href', '#share')
|
||||
|
||||
$(document)
|
||||
.off('click', '.header__title--editable')
|
||||
|
@ -610,18 +610,17 @@ photo.deleteTag = function(photoID, index) {
|
||||
|
||||
photo.share = function(photoID, service) {
|
||||
|
||||
let link = ''
|
||||
let url = photo.getViewLink(photoID)
|
||||
|
||||
switch (service) {
|
||||
case 'twitter':
|
||||
link = `https://twitter.com/share?url=${ encodeURI(url) }`
|
||||
window.open(`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) }`
|
||||
window.open(`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) }`
|
||||
location.href = `mailto:?subject=${ encodeURI(photo.json.title) }&body=${ encodeURI(url) }`
|
||||
break
|
||||
case 'dropbox':
|
||||
lychee.loadDropbox(function() {
|
||||
@ -629,13 +628,8 @@ photo.share = function(photoID, service) {
|
||||
Dropbox.save(photo.getDirectLink(), filename)
|
||||
})
|
||||
break
|
||||
default:
|
||||
link = ''
|
||||
break
|
||||
}
|
||||
|
||||
if (link!=='') location.href = link
|
||||
|
||||
}
|
||||
|
||||
photo.getArchive = function(photoID) {
|
||||
|
@ -3,21 +3,12 @@
|
||||
* @copyright 2015 by Tobias Reich
|
||||
*/
|
||||
|
||||
// Sub-implementation of Lychee -------------------------------------------------------------- //
|
||||
// Sub-implementation of lychee -------------------------------------------------------------- //
|
||||
|
||||
let lychee = {}
|
||||
|
||||
lychee.content = $('.content')
|
||||
|
||||
lychee.getEventName = function() {
|
||||
|
||||
let touchendSupport = (/Android|iPhone|iPad|iPod/i).test(navigator.userAgent || navigator.vendor || window.opera) && ('ontouchend' in document.documentElement)
|
||||
let eventName = (touchendSupport===true ? 'touchend' : 'click')
|
||||
|
||||
return eventName
|
||||
|
||||
}
|
||||
|
||||
lychee.escapeHTML = function(html = '') {
|
||||
|
||||
// Ensure that html is a string
|
||||
@ -69,31 +60,85 @@ lychee.html = function(literalSections, ...substs) {
|
||||
|
||||
}
|
||||
|
||||
lychee.getEventName = function() {
|
||||
|
||||
let touchendSupport = (/Android|iPhone|iPad|iPod/i).test(navigator.userAgent || navigator.vendor || window.opera) && ('ontouchend' in document.documentElement)
|
||||
let eventName = (touchendSupport===true ? 'touchend' : 'click')
|
||||
|
||||
return eventName
|
||||
|
||||
}
|
||||
|
||||
// Sub-implementation of photo -------------------------------------------------------------- //
|
||||
|
||||
let photo = {}
|
||||
|
||||
photo.share = function(photoID, service) {
|
||||
|
||||
let url = location.toString()
|
||||
|
||||
switch (service) {
|
||||
case 'twitter':
|
||||
window.open(`https://twitter.com/share?url=${ encodeURI(url) }`)
|
||||
break
|
||||
case 'facebook':
|
||||
window.open(`http://www.facebook.com/sharer.php?u=${ encodeURI(url) }`)
|
||||
break
|
||||
case 'mail':
|
||||
location.href = `mailto:?subject=&body=${ encodeURI(url) }`
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
photo.getDirectLink = function() {
|
||||
|
||||
return $('#imageview img').attr('src').replace(/"/g,'').replace(/url\(|\)$/ig, '')
|
||||
|
||||
}
|
||||
|
||||
// Sub-implementation of contextMenu -------------------------------------------------------------- //
|
||||
|
||||
let contextMenu = {}
|
||||
|
||||
contextMenu.sharePhoto = function(photoID, e) {
|
||||
|
||||
let iconClass = 'ionicons'
|
||||
|
||||
let items = [
|
||||
{ title: build.iconic('twitter', iconClass) + 'Twitter', fn: () => photo.share(photoID, 'twitter') },
|
||||
{ title: build.iconic('facebook', iconClass) + 'Facebook', fn: () => photo.share(photoID, 'facebook') },
|
||||
{ title: build.iconic('envelope-closed') + 'Mail', fn: () => photo.share(photoID, 'mail') },
|
||||
{ title: build.iconic('link-intact') + 'Direct Link', fn: () => window.open(photo.getDirectLink(), '_newtab') }
|
||||
]
|
||||
|
||||
basicContext.show(items, e.originalEvent)
|
||||
|
||||
}
|
||||
|
||||
// Main -------------------------------------------------------------- //
|
||||
|
||||
let loadingBar = { show() {}, hide() {} },
|
||||
imageview = $('#imageview')
|
||||
let loadingBar = { show() {}, hide() {} }
|
||||
let imageview = $('#imageview')
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
// Event Name
|
||||
let eventName = lychee.getEventName()
|
||||
// Save ID of photo
|
||||
let photoID = gup('p')
|
||||
|
||||
// Set API error handler
|
||||
api.onError = error
|
||||
|
||||
// Infobox
|
||||
header.dom('#button_info').on(eventName, sidebar.toggle)
|
||||
|
||||
// Direct Link
|
||||
header.dom('#button_direct').on(eventName, function() {
|
||||
|
||||
let link = $('#imageview img').attr('src').replace(/"/g,'').replace(/url\(|\)$/ig, '')
|
||||
window.open(link, '_newtab')
|
||||
|
||||
// Share
|
||||
header.dom('#button_share').on('click', function(e) {
|
||||
contextMenu.sharePhoto(photoID, e)
|
||||
})
|
||||
|
||||
loadPhotoInfo(gup('p'))
|
||||
// Infobox
|
||||
header.dom('#button_info').on('click', sidebar.toggle)
|
||||
|
||||
// Load photo
|
||||
loadPhotoInfo(photoID)
|
||||
|
||||
})
|
||||
|
||||
|
@ -57,10 +57,10 @@
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
background: #222;
|
||||
color: #222;
|
||||
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,
|
||||
|
@ -95,6 +95,8 @@
|
||||
|
||||
&--eye.active .iconic { fill: #ff9737; }
|
||||
|
||||
&--share .iconic { height: 18px; }
|
||||
|
||||
&--info.active .iconic { fill: $colorBlue; }
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,6 @@
|
||||
width: auto;
|
||||
height: auto;
|
||||
transition: top .3s, right .3s, bottom .3s, left .3s, max-width .3s, max-height .3s;
|
||||
will-change: transform;
|
||||
|
||||
animation-name: zoomIn;
|
||||
animation-duration: .3s;
|
||||
|
@ -92,6 +92,7 @@
|
||||
table {
|
||||
float: left;
|
||||
margin: 10px 0 15px 20px;
|
||||
width: calc(100% - 20px);
|
||||
}
|
||||
|
||||
table tr td {
|
||||
|
6
view.php
@ -48,12 +48,12 @@
|
||||
|
||||
<a class="header__title"></a>
|
||||
|
||||
<a class="button button--share" id="button_share" title="Share Photo">
|
||||
<svg class="iconic"><use xlink:href="#share"></use></svg>
|
||||
</a>
|
||||
<a class="button button--info" id="button_info" title="About Photo">
|
||||
<svg class="iconic"><use xlink:href="#info"></use></svg>
|
||||
</a>
|
||||
<a class="button" id="button_direct" title="Direct Link">
|
||||
<svg class="iconic"><use xlink:href="#link-intact"></use></svg>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
|