Browse Source

V2.0

- All new redefined interface
- Faster animations and transitions
- Import from Dropbox
- Import from Server
- Download public albums
- Several sorting options
- Installation assistent
- Infobox and description for albums
- Faster loading and improved performance
- Better file handling and upload
- Album covers are chosen intelligent
- Prettier URLs
- Massive changes under the hood
- IPTC support (Headline and Caption)
- EXIF Orientation support

How to update:
1. Replace all files, excluding `uploads/`
2. Open Lychee and enter your database details
Tobias Reich 5 years ago
parent
commit
dd2f6ebc77
100 changed files with 5618 additions and 4686 deletions
  1. 4 4
      .htaccess
  2. 5 5
      .user.ini
  3. 1 0
      assets/css/min/main.css
  4. 11 0
      assets/css/min/reset.css
  5. 311 0
      assets/css/modules/animations.css
  6. 256 0
      assets/css/modules/content.css
  7. 101 0
      assets/css/modules/contextmenu.css
  8. 3 3
      css/font-awesome.css
  9. 165 0
      assets/css/modules/header.css
  10. 88 0
      assets/css/modules/imageview.css
  11. 173 0
      assets/css/modules/infobox.css
  12. 59 0
      assets/css/modules/loading.css
  13. 61 0
      assets/css/modules/mediaquery.css
  14. 198 0
      assets/css/modules/message.css
  15. 35 0
      assets/css/modules/misc.css
  16. 39 0
      assets/css/modules/tooltip.css
  17. 123 0
      assets/css/modules/upload.css
  18. 0 0
      assets/font/fontawesome-webfont.eot
  19. 0 0
      assets/font/fontawesome-webfont.svg
  20. 0 0
      assets/font/fontawesome-webfont.ttf
  21. 0 0
      assets/font/fontawesome-webfont.woff
  22. BIN
      assets/img/apple-touch-icon-ipad.png
  23. BIN
      assets/img/apple-touch-icon-iphone.png
  24. BIN
      assets/img/favicon.ico
  25. 1 0
      assets/img/no_images.svg
  26. 1 0
      assets/img/password.svg
  27. 0 0
      assets/js/min/frameworks.js
  28. 2 0
      assets/js/min/main.js
  29. 117 56
      js/modules/album.js
  30. 12 19
      js/modules/albums.js
  31. 410 0
      assets/js/modules/build.js
  32. 120 112
      js/modules/contextMenu.js
  33. 138 0
      assets/js/modules/init.js
  34. 11 15
      js/modules/loadingBar.js
  35. 97 58
      js/modules/lychee.js
  36. 35 0
      assets/js/modules/modal.js
  37. 22 19
      js/modules/password.js
  38. 315 0
      assets/js/modules/photo.js
  39. 12 15
      js/modules/search.js
  40. 238 0
      assets/js/modules/settings.js
  41. 253 0
      assets/js/modules/upload.js
  42. 109 49
      js/modules/view.js
  43. 13 11
      js/modules/visible.js
  44. 11 10
      js/view.js
  45. 0 485
      css/animations.css
  46. 0 1398
      css/style.css
  47. BIN
      docs/assets/favicon.sketch/Data
  48. BIN
      docs/assets/favicon.sketch/QuickLook/Preview.png
  49. BIN
      docs/assets/favicon.sketch/QuickLook/Thumbnail.png
  50. 1 0
      docs/assets/favicon.sketch/fonts
  51. 14 0
      docs/assets/favicon.sketch/metadata
  52. 1 0
      docs/assets/favicon.sketch/version
  53. BIN
      docs/assets/thumbs.sketch/Data
  54. BIN
      docs/assets/thumbs.sketch/QuickLook/Preview.png
  55. BIN
      docs/assets/thumbs.sketch/QuickLook/Thumbnail.png
  56. 0 0
      docs/assets/thumbs.sketch/fonts
  57. 14 0
      docs/assets/thumbs.sketch/metadata
  58. 1 0
      docs/assets/thumbs.sketch/version
  59. 32 0
      docs/compile.sh
  60. 40 0
      docs/install.sh
  61. 16 0
      docs/md/Changelog.md
  62. 44 0
      docs/md/FAQ.md
  63. 13 0
      docs/md/FTP Upload.md
  64. 29 0
      docs/md/Installation.md
  65. 17 0
      docs/md/Keyboard Shortcuts.md
  66. 41 0
      docs/md/Settings.md
  67. BIN
      img/apple-touch-icon.png
  68. BIN
      img/background.jpg
  69. BIN
      img/bar/error.png
  70. BIN
      img/bar/loading.png
  71. BIN
      img/checks.png
  72. BIN
      img/favicon.png
  73. BIN
      img/no_images.png
  74. BIN
      img/no_images@2x.png
  75. BIN
      img/password.png
  76. BIN
      img/password@2x.png
  77. 53 35
      index.html
  78. 0 1
      js/functions.js
  79. 0 147
      js/main.js
  80. 0 288
      js/modules/build.js
  81. 0 37
      js/modules/modal.js
  82. 0 403
      js/modules/photo.js
  83. 0 431
      js/modules/photos.js
  84. 0 48
      lychee.sql
  85. 281 114
      php/api.php
  86. 0 45
      php/check.php
  87. 0 27
      php/config.php
  88. 0 801
      php/functions.php
  89. 354 0
      php/modules/album.php
  90. 183 0
      php/modules/db.php
  91. 95 0
      php/modules/misc.php
  92. 167 0
      php/modules/photo.php
  93. 64 0
      php/modules/session.php
  94. 137 0
      php/modules/settings.php
  95. 378 0
      php/modules/upload.php
  96. 0 17
      php/update.php
  97. 65 0
      plugins/check.php
  98. 28 33
      readme.md
  99. 0 0
      uploads/big/index.html
  100. 0 0
      uploads/import/index.html

+ 4 - 4
.htaccess

@@ -2,9 +2,9 @@ IndexIgnore *
 
 # Uncomment these lines to change PHP parameters if you are using the PHP Apache module
 #<IfModule mod_php5.c>
-#	php_value max_execution_time 300
-#	php_value post_max_size 100M
-#	php_value upload_max_size 100M
-#	php_value upload_max_filesize 5M
+#	php_value max_execution_time 200
+#	php_value post_max_size 200M
+#	php_value upload_max_size 200M
+#	php_value upload_max_filesize 20M
 #	php_value max_file_uploads 100
 #</IfModule>

+ 5 - 5
.user.ini

@@ -1,8 +1,8 @@
 ; Uncomment these lines to change PHP parameters if you are using PHP with CGI or FastCGI. Only works with PHP starting from 5.3.0.
 ; Note that with FastCGI, you might also want to set FcgidBusyTimeout, FcgidIOTimeout and FcgidMaxRequestLen in your Apache config
 
-;max_execution_time = 60
-;post_max_size = 100M
-;upload_max_size = 100M
-;upload_max_filesize = 5M
-;max_file_uploads = 100
+;max_execution_time = 200
+;post_max_size = 200M
+;upload_max_size = 200M
+;upload_max_filesize = 20M
+;max_file_uploads = 200

File diff suppressed because it is too large
+ 1 - 0
assets/css/min/main.css


File diff suppressed because it is too large
+ 11 - 0
assets/css/min/reset.css


+ 311 - 0
assets/css/modules/animations.css

@@ -0,0 +1,311 @@
+/**
+ * @name        animations.css
+ * @author      Tobias Reich
+ * @copyright   2014 by Tobias Reich
+ */
+
+/* Animation Setter ------------------------------------------------*/
+.fadeIn {
+	-webkit-animation-name: fadeIn;
+	-webkit-animation-duration: .3s;
+	-webkit-animation-fill-mode: forwards;
+	-moz-animation-name: fadeIn;
+	-moz-animation-duration: .3s;
+	-moz-animation-fill-mode: forwards;
+	animation-name: fadeIn;
+	animation-duration: .3s;
+	animation-fill-mode: forwards; }
+
+.fadeOut {
+	-webkit-animation-name: fadeOut;
+	-webkit-animation-duration: .3s;
+	-webkit-animation-fill-mode: forwards;
+	-moz-animation-name: fadeOut;
+	-moz-animation-duration: .3s;
+	-moz-animation-fill-mode: forwards;
+	animation-name: fadeOut;
+	animation-duration: .3s;
+	animation-fill-mode: forwards; }
+
+.contentZoomIn {
+	-webkit-animation-name: zoomIn;
+	-webkit-animation-duration: .2s;
+	-webkit-animation-fill-mode: forwards;
+	-moz-animation-name: zoomIn;
+	-moz-animation-duration: .2s;
+	-moz-animation-fill-mode: forwards;
+	animation-name: zoomIn;
+	animation-duration: .2s;
+	animation-fill-mode: forwards; }
+
+.contentZoomOut {
+	-webkit-animation-name: zoomOut;
+	-webkit-animation-duration: .2s;
+	-webkit-animation-fill-mode: forwards;
+	-moz-animation-name: zoomOut;
+	-moz-animation-duration: .2s;
+	-moz-animation-fill-mode: forwards;
+	animation-name: zoomOut;
+	animation-duration: .2s;
+	animation-fill-mode: forwards; }
+
+/* moveUp ------------------------------------------------*/
+@-webkit-keyframes moveUp {
+	0% {
+		-webkit-transform: translateY(30px);
+		opacity: 0;
+	}
+	100% {
+		-webkit-transform: translateY(0);
+		opacity: 1;
+	}
+}
+@-moz-keyframes moveUp {
+	0% {
+		opacity: 0;
+	}
+	100% {
+		opacity: 1;
+	}
+}
+@keyframes moveUp {
+	0% {
+		transform: translateY(30px);
+		opacity: 0;
+	}
+	100% {
+		transform: translateY(0);
+		opacity: 1;
+	}
+}
+
+/* fadeIn ------------------------------------------------*/
+@-webkit-keyframes fadeIn {
+	0% {
+		opacity: 0;
+	}
+	100% {
+		opacity: 1;
+	}
+}
+@-moz-keyframes fadeIn {
+	0% {
+		opacity: 0;
+	}
+	100% {
+		opacity: 1;
+	}
+}
+@keyframes fadeIn {
+	0% {
+		opacity: 0;
+	}
+	100% {
+		opacity: 1;
+	}
+}
+
+/* fadeOut ------------------------------------------------*/
+@-webkit-keyframes fadeOut {
+	0% {
+		opacity: 1;
+	}
+	100% {
+		opacity: 0;
+	}
+}
+@-moz-keyframes fadeOut {
+	0% {
+		opacity: 1;
+	}
+	100% {
+		opacity: 0;
+	}
+}
+@keyframes fadeOut {
+	0% {
+		opacity: 1;
+	}
+	100% {
+		opacity: 0;
+	}
+}
+
+/* moveBackground ------------------------------------------------*/
+@-webkit-keyframes moveBackground {
+	0% {
+		background-position-x: 0px;
+	}
+	100% {
+		background-position-x: -100px;
+	}
+}
+@-moz-keyframes moveBackground {
+	0% {
+		background-position-x: 0px;
+	}
+	100% {
+		background-position-x: -100px;
+	}
+}
+@keyframes moveBackground {
+	0% {
+		background-position-x: 0px;
+	}
+	100% {
+		background-position-x: -100px;
+	}
+}
+
+/* zoomIn ------------------------------------------------*/
+@-webkit-keyframes zoomIn {
+	0% {
+		opacity: 0;
+		-webkit-transform: scale(.8);
+	}
+	100% {
+		opacity: 1;
+		-webkit-transform: scale(1);
+	}
+}
+@-moz-keyframes zoomIn {
+	0% {
+		opacity: 0;
+	}
+	100% {
+		opacity: 1;
+	}
+}
+@keyframes zoomIn {
+	0% {
+		opacity: 0;
+		transform: scale(.8);
+	}
+	100% {
+		opacity: 1;
+		transform: scale(1);
+	}
+}
+
+/* zoomOut ------------------------------------------------*/
+@-webkit-keyframes zoomOut {
+	0% {
+		opacity: 1;
+		-webkit-transform: scale(1);
+	}
+	100% {
+		opacity: 0;
+		-webkit-transform: scale(.8);
+	}
+}
+@-moz-keyframes zoomOut {
+	0% {
+		opacity: 1;
+	}
+	100% {
+		opacity: 0;
+	}
+}
+@keyframes zoomOut {
+	0% {
+		opacity: 1;
+		transform: scale(1);
+	}
+	100% {
+		opacity: 0;
+		transform: scale(.8);
+	}
+}
+
+/* popIn ------------------------------------------------*/
+@-webkit-keyframes popIn {
+	0% {
+		opacity: 0;
+		-webkit-transform: scale(0);
+	}
+	100% {
+		opacity: 1;
+		-webkit-transform: scale(1);
+	}
+}
+@-moz-keyframes popIn {
+	0% {
+		opacity: 0;
+		-moz-transform: scale(0);
+	}
+	100% {
+		opacity: 1;
+		-moz-transform: scale(1);
+	}
+}
+@keyframes popIn {
+	0% {
+		opacity: 0;
+		transform: scale(0);
+	}
+	100% {
+		opacity: 1;
+		transform: scale(1);
+	}
+}
+
+/* pulse ------------------------------------------------*/
+@-webkit-keyframes pulse {
+	0% {
+		opacity: 1;
+	}
+	50% {
+		opacity: 0.3;
+	}
+	100% {
+		opacity: 1;
+	}
+}
+@-moz-keyframes pulse {
+	0% {
+		opacity: 1;
+	}
+	50% {
+		opacity: 0.8;
+	}
+	100% {
+		opacity: 1;
+	}
+}
+@keyframes pulse {
+	0% {
+		opacity: 1;
+	}
+	50% {
+		opacity: 0.8;
+	}
+	100% {
+		opacity: 1;
+	}
+}
+
+/* rotate ------------------------------------------------
+@-webkit-keyframes rotate {
+	0% {
+		-webkit-transform: rotate(0deg);
+	}
+	100% {
+		-webkit-transform: rotate(360deg);
+	}
+}
+@-moz-keyframes rotate {
+	0% {
+		-moz-transform: rotate(0deg);
+	}
+	100% {
+		-moz-transform: rotate(360deg);
+	}
+}
+@keyframes rotate {
+	0% {
+		transform: rotate(0deg);
+	}
+	100% {
+		transform: rotate(360deg);
+	}
+} */

+ 256 - 0
assets/css/modules/content.css

@@ -0,0 +1,256 @@
+/**
+ * @name        content.css
+ * @author      Tobias Reich
+ * @copyright   2014 by Tobias Reich
+ */
+
+#content::before {
+	content: "";
+	position: absolute;
+	left: 0px;
+	width: 100%;
+	height: 20px;
+	background-image: -webkit-linear-gradient(top, #262626, #222);
+	background-image: -moz-linear-gradient(top, #262626, #222);
+	background-image: -ms-linear-gradient(top, #262626, #222);
+	background-image: linear-gradient(top, #262626, #222);
+	border-top: 1px solid #333;
+}
+
+	/* Modes ------------------------------------------------*/
+	#content.view::before {
+		display: none;
+	}
+
+#content {
+	position: absolute;
+	padding: 50px 0px 33px 0px;
+	width: 100%;
+	-webkit-overflow-scrolling: touch;
+}
+
+	/* Photo ------------------------------------------------*/
+	.photo  {
+		float: left;
+		display: inline-block;
+		width: 206px;
+		height: 206px;
+		margin: 30px 0px 0px 30px;
+		cursor: pointer;
+	}
+	.photo img {
+		position: absolute;
+		width: 200px;
+		height: 200px;
+		background-color: #222;
+		border-radius: 2px;
+		border: 2px solid #ccc;
+	}
+	.photo:hover img,
+	.photo.active img {
+		box-shadow: 0px 0px 5px #005ecc;
+	}
+	.photo:active {
+		-webkit-transition-duration: .1s;
+		-webkit-transform: scale(.98);
+		-moz-transition-duration: .1s;
+		-moz-transform: scale(.98);
+		transition-duration: .1s;
+		transform: scale(.98);
+	}
+
+	/* Album ------------------------------------------------*/
+	.album  {
+		float: left;
+		display: inline-block;
+		width: 204px;
+		height: 204px;
+		margin: 30px 0px 0px 30px;
+		cursor: pointer;
+	}
+	.album img:first-child,
+	.album img:nth-child(2) {
+		-webkit-transform: rotate(0deg) translateY(0px) translateX(0px);
+		-moz-transform: rotate(0deg) translateY(0px) translateX(0px);
+		transform: rotate(0deg) translateY(0px) translateX(0px);
+		opacity: 0;
+	}
+	.album:hover img:first-child {
+		-webkit-transform: rotate(-2deg) translateY(10px) translateX(-12px);
+		-moz-transform: rotate(-2deg) translateY(10px) translateX(-12px);
+		transform: rotate(-2deg) translateY(10px) translateX(-12px);
+		opacity: 1;
+	}
+	.album:hover img:nth-child(2) {
+		-webkit-transform: rotate(5deg) translateY(-8px) translateX(12px);
+		-moz-transform: rotate(5deg) translateY(-8px) translateX(12px);
+		transform: rotate(5deg) translateY(-8px) translateX(12px);
+		opacity: 1;
+	}
+	.album img {
+		position: absolute;
+		width: 200px;
+		height: 200px;
+		background-color: #222;
+		border-radius: 2px;
+		border: 2px solid #ccc;
+	}
+	.album:hover img,
+	.album.active img {
+		box-shadow: 0px 0px 5px #005ecc;
+	}
+
+	/* Album/Photo Overlay ------------------------------------------------*/
+	.album .overlay,
+	.photo .overlay {
+		position: absolute;
+		width: 200px;
+		height: 200px;
+		margin: 2px;
+	}
+	.album .overlay {
+		background: -moz-linear-gradient(top, rgba(0,0,0,0) 0%, rgba(0,0,0,0) 20%, rgba(0,0,0,0.9) 100%); /* FF3.6+ */
+		background: -webkit-linear-gradient(top, rgba(0,0,0,0) 0%,rgba(0,0,0,0) 20%,rgba(0,0,0,0.9) 100%); /* Chrome10+,Safari5.1+ */
+		background: -ms-linear-gradient(top, rgba(0,0,0,0) 0%,rgba(0,0,0,0) 20%,rgba(0,0,0,0.9) 100%); /* IE10+ */
+		background: linear-gradient(top, rgba(0,0,0,0) 0%,rgba(0,0,0,0) 20%,rgba(0,0,0,0.9) 100%); /* W3C */
+	}
+	.photo .overlay {
+		background: rgba(0, 0, 0, .6);
+		opacity: 0;
+	}
+	.photo:hover .overlay,
+	.photo.active .overlay {
+		opacity: 1;
+	}
+	.album .overlay h1,
+	.photo .overlay h1 {
+		min-height: 19px;
+		width: 190px;
+		margin: 153px 0px 3px 15px;
+		color: #fff;
+		font-size: 16px;
+		font-weight: bold;
+		overflow: hidden;
+	}
+	.album .overlay a,
+	.photo .overlay a {
+		font-size: 11px;
+		color: #aaa;
+	}
+	.album .overlay a {
+		margin-left: 15px;
+	}
+	.photo .overlay a {
+		margin: 155px 0px 5px 15px;
+	}
+
+	/* Badges ------------------------------------------------*/
+	.album .badge,
+	.photo .badge {
+		position: absolute;
+		margin-top: -1px;
+		margin-left: 12px;
+		padding: 12px 7px 3px 7px;
+		box-shadow: 0px 0px 3px #000;
+		border-radius: 0px 0px 3px 3px;
+		border: 1px solid #fff;
+		border-top: none;
+		color: #fff;
+		font-size: 24px;
+		text-shadow: 0px 1px 0px #000;
+		opacity: .9;
+	}
+	.album .badge.icon-star,
+	.photo .badge.icon-star {
+		padding: 12px 8px 3px 8px;
+	}
+	.album .badge.icon-share,
+	.photo .badge.icon-share {
+		padding: 12px 6px 3px 8px;
+	}
+	.album .badge::after,
+	.photo .badge::after {
+		content: "";
+		position: absolute;
+		margin-top: -12px;
+		margin-left: -26px;
+		width: 38px;
+		height: 5px;
+		background: -moz-linear-gradient(top, rgba(0,0,0,1) 0%, rgba(0,0,0,0) 100%); /* FF3.6+ */
+		background: -webkit-linear-gradient(top, rgba(0,0,0,1) 0%,rgba(0,0,0,0) 100%); /* Chrome10+,Safari5.1+ */
+		background: -ms-linear-gradient(top, rgba(0,0,0,1) 0%,rgba(0,0,0,0) 100%); /* IE10+ */
+		background: linear-gradient(top, rgba(0,0,0,1) 0%,rgba(0,0,0,0) 100%); /* W3C */
+		opacity: .4;
+	}
+	.album .badge.icon-star::after,
+	.photo .badge.icon-star::after {
+		margin-left: -29px;
+	}
+	.album .badge.icon-share::after,
+	.photo .badge.icon-share::after {
+		margin-left: -31px;
+	}
+	.album .badge.icon-reorder::after {
+		margin-left: -30px;
+	}
+	.album .badge:nth-child(2n),
+	.photo .badge:nth-child(2n) {
+		margin-left: 57px;
+	}
+	.album .badge.red,
+	.photo .badge.red {
+		background: #d64b4b;
+		background: -webkit-linear-gradient(top, #d64b4b, #ab2c2c);
+		background: -moz-linear-gradient(top, #d64b4b, #ab2c2c);
+		background: -ms-linear-gradient(top, #d64b4b, #ab2c2c);
+	}
+	.album .badge.blue,
+	.photo .badge.blue {
+		background: #d64b4b;
+		background: -webkit-linear-gradient(top, #347cd6, #2945ab);
+		background: -moz-linear-gradient(top, #347cd6, #2945ab);
+		background: -ms-linear-gradient(top, #347cd6, #2945ab);
+	}
+
+	/* Divider ------------------------------------------------*/
+	.divider {
+		float: left;
+		width: 100%;
+		margin-top: 50px;
+		opacity: 0;
+		border-top: 1px solid #2E2E2E;
+		box-shadow: 0px -1px 0px #151515;
+	}
+	.divider:first-child {
+		margin-top: 0px;
+		border-top: none;
+	}
+	.divider h1 {
+		float: left;
+		margin: 20px 0px 0px 30px;
+		color: #fff;
+		font-size: 14px;
+		font-weight: bold;
+		text-shadow: 0px -1px 0px #000;
+	}
+
+	/* No Content ------------------------------------------------*/
+	.no_content {
+		position: absolute;
+		top: 50%;
+		left: 50%;
+		height: 160px;
+		width: 180px;
+		margin-top: -80px;
+		margin-left: -90px;
+		padding-top: 20px;
+		color: rgba(20, 20, 20, 1);
+		text-shadow: 0px 1px 0px rgba(255, 255, 255, .05);
+		text-align: center;
+	}
+	.no_content .icon {
+		font-size: 120px;
+	}
+	.no_content p {
+		font-size: 18px;
+	}

+ 101 - 0
assets/css/modules/contextmenu.css

@@ -0,0 +1,101 @@
+/**
+ * @name        contextmenu.css
+ * @author      Tobias Reich
+ * @copyright   2014 by Tobias Reich
+ */
+
+.contextmenu_bg {
+	position: fixed;
+	height: 100%;
+	width: 100%;
+	z-index: 1000;
+}
+.contextmenu {
+	position: fixed;
+	top: 110%;
+	left: 110%;
+	padding: 5px 0px 6px 0px;
+	background-color: #393939;
+	background-image: -webkit-linear-gradient(top, #444, #2d2d2d);
+	background-image: -moz-linear-gradient(top, #393939, #2d2d2d);
+	background-image: -ms-linear-gradient(top, #393939, #2d2d2d);
+	background-image: linear-gradient(top, #393939, #2d2d2d);
+	border: 1px solid rgba(0,0,0,0.7);
+	border-bottom: 1px solid rgba(0,0,0,.9);
+	border-radius: 5px;
+	box-shadow: 0px 4px 5px rgba(0,0,0,0.3), inset 0px 1px 0px rgba(255,255,255,0.15), inset 1px 0px 0px rgba(255,255,255,0.05), inset -1px 0px 0px rgba(255,255,255,0.05);
+	opacity: .98;
+	z-index: 1001;
+}
+
+	/* Items ------------------------------------------------*/
+	.contextmenu tr {
+		font-size: 14px;
+		color: #eee;
+		text-shadow: 0px -1px 0px rgba(0,0,0,.6);
+		cursor: pointer;
+	}
+	.contextmenu tr:hover {
+		background-color: #6a84f2;
+		background-image: -webkit-linear-gradient(top, #6a84f2, #3959ef);
+		background-image: -moz-linear-gradient(top, #6a84f2, #3959ef);
+		background-image: -ms-linear-gradient(top, #6a84f2, #3959ef);
+		background-image: linear-gradient(top, #6a84f2, #3959ef);
+	}
+	.contextmenu tr.no_hover:hover {
+		cursor: inherit;
+		background-color: inherit;
+		background-image: none;
+	}
+	.contextmenu tr.separator {
+		float: left;
+		height: 1px;
+		width: 100%;
+		background-color: #1c1c1c;
+		border-bottom: 1px solid #4a4a4a;
+		margin: 5px 0px;
+		cursor: inherit;
+	}
+	.contextmenu tr.separator:hover {
+		background-color: #222;
+		background-image: none;
+	}
+	.contextmenu tr td {
+		padding: 7px 30px 6px 12px;
+		white-space: nowrap;
+		-webkit-transition: none;
+		-moz-transition: none;
+		transition: none;
+	}
+	.contextmenu tr:hover td {
+		color: #fff;
+		box-shadow: inset 0px 1px 0px rgba(255,255,255,.05);
+		text-shadow: 0px -1px 0px rgba(0,0,0,.4);
+	}
+	.contextmenu tr.no_hover:hover td {
+		box-shadow: none;
+	}
+	.contextmenu tr a {
+		float: left;
+		width: 10px;
+		margin-right: 10px;
+		text-align: center;
+	}
+
+	/* Direct Link Input ------------------------------------------------*/
+	.contextmenu #link {
+		float: right;
+		width: 140px;
+		margin: 0px -17px -1px 0px;
+		padding: 4px 6px 5px 6px;
+		background-color: #444;
+		color: #fff;
+		border: none;
+		border: 1px solid #111;
+		box-shadow: 0px 1px 0px rgba(255,255,255,.1);
+		outline: none;
+		border-radius: 5px;
+	}
+	.contextmenu tr a#link_icon {
+		padding-top: 4px;
+	}

+ 3 - 3
css/font-awesome.css

@@ -23,8 +23,8 @@
     */
 @font-face {
   font-family: 'FontAwesome';
-  src: url('font/fontawesome-webfont.eot');
-  src: url('font/fontawesome-webfont.eot?#iefix') format('eot'), url('font/fontawesome-webfont.woff') format('woff'), url('font/fontawesome-webfont.ttf') format('truetype'), url('font/fontawesome-webfont.svg#FontAwesome') format('svg');
+  src: url('../../font/fontawesome-webfont.eot');
+  src: url('../../font/fontawesome-webfont.eot?#iefix') format('eot'), url('../../font/fontawesome-webfont.woff') format('woff'), url('../../font/fontawesome-webfont.ttf') format('truetype'), url('../../font/fontawesome-webfont.svg#FontAwesome') format('svg');
   font-weight: normal;
   font-style: normal;
 }
@@ -300,4 +300,4 @@ li[class^="icon-"].icon-large:before, li[class*=" icon-"].icon-large:before {
 .icon-umbrella:before             { content: "\f0e9"; }
 .icon-paste:before                { content: "\f0ea"; }
 
-.icon-user-md:before              { content: "\f200"; }
+.icon-user-md:before              { content: "\f200"; }

+ 165 - 0
assets/css/modules/header.css

@@ -0,0 +1,165 @@
+/**
+ * @name        _header.css
+ * @author      Tobias Reich
+ * @copyright   2014 by Tobias Reich
+ */
+
+header {
+	position: fixed;
+	height: 49px;
+	width: 100%;
+	background-image: -webkit-linear-gradient(top, #3E3E3E, #282828);
+	background-image: -moz-linear-gradient(top, #3E3E3E, #282828);
+	background-image: -ms-linear-gradient(top, #3E3E3E, #282828);
+	background-image: linear-gradient(top, #3E3E3E, #282828);
+	border-bottom: 1px solid #161616;
+	z-index: 1;
+
+	-webkit-transition: -webkit-transform .3s ease-out;
+	-moz-transition: -moz-transform .3s ease-out;
+	transition: transform .3s ease-out;
+}
+
+	/* Modes ------------------------------------------------*/
+	header.hidden {
+		-webkit-transform: translateY(-60px);
+		-moz-transform: translateY(-60px);
+		transform: translateY(-60px);
+	}
+	header.loading {
+		-webkit-transform: translateY(2px);
+		-moz-transform: translateY(2px);
+		transform: translateY(2px);
+	}
+	header.error {
+		-webkit-transform: translateY(40px);
+		-moz-transform: translateY(40px);
+		transform: translateY(40px);
+	}
+		header.view.error {
+			background-color: rgba(10,10,10,.99);
+		}
+	header.view {
+		background-image: none;
+		border-bottom: none;
+	}
+		header.view .button,
+		header.view #title,
+		header.view .tools {
+			text-shadow: none !important;
+		}
+
+	/* Title ------------------------------------------------*/
+	header #title {
+		position: absolute;
+		margin: 0px 30%;
+		width: 40%;
+		padding: 15px 0px;
+		color: #fff;
+		font-size: 16px;
+		font-weight: bold;
+		text-align: center;
+		text-shadow: 0px -1px 0px #222;
+	}
+	header #title.editable {
+		cursor: pointer;
+	}
+
+	/* Button ------------------------------------------------*/
+	header .button {
+		color: #888;
+		font-family: 'FontAwesome';
+		font-size: 21px;
+		font-weight: bold;
+		text-decoration: none !important;
+		cursor: pointer;
+		text-shadow: 0px -1px 0px #222;
+	}
+	header .button.left {
+		float: left;
+		position: absolute;
+		padding: 16px 10px 8px 18px;
+	}
+	header .button.right {
+		float: right;
+		position: relative;
+		padding: 16px 19px 13px 11px;
+	}
+	header .button:hover {
+		color: #fff;
+	}
+	header #tools_albums,
+	header #tools_album,
+	header #tools_photo,
+	header #button_signin {
+		display: none;
+	}
+
+	/* Button Divider ------------------------------------------------*/
+	header .button_divider {
+		float: right;
+		position: relative;
+		width: 14px;
+		height: 50px;
+	}
+
+	/* Search ------------------------------------------------*/
+	header #search {
+		float: right;
+		width: 80px;
+		margin: 12px 12px 0px 0px;
+		padding: 5px 12px 6px 12px;
+		background-color: #383838;
+		color: #fff;
+		border: none;
+		border: 1px solid #131313;
+		box-shadow: 0px 1px 0px rgba(255,255,255,.1);
+		outline: none;
+		border-radius: 50px;
+		opacity: .6;
+
+		-webkit-transition: opacity .3s ease-out, -webkit-transform .3s ease-out, box-shadow .3s, width .2s ease-out;
+		-moz-transition: opacity .3s ease-out, -moz-transform .3s ease-out, box-shadow .3s, width .2s ease-out;
+		transition: opacity .3s ease-out, transform .3s ease-out, box-shadow .3s, width .2s ease-out;
+	}
+	header #search:focus {
+		width: 140px;
+	}
+
+	/* Tools ------------------------------------------------*/
+	header .tools:first-of-type {
+		margin-right: 6px;
+	}
+	header .tools {
+		float: right;
+		padding: 14px 8px;
+		color: #888;
+		font-size: 21px;
+		text-shadow: 0px -1px 0px #222;
+		cursor: pointer;
+	}
+	header .tools:hover a {
+		color: #fff;
+	}
+	header .tools .icon-star {
+		color: #f0ef77;
+	}
+	header .tools .icon-share.active {
+		color: #ff9737;
+	}
+
+	/* Hosted with Lychee ------------------------------------------------*/
+	header #hostedwith {
+		float: right;
+		padding: 5px 10px;
+		margin: 13px 9px;
+		color: #888;
+		font-size: 13px;
+		text-shadow: 0px -1px 0px #222;
+		display: none;
+		cursor: pointer;
+	}
+	header #hostedwith:hover {
+		background-color: rgba(0, 0, 0, .2);
+		border-radius: 100px;
+	}

+ 88 - 0
assets/css/modules/imageview.css

@@ -0,0 +1,88 @@
+/**
+ * @name        imageview.css
+ * @author      Tobias Reich
+ * @copyright   2014 by Tobias Reich
+ */
+
+#imageview {
+	position: fixed;
+	display: none;
+	width: 100%;
+	min-height: 100%;
+	background-color: rgba(10,10,10,.99);
+
+	-webkit-transition: background-color .3s;
+}
+
+	/* Modes ------------------------------------------------*/
+	#imageview.view {
+		background-color: inherit;
+	}
+	#imageview.full {
+		background-color: #040404;
+	}
+
+	/* ImageView ------------------------------------------------*/
+	#imageview #image {
+		position: absolute;
+		top: 60px;
+		right: 30px;
+		bottom: 30px;
+		left: 30px;
+		background-repeat: no-repeat;
+		background-position: 50% 50%;
+		background-size: contain;
+
+		-webkit-transition: top .3s, bottom .3s, margin-top .3s;
+
+		-webkit-animation-name: zoomIn;
+		-webkit-animation-duration: .3s;
+		-moz-animation-name: zoomIn;
+		-moz-animation-duration: .3s;
+		animation-name: zoomIn;
+		animation-duration: .3s;
+	}
+	#imageview #image.small {
+		top: 50%;
+		right: auto;
+		bottom: auto;
+		left: 50%;
+	}
+
+	/* Previous/Next Buttons ------------------------------------------------*/
+	#imageview .arrow_wrapper {
+		position: fixed;
+		width: 20%;
+		height: calc(100% - 60px);
+		top: 60px;
+		z-index: 1;
+	}
+	#imageview .arrow_wrapper.previous {
+		left: 0;
+	}
+	#imageview .arrow_wrapper.next {
+		right: 0;
+	}
+	#imageview .arrow_wrapper a {
+		position: fixed;
+		top:  50%;
+		margin-top: -10px;
+		color: #fff;
+		font-size: 50px;
+		text-shadow: 0px 1px 2px #000;
+		cursor: pointer;
+		opacity: 0;
+		z-index: 2;
+		-webkit-transition: opacity .2s;
+		-moz-transition: opacity .2s;
+		transition: opacity .2s;
+	}
+	#imageview .arrow_wrapper:hover a {
+		opacity: .2;
+	}
+	#imageview .arrow_wrapper a#previous {
+		left: 20px;
+	}
+	#imageview .arrow_wrapper a#next {
+		right: 20px;
+	}

+ 173 - 0
assets/css/modules/infobox.css

@@ -0,0 +1,173 @@
+/**
+ * @name        infobox.css
+ * @author      Tobias Reich
+ * @copyright   2014 by Tobias Reich
+ */
+
+#infobox_overlay {
+	z-index: 3;
+	position: fixed;
+	width: 100%;
+	height: 100%;
+	top: 0px;
+	left: 0px;
+	background-color: rgba(0,0,0,.85);
+}
+#infobox {
+	z-index: 4;
+	position: fixed;
+	right: 0px;
+	width: 300px;
+	height: 100%;
+	background-color: rgba(20,20,20,0.98);
+	box-shadow: -1px 0px 2px rgba(0,0,0,.8);
+	display: none;
+
+	-webkit-transform: translateX(320px);
+	-moz-transform: translateX(320px);
+	transform: translateX(320px);
+
+	-webkit-user-select: text;
+	-moz-user-select: text;
+	user-select: text;
+
+	-webkit-transition: -webkit-transform .5s cubic-bezier(.225,.5,.165,1);
+	-moz-transition: -moz-transform .5s cubic-bezier(.225,.5,.165,1);
+	transition: transform .5s cubic-bezier(.225,.5,.165,1);
+}
+#infobox.active {
+	-webkit-transform: translateX(0px);
+	-moz-transform: translateX(0px);
+	transform: translateX(0px);
+}
+
+	/* Misc ------------------------------------------------*/
+	#infobox .wrapper {
+		float: left;
+		height: 100%;
+		overflow: scroll;
+	}
+	#infobox .edit {
+		display: inline;
+		margin-left: 3px;
+		width: 20px;
+		height: 5px;
+		cursor: pointer;
+	}
+	#infobox .bumper {
+		float: left;
+		width: 100%;
+		height: 50px;
+	}
+
+	/* Header ------------------------------------------------*/
+	#infobox .header {
+		float: left;
+		height: 49px;
+		width: 100%;
+		background-color: #1d1d1d;
+		background-image: -webkit-linear-gradient(top, #2A2A2A, #131313);
+		background-image: -moz-linear-gradient(top, #2A2A2A, #131313);
+		background-image: -ms-linear-gradient(top, #2A2A2A, #131313);
+		background-image: linear-gradient(top, #2A2A2A, #131313);
+		border-bottom: 1px solid #000;
+	}
+	#infobox .header h1 {
+		position: absolute;
+		margin: 15px 30%;
+		width: 40%;
+		color: #fff;
+		font-size: 16px;
+		font-weight: bold;
+		text-align: center;
+		text-shadow: 0px -1px 0px #000;
+	}
+	#infobox .header a {
+		float: right;
+		padding: 15px 15px;
+		color: #fff;
+		font-size: 20px;
+		font-weight: bold;
+		text-shadow: 0px -1px 0px #000;
+		opacity: .5;
+		cursor: pointer;
+	}
+	#infobox .header a:hover {
+		opacity: 1;
+	}
+
+	/* Seperator ------------------------------------------------*/
+	#infobox .separator {
+		float: left;
+		width: 100%;
+		border-top: 1px solid rgba(255,255,255,.04);
+		box-shadow: 0px -1px 0px #000;
+	}
+	#infobox .separator h1 {
+		margin: 20px 0px 5px 20px;
+		color: #fff;
+		font-size: 14px;
+		font-weight: bold;
+		text-shadow: 0px -1px 0px #000;
+	}
+
+	/* Table ------------------------------------------------*/
+	#infobox table {
+		float: left;
+		margin: 10px 0px 15px 20px;
+	}
+	#infobox table tr td {
+		padding: 5px 0px;
+		color: #fff;
+		font-size: 14px;
+		line-height: 19px;
+	}
+	#infobox table tr td:first-child {
+		width: 110px;
+	}
+	#infobox table tr td:last-child {
+		padding-right: 10px;
+	}
+
+	/* Tags ------------------------------------------------*/
+	#infobox #tags {
+		margin: 20px 20px 15px 20px;
+		color: #fff;
+		display: inline-block;
+	}
+	#infobox .tag {
+		float: left;
+		padding: 4px 7px;
+		margin: 0px 6px 8px 0px;
+		background-color: rgba(0,0,0,.5);
+		border: 2px solid rgba(255,255,255,.3);
+		border-radius: 100px;
+		font-size: 12px;
+		-webkit-transition: border .3s;
+		-moz-transition: border .3s;
+		transition: border .3s;
+	}
+	#infobox .tag:hover {
+		border: 2px solid #aaa;
+	}
+	#infobox .tag span {
+		float: right;
+		width: 0px;
+		padding: 0px;
+		margin: 0px 0px -2px 0px;
+		color: red;
+		font-size: 12px;
+		cursor: pointer;
+		overflow: hidden;
+		-webkit-transform: scale(0);
+		transform: scale(0);
+		-webkit-transition: width .3s, margin .3s, -webkit-transform .3s;
+		-moz-transition: width .3s, margin .3s;
+		transition: width .3s, margin .3s, transform .3s;
+	}
+	#infobox .tag:hover span {
+		width: 10px;
+		margin: 0px 0px -2px 6px;
+		-webkit-transform: scale(1);
+		transform: scale(1);
+	}

+ 59 - 0
assets/css/modules/loading.css

@@ -0,0 +1,59 @@
+/**
+ * @name        loading.css
+ * @author      Tobias Reich
+ * @copyright   2014 by Tobias Reich
+ */
+
+#loading {
+	position: fixed;
+	width: 100%;
+	height: 3px;
+	background-size: 100px 3px;
+	background-repeat: repeat-x;
+	border-bottom: 1px solid rgba(0,0,0,.3);
+	display: none;
+
+	/* Animation */
+	-webkit-animation-name: moveBackground;
+	-webkit-animation-duration: .3s;
+	-webkit-animation-iteration-count: infinite;
+	-webkit-animation-timing-function: linear;
+	-moz-animation-name: moveBackground;
+	-moz-animation-duration: .3s;
+	-moz-animation-iteration-count: infinite;
+	-moz-animation-timing-function: linear;
+	animation-name: moveBackground;
+	animation-duration: .3s;
+	animation-iteration-count: infinite;
+	animation-timing-function: linear;
+}
+
+	/* Modes ------------------------------------------------*/
+	#loading.loading {
+		background-image: -webkit-linear-gradient(left, #153674 0%, #153674 47%, #2651AE 53%, #2651AE 100%);
+		background-image: -moz-linear-gradient(left, #153674 0%, #153674 47%, #2651AE 53%, #2651AE 100%);
+		background-image: linear-gradient(left right, #153674 0%, #153674 47%, #2651AE 53%, #2651AE 100%);
+		z-index: 2;
+	}
+	#loading.error {
+		background-color: #2f0d0e;
+		background-image: -webkit-linear-gradient(left, #451317 0%, #451317 47%, #AA3039 53%, #AA3039 100%);
+		background-image: -moz-linear-gradient(left, #451317 0%, #451317 47%, #AA3039 53%, #AA3039 100%);
+		background-image: linear-gradient(left right, #451317 0%, #451317 47%, #AA3039 53%, #AA3039 100%);
+		z-index: 1;
+	}
+
+	/* Content ------------------------------------------------*/
+	#loading h1 {
+		margin: 13px;
+		color: #ddd;
+		font-size: 14px;
+		font-weight: bold;
+		text-shadow: 0px 1px 0px #000;
+		text-transform: capitalize;
+	}
+	#loading h1 span {
+		margin-left: 10px;
+		font-weight: normal;
+		text-transform: none;
+	}

+ 61 - 0
assets/css/modules/mediaquery.css

@@ -0,0 +1,61 @@
+/**
+ * @name        mediaquery.css
+ * @author      Tobias Reich
+ * @copyright   2014 by Tobias Reich
+ */
+
+@media only screen and (max-width: 900px) {
+
+	#title {
+		margin: 0px 20% !important;
+		width: 40% !important;
+	}
+	#title.view {
+		margin: 0px 20% !important;
+		width: 60% !important;
+	}
+	#title span {
+		display: none !important;
+	}
+
+}
+@media only screen and (max-width: 640px) {
+
+	#title {
+		display: none !important;
+	}
+	#title.view {
+		display: block !important;
+		width: 70% !important;
+		margin: 0px 20% 0px 10% !important;
+	}
+	#button_move {
+		display: none !important;
+	}
+	#button_archive {
+		display: none !important;
+	}
+
+	.center {
+		top: 0px !important;
+		left: 0px !important;
+	}
+
+	.album, .photo {
+		margin: 40px 0px 0px 50px !important;
+	}
+
+	.message {
+		position: fixed !important;
+		width: 100% !important;
+		height: 100% !important;
+		margin: 1px 0px 0px 0px !important;
+		border-radius: 0px !important;
+
+		/* Animation */
+		-webkit-animation: moveUp .3s !important;
+		-moz-animation: moveUp .3s !important;
+		animation: moveUp .3s !important;
+	}
+
+}

+ 198 - 0
assets/css/modules/message.css

@@ -0,0 +1,198 @@
+/**
+ * @name        message.css
+ * @author      Tobias Reich
+ * @copyright   2014 by Tobias Reich
+ */
+
+.message_overlay {
+	position: fixed;
+	width: 100%;
+	height: 100%;
+	top: 0px;
+	left: 0px;
+	background-color: rgba(0,0,0,.85);
+	z-index: 1000;
+}
+.message {
+	position: absolute;
+	display: inline-block;
+	width: 500px;
+	margin-left: -250px;
+	margin-top: -95px;
+	background-color: #444;
+	background-image: -webkit-linear-gradient(top, rgb(75, 75, 75), rgb(45, 45, 45));
+	background-image: -moz-linear-gradient(top, rgb(75, 75, 75), rgb(45, 45, 45));
+	background-image: -ms-linear-gradient(top, rgb(75, 75, 75), rgb(45, 45, 45));
+	background-image: linear-gradient(top, rgb(75, 75, 75), rgb(45, 45, 45));
+	border-radius: 5px;
+	box-shadow: 0px 0px 5px #000, inset 0px 1px 0px rgba(255,255,255,.08), inset 1px 0px 0px rgba(255,255,255,.03), inset -1px 0px 0px rgba(255,255,255,.03);
+
+	/* Animation */
+	-webkit-animation-name: moveUp;
+	-webkit-animation-duration: .3s;
+	-webkit-animation-timing-function: ease-out;
+	-moz-animation-name: moveUp;
+	-moz-animation-duration: .3s;
+	-moz-animation-timing-function: ease-out;
+	animation-name: moveUp;
+	animation-duration: .3s;
+	animation-timing-function: ease-out;
+}
+
+	/* Header ------------------------------------------------*/
+	.message h1 {
+		float: left;
+		width: 100%;
+		padding: 12px 0px;
+		color: #fff;
+		font-size: 16px;
+		font-weight: bold;
+		text-shadow: 0px -1px 0px #222;
+		text-align: center;
+	}
+	.message .close {
+		position: absolute;
+		top: 0px;
+		right: 0px;
+		padding: 12px 14px 6px 7px;
+		color: #aaa;
+		font-size: 20px;
+		text-shadow: 0px -1px 0px #222;
+		cursor: pointer;
+	}
+	.message .close:hover {
+		color: #fff;
+	}
+
+	/* Text ------------------------------------------------*/
+	.message p {
+		float: left;
+		width: 90%;
+		margin-top: 1px;
+		padding: 12px 5% 15px 5%;
+		color: #eee;
+		font-size: 14px;
+		text-shadow: 0px -1px 0px #222;
+		line-height: 20px;
+	}
+	.message p b {
+		font-weight: bold;
+	}
+	.message p a {
+		color: #eee;
+		text-decoration: none;
+		border-bottom: 1px dashed #888;
+	}
+
+	/* Button ------------------------------------------------*/
+	.message .button {
+		float: right;
+		margin: 15px 15px 15px 0px;
+		padding: 6px 10px 8px 10px;
+		background-color: #4e4e4e;
+		background-image: -webkit-linear-gradient(top, rgb(60, 60, 60), rgb(45, 45, 45));
+		background-image: -moz-linear-gradient(top, rgb(60, 60, 60), rgb(45, 45, 45));
+		background-image: -ms-linear-gradient(top, rgb(60, 60, 60), rgb(45, 45, 45));
+		background-image: linear-gradient(top, rgb(60, 60, 60), rgb(45, 45, 45));
+		color: #ccc;
+		font-size: 14px;
+		font-weight: bold;
+		text-align: center;
+		text-shadow: 0px -1px 0px #222;
+		border-radius: 5px;
+		border: 1px solid #191919;
+		box-shadow: inset 0px 1px 0px rgba(255,255,255,.1), 0px 1px 0px rgba(255,255,255,.1);
+		cursor: pointer;
+	}
+	.message .button:first-of-type {
+		margin: 15px 5% 18px 0px !important;
+	}
+	.message .button.active {
+		color: #fff;
+		box-shadow: inset 0px 1px 0px rgba(255,255,255,.1), 0px 1px 0px rgba(255,255,255,.1), 0px 0px 4px #005ecc;
+	}
+	.message .button:hover {
+		background-color: #565757;
+		background-image: -webkit-linear-gradient(top, rgb(80, 80, 80), rgb(57, 57, 57));
+		background-image: -moz-linear-gradient(top, rgb(80, 80, 80), rgb(57, 57, 57));
+		background-image: -ms-linear-gradient(top, rgb(80, 80, 80), rgb(57, 57, 57));
+		background-image: linear-gradient(top, rgb(80, 80, 80), rgb(57, 57, 57));
+	}
+	.message .button:active,
+	.message .button.pressed {
+		background-color: #393939;
+		background-image: -webkit-linear-gradient(top, rgb(57, 57, 57), rgb(70, 70, 70));
+		background-image: -moz-linear-gradient(top, rgb(57, 57, 57), rgb(70, 70, 70));
+		background-image: -ms-linear-gradient(top, rgb(57, 57, 57), rgb(70, 70, 70));
+		background-image: linear-gradient(top, rgb(57, 57, 57), rgb(70, 70, 70));
+	}
+
+	/* Sign in ------------------------------------------------*/
+	.sign_in {
+		float: left;
+		width: 100%;
+		margin-top: 1px;
+		padding: 5px 0px;
+		color: #eee;
+		font-size: 14px;
+		text-shadow: 0px -1px 0px #222;
+		line-height: 20px;
+	}
+	.sign_in input {
+		float: left;
+		width: 88%;
+		padding: 7px 1% 9px 1%;
+		margin: 0px 5%;
+		background-color: transparent;
+		color: #fff;
+		text-shadow: 0px -1px 0px #222;
+		border: none;
+		border-bottom: 1px solid #222;
+		box-shadow: 0px 1px 0px rgba(255,255,255,.1);
+		border-radius: 0px;
+		outline: none;
+	}
+	.sign_in input:first-of-type {
+		margin-bottom: 10px;
+	}
+	.sign_in input.error:focus {
+		box-shadow: 0px 1px 0px rgba(204, 0, 7, 0.6);
+	}
+	.message #version {
+		display: inline-block;
+		margin-top: 23px;
+		margin-left: 5%;
+		color: #888;
+		text-shadow: 0px -1px 0px #111;
+	}
+	.message #version span {
+		display: none;
+	}
+	.message #version span a {
+		color: #888;
+	}
+
+	/* Input Misc ------------------------------------------------*/
+	.message input.text {
+		float: left;
+		width: calc(100% - 10px);
+		padding: 17px 5px 9px 5px;
+		margin-top: 10px;
+		background-color: transparent;
+		color: #fff;
+		text-shadow: 0px -1px 0px #222;
+		border: none;
+		box-shadow: 0px 1px 0px rgba(255,255,255,.1);
+		border-bottom: 1px solid #222;
+		border-radius: 0px;
+		outline: none;
+	}
+	.message input.less {
+		margin-bottom: -10px;
+	}
+	.message input.more {
+		margin-bottom: 30px;
+	}
+	.message .copylink {
+		margin-bottom: 20px;
+	}

+ 35 - 0
assets/css/modules/misc.css

@@ -0,0 +1,35 @@
+/**
+ * @name        misc.css
+ * @author      Tobias Reich
+ * @copyright   2014 by Tobias Reich
+ */
+
+html,
+body {
+	min-height: 100%;
+	-webkit-user-select: none;
+	-moz-user-select: none;
+	user-select: none;
+}
+body {
+	background-color: #222;
+	font-family:'Helvetica Neue', Helvetica, Arial, sans-serif;
+	font-size: 12px;
+	-webkit-font-smoothing: antialiased;
+	-moz-font-smoothing: antialiased;
+	-moz-osx-font-smoothing: grayscale;
+	font-smoothing: antialiased;
+}
+body.view {
+	background-color: #0f0f0f;
+}
+.center {
+	position: absolute;
+	left: 50%;
+	top:50%;
+}
+* {
+	-webkit-transition: color .3s, opacity .3s ease-out, -webkit-transform .3s ease-out, box-shadow .3s;
+	-moz-transition: opacity .3s ease-out, -moz-transform .3s ease-out, box-shadow .3s;
+	transition: color .3s, opacity .3s ease-out, transform .3s ease-out, box-shadow .3s;
+}

+ 39 - 0
assets/css/modules/tooltip.css

@@ -0,0 +1,39 @@
+/**
+ * @name        tooltip.css
+ */
+
+.tipsy {
+	padding: 4px;
+	font-size: 12px;
+	position: absolute;
+	z-index: 100000;
+
+	/* Animation */
+	-webkit-animation-name: fadeIn;
+	-webkit-animation-duration: .3s;
+	-moz-animation-name: fadeIn;
+	-moz-animation-duration: .3s;
+	animation-name: fadeIn;
+	animation-duration: .3s;
+}
+.tipsy-inner {
+	padding: 8px 10px 7px;
+	color: #fff;
+	max-width: 200px;
+	text-align: center;
+	background: rgba(0, 0, 0, .8);
+	border-radius: 25px;
+}
+.tipsy-arrow { position: absolute; width: 0; height: 0; line-height: 0; border: 5px dashed rgba(0, 0, 0, .8); }
+.tipsy-arrow-n { border-bottom-color: rgba(0, 0, 0, .8); }
+.tipsy-arrow-s { border-top-color: rgba(0, 0, 0, .8); }
+.tipsy-arrow-e { border-left-color: rgba(0, 0, 0, .8); }
+.tipsy-arrow-w { border-right-color: rgba(0, 0, 0, .8); }
+.tipsy-n .tipsy-arrow { top: 0px; left: 50%; margin-left: -5px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent; }
+.tipsy-nw .tipsy-arrow { top: 0; left: 10px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent;}
+.tipsy-ne .tipsy-arrow { top: 0; right: 10px; border-bottom-style: solid; border-top: none;  border-left-color: transparent; border-right-color: transparent;}
+.tipsy-s .tipsy-arrow { bottom: 0; left: 50%; margin-left: -5px; border-top-style: solid; border-bottom: none;  border-left-color: transparent; border-right-color: transparent; }
+.tipsy-sw .tipsy-arrow { bottom: 0; left: 10px; border-top-style: solid; border-bottom: none;  border-left-color: transparent; border-right-color: transparent; }
+.tipsy-se .tipsy-arrow { bottom: 0; right: 10px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
+.tipsy-e .tipsy-arrow { right: 0; top: 50%; margin-top: -5px; border-left-style: solid; border-right: none; border-top-color: transparent; border-bottom-color: transparent; }
+.tipsy-w .tipsy-arrow { left: 0; top: 50%; margin-top: -5px; border-right-style: solid; border-left: none; border-top-color: transparent; border-bottom-color: transparent; }

+ 123 - 0
assets/css/modules/upload.css

@@ -0,0 +1,123 @@
+/**
+ * @name        upload.css
+ * @author      Tobias Reich
+ * @copyright   2014 by Tobias Reich
+ */
+
+#upload {
+	display: none;
+}
+.upload_overlay {
+	position: fixed;
+	width: 100%;
+	height: 100%;
+	top: 0px;
+	left: 0px;
+	background-color: rgba(0,0,0,.85);
+	z-index: 1000;
+}
+.upload_message {
+	position: absolute;
+	display: inline-block;
+	width: 200px;
+	margin-left: -100px;
+	margin-top: -85px;
+	background-color: #444;
+	background-image: -webkit-linear-gradient(top, rgb(75, 75, 75), rgb(45, 45, 45));
+	background-image: -moz-linear-gradient(top, rgb(75, 75, 75), rgb(45, 45, 45));
+	background-image: -ms-linear-gradient(top, rgb(75, 75, 75), rgb(45, 45, 45));
+	background-image: linear-gradient(top, rgb(75, 75, 75), rgb(45, 45, 45));
+	border-radius: 5px;
+	box-shadow: 0px 0px 5px #000, inset 0px 1px 0px rgba(255,255,255,.08), inset 1px 0px 0px rgba(255,255,255,.03), inset -1px 0px 0px rgba(255,255,255,.03);
+
+	/* Animation */
+	-webkit-animation-name: moveUp;
+	-webkit-animation-duration: .3s;
+	-webkit-animation-timing-function: ease-out;
+	-moz-animation-name: moveUp;
+	-moz-animation-duration: .3s;
+	-moz-animation-timing-function: ease-out;
+	animation-name: moveUp;
+	animation-duration: .3s;
+	animation-timing-function: ease-out;
+}
+
+	/* Icon ------------------------------------------------*/
+	.upload_message a {
+		float: left;
+		width: 100%;
+		margin: 35px 0px 5px 0px;
+		color: #fff;
+		font-size: 70px;
+		text-shadow: 0px 1px 2px rgba(0,0,0,.5);
+		text-align: center;
+
+		-webkit-animation-name: pulse;
+		-webkit-animation-duration: 2s;
+		-webkit-animation-timing-function: ease-in-out;
+		-webkit-animation-iteration-count: infinite;
+		-moz-animation-name: pulse;
+		-moz-animation-duration: 2s;
+		-moz-animation-timing-function: ease-in-out;
+		-moz-animation-iteration-count: infinite;
+		animation-name: pulse;
+		animation-duration: 2s;
+		animation-timing-function: ease-in-out;
+		animation-iteration-count: infinite;
+	}
+
+	/* Text ------------------------------------------------*/
+	.upload_message p {
+		float: left;
+		width: 100%;
+		margin: 10px 0px 35px 0px;
+		color: #fff;
+		font-size: 14px;
+		text-align: center;
+		text-shadow: 0px -1px 0px rgba(0,0,0,.5);
+	}
+
+	/* Progress ------------------------------------------------*/
+	.upload_message .progressbar {
+		float: left;
+		width: 170px;
+		height: 25px;
+		margin: 15px;
+		background-size: 50px 25px;
+		background-repeat: repeat-x;
+		background-image: -webkit-linear-gradient(left, #191919 0%, #191919 47%, #1D1D1D 53%, #1D1D1D 100%);
+		background-image: -moz-linear-gradient(left, #191919 0%, #191919 47%, #1D1D1D 53%, #1D1D1D 100%);
+		background-image: linear-gradient(left right, #191919 0%, #191919 47%, #1D1D1D 53%, #1D1D1D 100%);
+		border: 1px solid #090909;
+		box-shadow: 0 1px 0 rgba(255,255,255,.06), inset 0px 0px 2px #222;
+		border-radius: 50px;
+
+		/* Animation */
+		-webkit-animation-name: moveBackground;
+		-webkit-animation-duration: 1s;
+		-webkit-animation-timing-function: linear;
+		-webkit-animation-iteration-count: infinite;
+		-moz-animation-name: moveBackground;
+		-moz-animation-duration: 1s;
+		-moz-animation-timing-function: linear;
+		-moz-animation-iteration-count: infinite;
+		animation-name: moveBackground;
+		animation-duration: 1s;
+		animation-timing-function: linear;
+		animation-iteration-count: infinite;
+	}
+	.upload_message .progressbar div {
+		float: left;
+		width: 0%;
+		height: 100%;
+		box-shadow: 0 1px 0 #000, 1px 0px 2px #000;
+		background-color: #f5f2f7;
+		background-image: -webkit-linear-gradient(top, #f5f2f7, #c7c6c8);
+		background-image: -moz-linear-gradient(top, #f5f2f7, #c7c6c8);
+		background-image: -ms-linear-gradient(top, #f5f2f7, #c7c6c8);
+		background-image: linear-gradient(top, #f5f2f7, #c7c6c8);
+		border-radius: 50px;
+		-webkit-transition: width .2s, opacity .5;
+		-moz-transition: width .2s, opacity .5;
+		transition: width .2s, opacity .5;
+	}

css/font/fontawesome-webfont.eot → assets/font/fontawesome-webfont.eot


css/font/fontawesome-webfont.svg → assets/font/fontawesome-webfont.svg


css/font/fontawesome-webfont.ttf → assets/font/fontawesome-webfont.ttf


css/font/fontawesome-webfont.woff → assets/font/fontawesome-webfont.woff


BIN
assets/img/apple-touch-icon-ipad.png


BIN
assets/img/apple-touch-icon-iphone.png


BIN
assets/img/favicon.ico


File diff suppressed because it is too large
+ 1 - 0
assets/img/no_images.svg


File diff suppressed because it is too large
+ 1 - 0
assets/img/password.svg


js/frameworks.js → assets/js/min/frameworks.js


File diff suppressed because it is too large
+ 2 - 0
assets/js/min/main.js


+ 117 - 56
js/modules/album.js

@@ -1,11 +1,8 @@
 /**
- * @name        album.js
- * @author      Philipp Maurer
- * @author      Tobias Reich
- * @copyright   2013 by Philipp Maurer, Tobias Reich
- *
- * Album Module
- * Takes care of every action an album can handle and execute.
+ * @name		Album Module
+ * @description	Takes care of every action an album can handle and execute.
+ * @author		Tobias Reich
+ * @copyright	2014 by Tobias Reich
  */
 
 album = {
@@ -33,8 +30,7 @@ album = {
 		var startTime,
 			params,
 			durationTime,
-			waitTime,
-			photosData = "";
+			waitTime;
 
 		password.get(albumID, function() {
 
@@ -47,14 +43,14 @@ album = {
 			startTime = new Date().getTime();
 
 			params = "getAlbum&albumID=" + albumID + "&password=" + password.value;
-			lychee.api(params, "json", function(data) {
+			lychee.api(params, function(data) {
 
-				if (data=="HTTP/1.1 403 Album private!") {
+				if (data==="Warning: Album private!") {
 					lychee.setMode("view");
 					return false;
 				}
 
-				if (data=="HTTP/1.1 403 Wrong password!") {
+				if (data==="Warning: Wrong password!") {
 					album.load(albumID, refresh);
 					return false;
 				}
@@ -66,14 +62,14 @@ album = {
 				if (!visible.albums()&&!visible.photo()&&!visible.album()) waitTime = 0;
 
 				setTimeout(function() {
-				
+
 					view.album.init();
-					
+
 					if (!refresh) {
 						lychee.animate(".album, .photo", "contentZoomIn");
 						view.header.mode("album");
 					}
-				
+
 				}, waitTime);
 
 			});
@@ -91,22 +87,35 @@ album = {
 
 	add: function() {
 
-		var title = prompt("Please enter a title for this album:", "Untitled"),
-			params;
+		var title,
+			params,
+			buttons;
 
-		if (title.length>0&&title.length<31) {
+		buttons = [
+			["Create Album", function() {
 
-			modal.close();
+				title = $(".message input.text").val();
 
-			params = "addAlbum&title=" + escape(encodeURI(title));
-			lychee.api(params, "text", function(data) {
+				if (title==="") title = "Untitled";
 
-				if (data) lychee.goto("a" + data);
-				else loadingBar.show("error");
+				if (title.length>0&&title.length<31) {
 
-			});
+					modal.close();
 
-		} else if (title.length>0) loadingBar.show("error", "Error", "Title to short or too long. Please try another one!");
+					params = "addAlbum&title=" + escape(encodeURI(title));
+					lychee.api(params, function(data) {
+
+						if (data!==false) lychee.goto(data);
+						else lychee.error(null, params, data);
+
+					});
+
+				} else loadingBar.show("error", "Title too short or too long. Please try again!");
+
+			}],
+			["Cancel", function() {}]
+		];
+		modal.show("New Album", "Please enter a title for this album: <input class='text' type='text' placeholder='Title' value='Untitled'>", buttons);
 
 	},
 
@@ -120,12 +129,14 @@ album = {
 			["Delete Album and Photos", function() {
 
 				params = "deleteAlbum&albumID=" + albumID + "&delAll=true";
-				lychee.api(params, "text", function(data) {
+				lychee.api(params, function(data) {
 
-					if (visible.albums()) view.albums.content.delete(albumID);
-					else lychee.goto("");
+					if (visible.albums()) {
+						albums.json.num--;
+						view.albums.content.delete(albumID);
+					} else lychee.goto("");
 
-					if (!data) loadingBar.show("error");
+					if (data!==true) lychee.error(null, params, data);
 
 				});
 
@@ -133,7 +144,7 @@ album = {
 			["Keep Album", function() {}]
 		];
 
-		if (albumID==0) {
+		if (albumID==="0") {
 
 			buttons[0][0] = "Clear Unsorted";
 			modal.show("Clear Unsorted", "Are you sure you want to delete all photos from 'Unsorted'?<br>This action can't be undone!", buttons)
@@ -152,66 +163,113 @@ album = {
 
 		var oldTitle = "",
 			newTitle,
-			params;
+			params,
+			buttons;
 
-		if (!albumID) albumID = album.getID();
+		if (!albumID) return false;
 		if (album.json) oldTitle = album.json.title;
 		else if (albums.json) oldTitle = albums.json.content[albumID].title;
 
-		newTitle = prompt("Please enter a new title for this album:", oldTitle);
+		buttons = [
+			["Set Title", function() {
 
-		if (albumID!=""&&albumID!=null&&albumID&&newTitle.length>0&&newTitle.length<31) {
+				newTitle = $(".message input.text").val();
 
-			if (visible.album()) {
+				if (newTitle==="") newTitle = "Untitled";
 
-				album.json.title = newTitle;
-				view.album.title();
+				if (albumID!==""&&albumID!=null&&albumID&&newTitle.length<31) {
 
-			} else if (visible.albums()) {
+					if (visible.album()) {
 
-				albums.json.content[albumID].title = newTitle;
-				view.albums.content.title(albumID);
+						album.json.title = newTitle;
+						view.album.title(oldTitle);
 
-			}
+					} else if (visible.albums()) {
 
-			params = "setAlbumTitle&albumID=" + albumID + "&title=" + escape(encodeURI(newTitle));
-			lychee.api(params, "text", function(data) {
+						albums.json.content[albumID].title = newTitle;
+						view.albums.content.title(albumID);
 
-				if (!data) loadingBar.show("error");
+					}
 
-			});
+					params = "setAlbumTitle&albumID=" + albumID + "&title=" + escape(encodeURI(newTitle));
+					lychee.api(params, function(data) {
+
+						if (data!==true) lychee.error(null, params, data);
+
+					});
+
+				} else if (newTitle.length>0) loadingBar.show("error", "New title too short or too long. Please try again!");
+
+			}],
+			["Cancel", function() {}]
+		];
+		modal.show("Set Title", "Please enter a new title for this album: <input class='text' type='text' placeholder='Title' value='" + oldTitle + "'>", buttons);
+
+	},
+
+	setDescription: function(photoID) {
+
+		var oldDescription = album.json.description,
+			description,
+			params,
+			buttons;
+
+		buttons = [
+			["Set Description", function() {
+
+				description = $(".message input.text").val();
+
+				if (description.length<800) {
+
+					if (visible.album()) {
+						album.json.description = description;
+						view.album.description();
+					}
+
+					params = "setAlbumDescription&albumID=" + photoID + "&description=" + escape(description);
+					lychee.api(params, function(data) {
+
+						if (data!==true) lychee.error(null, params, data);
 
-		} else if (newTitle.length>0) loadingBar.show("error", "Error", "New title to short or too long. Please try another one!");
+					});
+
+				} else loadingBar.show("error", "Description too long. Please try again!");
+
+			}],
+			["Cancel", function() {}]
+		];
+		modal.show("Set Description", "Please enter a description for this album: <input class='text' type='text' placeholder='Description' value='" + oldDescription + "'>", buttons);
 
 	},
 
 	setPublic: function(albumID, e) {
 
 		var params;
-		
-		if ($("input.password").length>0&&$("input.password").val().length>0) {
-		
-			params = "setAlbumPublic&albumID=" + albumID + "&password=" + hex_md5($("input.password").val());
+
+		if ($(".message input.text").length>0&&$(".message input.text").val().length>0) {
+
+			params = "setAlbumPublic&albumID=" + albumID + "&password=" + hex_md5($(".message input.text").val());
 			album.json.password = true;
-			
+
 		} else {
-		
+
 			params = "setAlbumPublic&albumID=" + albumID;
 			album.json.password = false;
-			
+
 		}
 
 		if (visible.album()) {
 
 			album.json.public = (album.json.public==0) ? 1 : 0;
 			view.album.public();
+			view.album.password();
 			if (album.json.public==1) contextMenu.shareAlbum(albumID, e);
 
 		}
-		
-		lychee.api(params, "text", function(data) {
 
-			if (!data) loadingBar.show("error");
+		lychee.api(params, function(data) {
+
+			if (data!==true) lychee.error(null, params, data);
 
 		});
 
@@ -247,6 +305,9 @@ album = {
 
 		if (location.href.indexOf("index.html")>0) link = location.href.replace(location.hash, "").replace("index.html", "php/api.php?function=getAlbumArchive&albumID=" + albumID);
 		else link = location.href.replace(location.hash, "") + "php/api.php?function=getAlbumArchive&albumID=" + albumID;
+
+		if (lychee.publicMode) link += "&password=" + password.value;
+
 		location.href = link;
 
 	}

+ 12 - 19
js/modules/albums.js

@@ -1,11 +1,8 @@
 /**
- * @name        albums.js
- * @author      Philipp Maurer
- * @author      Tobias Reich
- * @copyright   2013 by Philipp Maurer, Tobias Reich
- *
- * Albums Module
- * Takes care of every action albums can handle and execute.
+ * @name		Albums Module
+ * @description	Takes care of every action albums can handle and execute.
+ * @author		Tobias Reich
+ * @copyright	2014 by Tobias Reich
  */
 
 albums = {
@@ -16,18 +13,14 @@ albums = {
 
 		var startTime,
 			durationTime,
-			unsortedAlbum,
-			starredAlbum,
-			publicAlbum,
-			smartData = "",
-			albumsData = "";
+			waitTime;
 
 		lychee.animate(".album, .photo", "contentZoomOut");
 		lychee.animate(".divider", "fadeOut");
 
 		startTime = new Date().getTime();
 
-		lychee.api("getAlbums", "json", function(data) {
+		lychee.api("getAlbums", function(data) {
 
 			/* Smart Albums */
 			data.unsortedAlbum = {
@@ -81,13 +74,13 @@ albums = {
 	parse: function(album) {
 
 		if (album.password&&lychee.publicMode) {
-			album.thumb0 = "img/password.png";
-			album.thumb1 = "img/password.png";
-			album.thumb2 = "img/password.png";
+			album.thumb0 = "assets/img/password.svg";
+			album.thumb1 = "assets/img/password.svg";
+			album.thumb2 = "assets/img/password.svg";
 		} else {
-			if (album.thumb0) album.thumb0 = lychee.upload_path_thumb + album.thumb0; else album.thumb0 = "img/no_images.png";
-			if (album.thumb1) album.thumb1 = lychee.upload_path_thumb + album.thumb1; else album.thumb1 = "img/no_images.png";
-			if (album.thumb2) album.thumb2 = lychee.upload_path_thumb + album.thumb2; else album.thumb2 = "img/no_images.png";
+			if (album.thumb0) album.thumb0 = lychee.upload_path_thumb + album.thumb0; else album.thumb0 = "assets/img/no_images.svg";
+			if (album.thumb1) album.thumb1 = lychee.upload_path_thumb + album.thumb1; else album.thumb1 = "assets/img/no_images.svg";
+			if (album.thumb2) album.thumb2 = lychee.upload_path_thumb + album.thumb2; else album.thumb2 = "assets/img/no_images.svg";
 		}
 
 	}

+ 410 - 0
assets/js/modules/build.js

@@ -0,0 +1,410 @@
+/**
+ * @name		Build Module
+ * @description	This module is used to generate HTML-Code.
+ * @author		Tobias Reich
+ * @copyright	2014 by Tobias Reich
+ */
+
+build = {
+
+	divider: function(title) {
+
+		return "<div class='divider fadeIn'><h1>" + title + "</h1></div>";
+
+	},
+
+	editIcon: function(id) {
+
+		return "<div id='" + id + "' class='edit'><a class='icon-pencil'></a></div>";
+
+	},
+
+	album: function(albumJSON) {
+
+		if (!albumJSON) return "";
+
+		var album = "",
+			title = albumJSON.title;
+
+		if (title.length>18) title = albumJSON.title.substr(0, 18) + "...";
+
+		album += "<div  class='album' data-id='" + albumJSON.id + "' data-password='" + albumJSON.password + "'>";
+		album +=	"<img src='" + albumJSON.thumb2 + "' width='200' height='200' alt='thumb'>";
+		album +=	"<img src='" + albumJSON.thumb1 + "' width='200' height='200' alt='thumb'>";
+		album +=	"<img src='" + albumJSON.thumb0 + "' width='200' height='200' alt='thumb'>";
+		album +=	"<div class='overlay'>";
+
+		if (albumJSON.password&&!lychee.publicMode) album += "<h1><span class='icon-lock'></span> " + title + "</h1>";
+		else album += "<h1>" + title + "</h1>";
+
+		album +=		"<a>" + albumJSON.sysdate + "</a>";
+		album +=	"</div>";
+
+		if(!lychee.publicMode&&albumJSON.star==1) album += "<a class='badge red icon-star'></a>";
+		if(!lychee.publicMode&&albumJSON.public==1) album += "<a class='badge red icon-share'></a>";
+		if(!lychee.publicMode&&albumJSON.unsorted==1) album += "<a class='badge red icon-reorder'></a>";
+
+		album += "</div>";
+
+		return album;
+
+	},
+
+	photo: function(photoJSON) {
+
+		if (!photoJSON) return "";
+
+		var photo = "",
+			title = photoJSON.title;
+
+		if (title.length>18) title = photoJSON.title.substr(0, 18) + "...";
+
+		photo += "<div class='photo' data-album-id='" + photoJSON.album + "' data-id='" + photoJSON.id + "'>";
+		photo +=	"<img src='" + photoJSON.thumbUrl + "' width='200' height='200' alt='thumb'>";
+		photo +=	"<div class='overlay'>";
+		photo +=		"<h1>" + title + "</h1>";
+		photo +=		"<a>" + photoJSON.sysdate + "</a>";
+		photo +=	"</div>";
+
+		if (photoJSON.star==1) photo += "<a class='badge red icon-star'></a>";
+		if (!lychee.publicMode&&photoJSON.public==1&&album.json.public!=1) photo += "<a class='badge red icon-share'></a>";
+
+		photo += "</div>";
+
+		return photo;
+
+	},
+
+	imageview: function(photoJSON, isSmall, visibleControls) {
+
+		if (!photoJSON) return "";
+
+		var view = "";
+
+		view += "<div class='arrow_wrapper previous'><a id='previous' class='icon-caret-left'></a></div>";
+		view += "<div class='arrow_wrapper next'><a id='next' class='icon-caret-right'></a></div>";
+
+		if (isSmall) {
+
+			if (visibleControls)
+				view += "<div id='image' class='small' style='background-image: url(" + photoJSON.url + "); width: " + photoJSON.width + "px; height: " + photoJSON.height + "px; margin-top: -" + parseInt(photoJSON.height/2-20) + "px; margin-left: -" + photoJSON.width/2 + "px;'></div>";
+			else
+				view += "<div id='image' class='small' style='background-image: url(" + photoJSON.url + "); width: " + photoJSON.width + "px; height: " + photoJSON.height + "px; margin-top: -" + parseInt(photoJSON.height/2) + "px; margin-left: -" + photoJSON.width/2 + "px;'></div>";
+
+		} else {
+
+			if (visibleControls)
+				view += "<div id='image' style='background-image: url(" + photoJSON.url + ")'></div>";
+			else
+				view += "<div id='image' style='background-image: url(" + photoJSON.url + "); top: 0px; right: 0px; bottom: 0px; left: 0px;'></div>";
+
+		}
+
+		return view;
+
+	},
+
+	no_content: function(typ) {
+
+		var no_content = "";
+
+		no_content += "<div class='no_content fadeIn'>";
+		no_content +=	"<a class='icon icon-" + typ + "'></a>";
+
+		if (typ==="search") no_content += "<p>No results</p>";
+		else if (typ==="picture") no_content += "<p>No public albums</p>";
+		else if (typ==="cog") no_content += "<p>No Configuration!</p>";
+
+		no_content += "</div>";
+
+		return no_content;
+
+	},
+
+	modal: function(title, text, button, marginTop, closeButton) {
+
+		var modal = "",
+			custom_style = "";
+
+		if (marginTop) custom_style = "style='margin-top: " + marginTop + "px;'";
+
+		modal += "<div class='message_overlay fadeIn'>";
+		modal +=	"<div class='message center'" + custom_style + ">";
+		modal +=		"<h1>" + title + "</h1>";
+
+		if (closeButton!=false) {
+
+			modal +=		"<a class='close icon-remove-sign'></a>";
+
+		}
+
+		modal +=		"<p>" + text + "</p>";
+
+		$.each(button, function(index) {
+
+			if (this[0]!="") {
+
+				if (index===0) modal += "<a class='button active'>" + this[0] + "</a>";
+				else modal += "<a class='button'>" + this[0] + "</a>";
+
+			}
+
+		});
+
+		modal +=	"</div>";
+		modal += "</div>";
+
+		return modal;
+
+	},
+
+	signInModal: function() {
+
+		var modal = "";
+
+		modal += "<div class='message_overlay'>";
+		modal +=	"<div class='message center'>";
+		modal +=		"<h1><a class='icon-lock'></a> Sign In</h1>";
+		modal +=		"<a class='close icon-remove-sign'></a>";
+		modal +=		"<div class='sign_in'>";
+		modal +=			"<input id='username' type='text' name='' value='' placeholder='username'>";
+		modal +=			"<input id='password' type='password' name='' value='' placeholder='password'>";
+		modal +=		"</div>";
+		modal +=		"<div id='version'>Version " + lychee.version + "<span> &#8211; <a target='_blank' href='" + lychee.updateURL + "'>Update available!</a><span></div>";
+		modal +=		"<a onclick='lychee.login()' class='button active'>Sign in</a>";
+		modal +=	"</div>";
+		modal += "</div>";
+
+		return modal;
+
+	},
+
+	uploadModal: function(icon, text) {
+
+		var modal = "";
+
+		modal += "<div class='upload_overlay fadeIn'>";
+		modal +=	"<div class='upload_message center'>";
+		modal +=		"<a class='icon-" + icon + "'></a>";
+
+		if (text!==undefined) modal += "<p>" + text + "</p>";
+		else modal += "<div class='progressbar'><div></div></div>";
+
+		modal +=	"</div>";
+		modal += "</div>";
+
+		return modal;
+
+	},
+
+	contextMenu: function(items, orientation) {
+
+		var menu = "";
+
+		menu += "<div class='contextmenu_bg'></div>";
+		menu += "<div class='contextmenu " + orientation + "'>";
+		menu +=		"<table>";
+		menu +=			"<tbody>";
+
+		$.each(items, function(index) {
+
+			if (items[index][0]==="separator"&&items[index][1]===-1) menu += "<tr class='separator'></tr>";
+			else if (items[index][1]===-1) menu += "<tr class='no_hover'><td>" + items[index][0] + "</td></tr>";
+			else if (items[index][2]!=undefined) menu += "<tr><td onclick='" + items[index][2] + "; window.contextMenu.close();'>" + items[index][0] + "</td></tr>";
+			else menu += "<tr><td onclick='window.contextMenu.fns[" + items[index][1] + "](); window.contextMenu.close();'>" + items[index][0] + "</td></tr>";
+
+		});
+
+		menu +=			"</tbody>";
+		menu +=		"</table>";
+		menu +=	"</div>";
+
+		return menu;
+
+	},
+
+	infoboxPhoto: function(photoJSON, forView) {
+
+		if (!photoJSON) return "";
+
+		var infobox = "",
+			public,
+			editTitleHTML,
+			editDescriptionHTML,
+			infos;
+
+		infobox += "<div class='header'><h1>About</h1><a class='icon-remove-sign'></a></div>";
+		infobox += "<div class='wrapper'>";
+
+		switch (photoJSON.public) {
+			case "0":
+				public = "Private";
+				break;
+			case "1":
+				public = "Public";
+				break;
+			case "2":
+				public = "Public (Album)";
+				break;
+			default:
+				public = "-";
+				break;
+		}
+
+		editTitleHTML = (forView===true||lychee.publicMode) ? "" : " " + build.editIcon("edit_title");
+		editDescriptionHTML = (forView===true||lychee.publicMode) ? "" : " " + build.editIcon("edit_description");
+
+		//["Tags", "<a class='tag'>Abstract<span class='icon-remove'></span></a><a class='tag'>Colors<span class='icon-remove'></span></a><a class='tag'>Photoshop<span class='icon-remove'></span></a><a class='tag'>Something<span class='icon-remove'></span></a><a class='tag'>Lychee<span class='icon-remove'></span></a><a class='tag'>Tags<span class='icon-remove'></span></a><a class='tag icon-plus'></a>"]
+		infos = [
+			["", "Basics"],
+			["Name", photoJSON.title + editTitleHTML],
+			["Uploaded", photoJSON.sysdate],
+			["Description", photoJSON.description + editDescriptionHTML],
+			["", "Image"],
+			["Size", photoJSON.size],
+			["Format", photoJSON.type],
+			["Resolution", photoJSON.width + " x " + photoJSON.height]
+		];
+
+		if ((photoJSON.takedate+photoJSON.make+photoJSON.model+photoJSON.shutter+photoJSON.aperture+photoJSON.focal+photoJSON.iso)!="") {
+
+			infos = infos.concat([
+				["", "Camera"],
+				["Captured", photoJSON.takedate],
+				["Make", photoJSON.make],
+				["Type/Model", photoJSON.model],
+				["Shutter Speed", photoJSON.shutter],
+				["Aperture", photoJSON.aperture],
+				["Focal Length", photoJSON.focal],
+				["ISO", photoJSON.iso]
+			]);
+
+		}
+
+		infos = infos.concat([
+			["", "Share"],
+			["Visibility", public]
+		]);
+
+		$.each(infos, function(index) {
+
+			if (infos[index][1]===""||infos[index][1]==undefined||infos[index][1]==null) infos[index][1] = "-";
+
+			switch (infos[index][0]) {
+
+				case "":		// Separator
+								infobox += "</table>";
+								infobox += "<div class='separator'><h1>" + infos[index][1] + "</h1></div>";
+								infobox += "<table>";
+								break;
+
+				case "Tags":	// Tags
+								infobox += "</table>";
+								infobox += "<div class='separator'><h1>" + infos[index][0] + "</h1></div>";
+								infobox += "<tr>";
+								infobox +=		"<div id='tags'>" + infos[index][1] + "</div>";
+								infobox += "</tr>";
+								break;
+
+				default:		// Item
+								infobox +=	"<tr>";
+								infobox +=		"<td>" + infos[index][0] + "</td>";
+								infobox +=		"<td class='attr_" + infos[index][0].toLowerCase() + "'>" + infos[index][1] + "</td>";
+								infobox +=	"</tr>";
+								break;
+
+			}
+
+		});
+
+		infobox += "</table>";
+		infobox += "<div class='bumper'></div>";
+		infobox += "</div>";
+
+		return infobox;
+
+	},
+
+	infoboxAlbum: function(albumJSON, forView) {
+
+		if (!albumJSON) return "";
+
+		var infobox = "",
+			public,
+			password,
+			editTitleHTML,
+			editDescriptionHTML,
+			infos;
+
+		infobox += "<div class='header'><h1>About</h1><a class='icon-remove-sign'></a></div>";
+		infobox += "<div class='wrapper'>";
+
+		switch (albumJSON.public) {
+			case "0":
+				public = "Private";
+				break;
+			case "1":
+				public = "Public";
+				break;
+			default:
+				public = "-";
+				break;
+		}
+
+		switch (albumJSON.password) {
+			case false:
+				password = "No";
+				break;
+			case true:
+				password = "Yes";
+				break;
+			default:
+				password = "-";
+				break;
+		}
+
+		editTitleHTML = (forView===true||lychee.publicMode) ? "" : " " + build.editIcon("edit_title_album");
+		editDescriptionHTML = (forView===true||lychee.publicMode) ? "" : " " + build.editIcon("edit_description_album");
+
+		infos = [
+			["", "Basics"],
+			["Name", albumJSON.title + editTitleHTML],
+			["Description", albumJSON.description + editDescriptionHTML],
+			["", "Album"],
+			["Created", albumJSON.sysdate],
+			["Images", albumJSON.num],
+			["", "Share"],
+			["Visibility", public],
+			["Password", password]
+		];
+
+		$.each(infos, function(index) {
+
+			if (infos[index][1]===""||infos[index][1]==undefined||infos[index][1]==null) infos[index][1] = "-";
+
+			if (infos[index][0]==="") {
+
+				infobox += "</table>";
+				infobox += "<div class='separator'><h1>" + infos[index][1] + "</h1></div>";
+				infobox += "<table id='infos'>";
+
+			} else {
+
+				infobox +=	"<tr>";
+				infobox +=		"<td>" + infos[index][0] + "</td>";
+				infobox +=		"<td class='attr_" + infos[index][0].toLowerCase() + "'>" + infos[index][1] + "</td>";
+				infobox +=	"</tr>";
+
+			}
+
+		});
+
+		infobox += "</table>";
+		infobox += "<div class='bumper'></div>";
+		infobox += "</div>";
+
+		return infobox;
+
+	}
+
+}

+ 120 - 112
js/modules/contextMenu.js

@@ -1,26 +1,101 @@
 /**
- * @name        contextMenu.js
- * @author      Philipp Maurer
- * @author      Tobias Reich
- * @copyright   2013 by Philipp Maurer, Tobias Reich
- *
- * ContextMenu Module
- * This module is used for the context menu.
+ * @name		ContextMenu Module
+ * @description	This module is used for the context menu.
+ * @author		Tobias Reich
+ * @copyright	2014 by Tobias Reich
  */
 
 contextMenu = {
 
 	fns: null,
 
-	album: function(e) {
+	show: function(items, mouse_x, mouse_y, orientation) {
+
+		contextMenu.close();
+
+		$("body")
+			.css("overflow", "hidden")
+			.append(build.contextMenu(items, orientation));
+
+		if (orientation==="left") mouse_x -= $(".contextmenu").outerWidth(true);
+
+		if (!mouse_x||!mouse_y) {
+			mouse_x = "10px";
+			mouse_y = "10px";
+		}
+
+		$(".contextmenu").css({
+			"top": mouse_y,
+			"left": mouse_x
+		});
+
+	},
+
+	add: function(e) {
+
+		var mouse_x = e.pageX,
+			mouse_y = e.pageY,
+			items;
+
+		mouse_y -= $(document).scrollTop();
+
+		contextMenu.fns = [
+			function() { $("#upload_files").click() },
+			function() { upload.start.url() },
+			function() { upload.start.dropbox() },
+			function() { upload.start.server() },
+			function() { album.add() }
+		];
+
+		items = [
+			["<a class='icon-picture'></a> Upload Photo", 0],
+			["separator", -1],
+			["<a class='icon-link'></a> Import from Link", 1],
+			["<a class='icon-folder-open'></a> Import from Dropbox", 2],
+			["<a class='icon-hdd'></a> Import from Server", 3],
+			["separator", -1],
+			["<a class='icon-folder-close'></a> New Album", 4]
+		];
+
+		contextMenu.show(items, mouse_x, mouse_y, "left");
+
+	},
+
+	settings: function(e) {
+
+		var mouse_x = e.pageX,
+			mouse_y = e.pageY,
+			items;
+
+		mouse_y -= $(document).scrollTop();
+
+		contextMenu.fns = [
+			function() { settings.setLogin() },
+			function() { settings.setSorting() },
+			function() { window.open(lychee.website,"_newtab"); },
+			function() { lychee.logout() }
+		];
+
+		items = [
+			["<a class='icon-user'></a> Change Login", 0],
+			["<a class='icon-sort'></a> Change Sorting", 1],
+			["<a class='icon-info-sign'></a> About Lychee", 2],
+			["separator", -1],
+			["<a class='icon-signout'></a> Sign Out", 3]
+		];
+
+		contextMenu.show(items, mouse_x, mouse_y, "right");
+
+	},
+
+	album: function(albumID, e) {
 
 		e.preventDefault();
 		var mouse_x = e.pageX,
 			mouse_y = e.pageY,
-			albumID = album.getID(),
 			items;
 
-		if (albumID=="0"||albumID=="f"||albumID=="s") return false;
+		if (albumID==="0"||albumID==="f"||albumID==="s") return false;
 
 		mouse_y -= $(document).scrollTop();
 
@@ -34,24 +109,17 @@ contextMenu = {
 			["<a class='icon-trash'></a> Delete", 1]
 		];
 
-		contextMenu.close();
+		contextMenu.show(items, mouse_x, mouse_y, "right");
+
 		$(".album[data-id='" + albumID + "']").addClass("active");
-		$("body")
-			.css("overflow", "hidden")
-			.append(build.contextMenu(items));
-		$(".contextmenu").css({
-			"top": mouse_y,
-			"left": mouse_x
-		});
 
 	},
 
-	photo: function(e) {
+	photo: function(photoID, e) {
 
 		e.preventDefault();
 		var mouse_x = e.pageX,
 			mouse_y = e.pageY,
-			photoID = photo.getID(),
 			items;
 
 		mouse_y -= $(document).scrollTop();
@@ -59,7 +127,7 @@ contextMenu = {
 		contextMenu.fns = [
 			function() { photo.setStar(photoID) },
 			function() { photo.setTitle(photoID) },
-			function() { contextMenu.move(photoID, e) },
+			function() { contextMenu.move(photoID, e, "right") },
 			function() { photo.delete(photoID) }
 		];
 
@@ -71,41 +139,36 @@ contextMenu = {
 			["<a class='icon-trash'></a> Delete", 3]
 		];
 
-		contextMenu.close();
+		contextMenu.show(items, mouse_x, mouse_y, "right");
+
 		$(".photo[data-id='" + photoID + "']").addClass("active");
-		$("body")
-			.css("overflow", "hidden")
-			.append(build.contextMenu(items));
-		$(".contextmenu").css({
-			"top": mouse_y,
-			"left": mouse_x
-		});
 
 	},
 
-	move: function(photoID, e) {
+	move: function(photoID, e, orientation) {
 
 		var mouse_x = e.pageX,
 			mouse_y = e.pageY,
-			items = [],
-			albumID;
+			items = [];
 
 		contextMenu.fns = [];
 		mouse_y -= $(document).scrollTop();
 
+		if (orientation===undefined) orientation = "left";
+
 		if (!mouse_x||!mouse_y) {
 			mouse_x = "10px";
 			mouse_y = "10px";
 		}
 
-		if (album.getID()!=0) {
+		if (album.getID()!=="0") {
 			items = [
 				["Unsorted", 0, "photo.setAlbum(0, " + photoID + ")"],
 				["separator", -1]
 			];
 		}
 
-		lychee.api("getAlbums", "json", function(data) {
+		lychee.api("getAlbums", function(data) {
 
 			if (!data.albums) {
 				items = [["New Album", 0, "album.add()"]];
@@ -119,7 +182,7 @@ contextMenu = {
 			$(".photo[data-id='" + photoID + "']").addClass("active");
 			$("body")
 				.css("overflow", "hidden")
-				.append(build.contextMenu(items));
+				.append(build.contextMenu(items, orientation));
 			if (!visible.photo()) mouse_x += $(".contextmenu").width();
 			$(".contextmenu").css({
 				"top": mouse_y,
@@ -138,11 +201,6 @@ contextMenu = {
 
 		mouse_y -= $(document).scrollTop();
 
-		if (!mouse_x||!mouse_y) {
-			mouse_x = "10px";
-			mouse_y = "10px";
-		}
-
 		contextMenu.fns = [
 			function() { photo.setPublic(photoID) },
 			function() { photo.share(photoID, 0) },
@@ -151,40 +209,18 @@ contextMenu = {
 			function() { photo.share(photoID, 3) }
 		];
 
-		if (document.location.hostname!="localhost") {
-
-			items = [
-				["<input readonly id='link' value='" + photo.getViewLink(photoID) + "'>", -1],
-				["separator", -1],
-				["<a class='icon-eye-close'></a> Make Private", 0],
-				["separator", -1],
-				["<a class='icon-twitter'></a> Twitter", 1],
-				["<a class='icon-facebook'></a> Facebook", 2],
-				["<a class='icon-envelope'></a> Mail", 3],
-				["<a class='icon-hdd'></a> Dropbox", 4]
-			];
-
-		} else {
-
-			items = [
-				["<input readonly id='link' value='" + photo.getViewLink(photoID) + "'>", -1],
-				["separator", -1],
-				["<a class='icon-eye-close'></a> Make Private", 0],
-				["separator", -1],
-				["<a class='icon-envelope'></a> Mail", 3]
-			];
-
-		}
-
-		contextMenu.close();
-		$("body")
-			.css("overflow", "hidden")
-			.append(build.contextMenu(items));
-		$(".contextmenu").css({
-			"top": mouse_y,
-			"left": mouse_x+20-$(".contextmenu").width()
-		});
+		items = [
+			["<input readonly id='link' value='" + photo.getViewLink(photoID) + "'>", -1],
+			["separator", -1],
+			["<a class='icon-eye-close'></a> Make Private", 0],
+			["separator", -1],
+			["<a class='icon-twitter'></a> Twitter", 1],
+			["<a class='icon-facebook'></a> Facebook", 2],
+			["<a class='icon-envelope'></a> Mail", 3],
+			["<a class='icon-hdd'></a> Dropbox", 4]
+		];
 
+		contextMenu.show(items, mouse_x, mouse_y, "left");
 		$(".contextmenu input").focus();
 
 	},
@@ -197,11 +233,6 @@ contextMenu = {
 
 		mouse_y -= $(document).scrollTop();
 
-		if (!mouse_x||!mouse_y) {
-			mouse_x = "10px";
-			mouse_y = "10px";
-		}
-
 		contextMenu.fns = [
 			function() { album.setPublic(albumID) },
 			function() { password.set(albumID) },
@@ -211,43 +242,20 @@ contextMenu = {
 			function() { password.remove(albumID) }
 		];
 
-		if (document.location.hostname!="localhost") {
-
-			items = [
-				["<input readonly id='link' value='" + location.href + "'>", -1],
-				["separator", -1],
-				["<a class='icon-eye-close'></a> Make Private", 0],
-				["<a class='icon-lock'></a> Set Password", 1],
-				["separator", -1],
-				["<a class='icon-twitter'></a> Twitter", 2],
-				["<a class='icon-facebook'></a> Facebook", 3],
-				["<a class='icon-envelope'></a> Mail", 4],
-			];
-
-		} else {
-
-			items = [
-				["<input readonly id='link' value='" + location.href + "'>", -1],
-				["separator", -1],
-				["<a class='icon-eye-close'></a> Make Private", 0],
-				["<a class='icon-lock'></a> Set Password", 1],
-				["separator", -1],
-				["<a class='icon-envelope'></a> Mail", 4],
-			];
-
-		}
+		items = [
+			["<input readonly id='link' value='" + location.href + "'>", -1],
+			["separator", -1],
+			["<a class='icon-eye-close'></a> Make Private", 0],
+			["<a class='icon-lock'></a> Set Password", 1],
+			["separator", -1],
+			["<a class='icon-twitter'></a> Twitter", 2],
+			["<a class='icon-facebook'></a> Facebook", 3],
+			["<a class='icon-envelope'></a> Mail", 4],
+		];
 
 		if (album.json.password==true) items[3] = ["<a class='icon-unlock'></a> Remove Password", 5];
 
-		contextMenu.close();
-		$("body")
-			.css("overflow", "hidden")
-			.append(build.contextMenu(items));
-		$(".contextmenu").css({
-			"top": mouse_y,
-			"left": mouse_x+20-$(".contextmenu").width()
-		});
-
+		contextMenu.show(items, mouse_x, mouse_y, "left");
 		$(".contextmenu input").focus();
 
 	},

+ 138 - 0
assets/js/modules/init.js

@@ -0,0 +1,138 @@
+/**
+ * @name		Init Module
+ * @author		Tobias Reich
+ * @copyright	2014 by Tobias Reich
+ */
+
+$(document).ready(function(){
+
+	/* Event Name */
+	var event_name = (mobileBrowser()) ? "touchend" : "click";
+
+	/* Notifications */
+	if (window.webkitNotifications) window.webkitNotifications.requestPermission();
+
+	/* Tooltips */
+	if (!mobileBrowser()) $(".tools").tipsy({gravity: 'n', fade: false, delayIn: 0, opacity: 1});
+
+	/* Header */
+	$("#hostedwith").on(event_name, function() { window.open(lychee.website,"_newtab") });
+	$("#button_signin").on(event_name, lychee.loginDialog);
+	$("#button_settings").on(event_name, contextMenu.settings);
+	$("#button_share").on(event_name, function(e) {
+		if (photo.json.public==1||photo.json.public==2) contextMenu.sharePhoto(photo.getID(), e);
+		else photo.setPublic(photo.getID(), e);
+	});