Browse Source

Merge pull request #534 from electerious/develop

Lychee 3.1.1
Tobias Reich 8 years ago
parent
commit
75b11acab3

+ 0 - 0
LICENSE.md → LICENSE


File diff suppressed because it is too large
+ 0 - 0
dist/main.css


File diff suppressed because it is too large
+ 0 - 0
dist/main.js


File diff suppressed because it is too large
+ 0 - 0
dist/view.js


+ 14 - 0
docs/Changelog.md

@@ -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

+ 6 - 3
docs/FAQ.md

@@ -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.

+ 1 - 1
docs/Update.md

@@ -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)
 

+ 10 - 1
php/Modules/Database.php

@@ -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.
 	 */

+ 65 - 43
php/Modules/Photo.php

@@ -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,86 +727,108 @@ final class Photo {
 		$info      = getimagesize($url, $iptcArray);
 
 		// General information
-		$return['type']   = $info['mime'];
-		$return['width']  = $info[0];
-		$return['height'] = $info[1];
+		$return['type']        = $info['mime'];
+		$return['width']       = $info[0];
+		$return['height']      = $info[1];
+		$return['title']       = '';
+		$return['description'] = '';
+		$return['orientation'] = '';
+		$return['iso']         = '';
+		$return['aperture']    = '';
+		$return['make']        = '';
+		$return['model']       = '';
+		$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 Fallback
-		$return['title']       = '';
-		$return['description'] = '';
-
 		// 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)) {
 
-				$temp = @$iptcInfo['2#105'][0];
-				if (isset($temp)&&strlen($temp)>0) $return['title'] = $temp;
+				// 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];
 
-				$temp = @$iptcInfo['2#120'][0];
-				if (isset($temp)&&strlen($temp)>0) $return['description'] = $temp;
+				// Description
+				if (!empty($iptcInfo['2#120'][0])) $return['description'] = $iptcInfo['2#120'][0];
 
-				$temp = @$iptcInfo['2#005'][0];
-				if (isset($temp)&&strlen($temp)>0&&$return['title']==='') $return['title'] = $temp;
+				// 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);
 
 			}
 
 		}
 
-		// EXIF Metadata Fallback
-		$return['orientation'] = '';
-		$return['iso']         = '';
-		$return['aperture']    = '';
-		$return['make']        = '';
-		$return['model']       = '';
-		$return['shutter']     = '';
-		$return['focal']       = '';
-		$return['takestamp']   = 0;
-
 		// 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']);
 
 		}
 

+ 7 - 0
php/Modules/Settings.php

@@ -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 - 0
php/helpers/getGPSCoordinate.php

@@ -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];
+
+}
+
+?>

+ 1 - 0
php/index.php

@@ -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');

+ 1 - 0
src/gulpfile.js

@@ -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: [

BIN
src/images/apple-touch-icon-ipad.png


BIN
src/images/apple-touch-icon-iphone-plus.png


BIN
src/images/apple-touch-icon-iphone.png


+ 1 - 1
src/images/no_cover.svg

@@ -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>

+ 1 - 1
src/images/no_images.svg

@@ -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>

+ 1 - 1
src/images/password.svg

@@ -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>

+ 62 - 8
src/npm-shrinkwrap.json

@@ -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",

+ 6 - 6
src/package.json

@@ -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"
   }
 }

+ 3 - 9
src/scripts/album.js

@@ -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) {

+ 8 - 4
src/scripts/contextMenu.js

@@ -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()
 

+ 10 - 4
src/scripts/lychee.js

@@ -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')

+ 3 - 9
src/scripts/photo.js

@@ -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) {

+ 69 - 24
src/scripts/view/main.js

@@ -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)
 
 })
 

+ 1 - 1
src/styles/_content.scss

@@ -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,

+ 2 - 0
src/styles/_header.scss

@@ -95,6 +95,8 @@
 
 		&--eye.active .iconic { fill: #ff9737; }
 
+		&--share .iconic { height: 18px; }
+
 		&--info.active .iconic { fill: $colorBlue; }
 	}
 

+ 0 - 1
src/styles/_imageview.scss

@@ -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;

+ 1 - 0
src/styles/_sidebar.scss

@@ -92,6 +92,7 @@
 	table {
 		float: left;
 		margin: 10px 0 15px 20px;
+		width: calc(100% - 20px);
 	}
 
 	table tr td {

+ 3 - 3
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>
 

Some files were not shown because too many files changed in this diff