photo.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699
  1. /**
  2. * @description Takes care of every action a photo can handle and execute.
  3. * @copyright 2015 by Tobias Reich
  4. */
  5. photo = {
  6. json: null,
  7. cache: null
  8. }
  9. photo.getID = function() {
  10. var id;
  11. if (photo.json) id = photo.json.id;
  12. else id = $('.photo:hover, .photo.active').attr('data-id');
  13. if (id) return id;
  14. else return false;
  15. }
  16. photo.load = function(photoID, albumID) {
  17. var params,
  18. checkPasswd;
  19. params = 'getPhoto&photoID=' + photoID + '&albumID=' + albumID + '&password=' + password.value;
  20. lychee.api(params, function(data) {
  21. if (data==='Warning: Wrong password!') {
  22. checkPasswd = function() {
  23. if (password.value!=='') photo.load(photoID, albumID);
  24. else setTimeout(checkPasswd, 250);
  25. };
  26. checkPasswd();
  27. return false;
  28. }
  29. photo.json = data;
  30. if (!visible.photo()) view.photo.show();
  31. view.photo.init();
  32. lychee.imageview.show();
  33. setTimeout(function() {
  34. lychee.content.show();
  35. //photo.preloadNext(photoID, albumID);
  36. }, 300);
  37. });
  38. }
  39. // Preload the next photo for better response time
  40. photo.preloadNext = function(photoID) {
  41. var nextPhoto,
  42. url;
  43. // Never preload on mobile devices with bare RAM and
  44. // mostly mobile internet
  45. // {{ code }}
  46. if (album.json &&
  47. album.json.content &&
  48. album.json.content[photoID] &&
  49. album.json.content[photoID].nextPhoto!='') {
  50. nextPhoto = album.json.content[photoID].nextPhoto;
  51. url = album.json.content[nextPhoto].url;
  52. photo.cache = new Image();
  53. photo.cache.src = url;
  54. photo.cache.onload = function() { photo.cache = null };
  55. }
  56. }
  57. photo.parse = function() {
  58. if (!photo.json.title) photo.json.title = 'Untitled';
  59. }
  60. photo.previous = function(animate) {
  61. var delay = 0;
  62. if (photo.getID()!==false&&
  63. album.json&&
  64. album.json.content[photo.getID()]&&
  65. album.json.content[photo.getID()].previousPhoto!=='') {
  66. if (animate===true) {
  67. delay = 200;
  68. $('#image').css({
  69. WebkitTransform: 'translateX(100%)',
  70. MozTransform: 'translateX(100%)',
  71. transform: 'translateX(100%)',
  72. opacity: 0
  73. });
  74. }
  75. setTimeout(function() {
  76. if (photo.getID()===false) return false;
  77. lychee.goto(album.getID() + '/' + album.json.content[photo.getID()].previousPhoto)
  78. }, delay);
  79. }
  80. }
  81. photo.next = function(animate) {
  82. var delay = 0;
  83. if (photo.getID()!==false&&
  84. album.json&&
  85. album.json.content[photo.getID()]&&
  86. album.json.content[photo.getID()].nextPhoto!=='') {
  87. if (animate===true) {
  88. delay = 200;
  89. $('#image').css({
  90. WebkitTransform: 'translateX(-100%)',
  91. MozTransform: 'translateX(-100%)',
  92. transform: 'translateX(-100%)',
  93. opacity: 0
  94. });
  95. }
  96. setTimeout(function() {
  97. if (photo.getID()===false) return false;
  98. lychee.goto(album.getID() + '/' + album.json.content[photo.getID()].nextPhoto);
  99. }, delay);
  100. }
  101. }
  102. photo.duplicate = function(photoIDs) {
  103. var params;
  104. if (!photoIDs) return false;
  105. if (photoIDs instanceof Array===false) photoIDs = [photoIDs];
  106. albums.refresh();
  107. params = 'duplicatePhoto&photoIDs=' + photoIDs;
  108. lychee.api(params, function(data) {
  109. if (data!==true) lychee.error(null, params, data);
  110. else album.load(album.getID());
  111. });
  112. }
  113. photo.delete = function(photoIDs) {
  114. var action = {},
  115. cancel = {},
  116. msg = '',
  117. photoTitle = '';
  118. if (!photoIDs) return false;
  119. if (photoIDs instanceof Array===false) photoIDs = [photoIDs];
  120. if (photoIDs.length===1) {
  121. // Get title if only one photo is selected
  122. if (visible.photo()) photoTitle = photo.json.title;
  123. else photoTitle = album.json.content[photoIDs].title;
  124. // Fallback for photos without a title
  125. if (photoTitle==='') photoTitle = 'Untitled';
  126. }
  127. action.fn = function() {
  128. var params = '',
  129. nextPhoto = '',
  130. previousPhoto = '';
  131. basicModal.close();
  132. photoIDs.forEach(function(id, index, array) {
  133. // Change reference for the next and previous photo
  134. if (album.json.content[id].nextPhoto!==''||album.json.content[id].previousPhoto!=='') {
  135. nextPhoto = album.json.content[id].nextPhoto;
  136. previousPhoto = album.json.content[id].previousPhoto;
  137. album.json.content[previousPhoto].nextPhoto = nextPhoto;
  138. album.json.content[nextPhoto].previousPhoto = previousPhoto;
  139. }
  140. album.json.content[id] = null;
  141. view.album.content.delete(id);
  142. });
  143. albums.refresh();
  144. // Go to next photo if there is a next photo and
  145. // next photo is not the current one. Show album otherwise.
  146. if (visible.photo()&&nextPhoto!==''&&nextPhoto!==photo.getID()) lychee.goto(album.getID() + '/' + nextPhoto);
  147. else if (!visible.albums()) lychee.goto(album.getID());
  148. params = 'deletePhoto&photoIDs=' + photoIDs;
  149. lychee.api(params, function(data) {
  150. if (data!==true) lychee.error(null, params, data);
  151. });
  152. }
  153. if (photoIDs.length===1) {
  154. action.title = 'Delete Photo';
  155. cancel.title = 'Keep Photo';
  156. msg = "<p>Are you sure you want to delete the photo '" + photoTitle + "'?<br>This action can't be undone!</p>";
  157. } else {
  158. action.title = 'Delete Photo';
  159. cancel.title = 'Keep Photo';
  160. msg = "<p>Are you sure you want to delete all " + photoIDs.length + " selected photo?<br>This action can't be undone!</p>";
  161. }
  162. basicModal.show({
  163. body: msg,
  164. buttons: {
  165. action: {
  166. title: action.title,
  167. fn: action.fn,
  168. class: 'red'
  169. },
  170. cancel: {
  171. title: cancel.title,
  172. fn: basicModal.close
  173. }
  174. }
  175. });
  176. }
  177. photo.setTitle = function(photoIDs) {
  178. var oldTitle = '',
  179. input = '',
  180. msg = '',
  181. action;
  182. if (!photoIDs) return false;
  183. if (photoIDs instanceof Array===false) photoIDs = [photoIDs];
  184. if (photoIDs.length===1) {
  185. // Get old title if only one photo is selected
  186. if (photo.json) oldTitle = photo.json.title;
  187. else if (album.json) oldTitle = album.json.content[photoIDs].title;
  188. oldTitle = oldTitle.replace("'", '&apos;');
  189. }
  190. action = function(data) {
  191. var params,
  192. newTitle = data.title;
  193. basicModal.close();
  194. // Remove html from input
  195. newTitle = lychee.removeHTML(newTitle);
  196. if (visible.photo()) {
  197. photo.json.title = (newTitle==='') ? 'Untitled' : newTitle;
  198. view.photo.title();
  199. }
  200. photoIDs.forEach(function(id, index, array) {
  201. album.json.content[id].title = newTitle;
  202. view.album.content.title(id);
  203. });
  204. params = 'setPhotoTitle&photoIDs=' + photoIDs + '&title=' + escape(encodeURI(newTitle));
  205. lychee.api(params, function(data) {
  206. if (data!==true) lychee.error(null, params, data);
  207. });
  208. }
  209. input = "<input class='text' data-name='title' type='text' maxlength='30' placeholder='Title' value='" + oldTitle + "'>";
  210. if (photoIDs.length===1) msg = "<p>Enter a new title for this photo: " + input + "</p>";
  211. else msg = "<p>Enter a title for all " + photoIDs.length + " selected photos: " + input + "</p>";
  212. basicModal.show({
  213. body: msg,
  214. buttons: {
  215. action: {
  216. title: 'Set title',
  217. fn: action
  218. },
  219. cancel: {
  220. title: 'Cancel',
  221. fn: basicModal.close
  222. }
  223. }
  224. });
  225. }
  226. photo.setAlbum = function(photoIDs, albumID) {
  227. var params,
  228. nextPhoto,
  229. previousPhoto;
  230. if (!photoIDs) return false;
  231. if (visible.photo) lychee.goto(album.getID());
  232. if (photoIDs instanceof Array===false) photoIDs = [photoIDs];
  233. photoIDs.forEach(function(id, index, array) {
  234. // Change reference for the next and previous photo
  235. if (album.json.content[id].nextPhoto!==''||album.json.content[id].previousPhoto!=='') {
  236. nextPhoto = album.json.content[id].nextPhoto;
  237. previousPhoto = album.json.content[id].previousPhoto;
  238. album.json.content[previousPhoto].nextPhoto = nextPhoto;
  239. album.json.content[nextPhoto].previousPhoto = previousPhoto;
  240. }
  241. album.json.content[id] = null;
  242. view.album.content.delete(id);
  243. });
  244. albums.refresh();
  245. params = 'setPhotoAlbum&photoIDs=' + photoIDs + '&albumID=' + albumID;
  246. lychee.api(params, function(data) {
  247. if (data!==true) lychee.error(null, params, data);
  248. });
  249. }
  250. photo.setStar = function(photoIDs) {
  251. var params;
  252. if (!photoIDs) return false;
  253. if (visible.photo()) {
  254. photo.json.star = (photo.json.star==0) ? 1 : 0;
  255. view.photo.star();
  256. }
  257. photoIDs.forEach(function(id, index, array) {
  258. album.json.content[id].star = (album.json.content[id].star==0) ? 1 : 0;
  259. view.album.content.star(id);
  260. });
  261. albums.refresh();
  262. params = 'setPhotoStar&photoIDs=' + photoIDs;
  263. lychee.api(params, function(data) {
  264. if (data!==true) lychee.error(null, params, data);
  265. });
  266. }
  267. photo.setPublic = function(photoID, e) {
  268. var params;
  269. if (photo.json.public==2) {
  270. var action;
  271. action = function() {
  272. basicModal.close();
  273. lychee.goto(photo.json.original_album);
  274. }
  275. basicModal.show({
  276. body: "<p>This photo is located in a public album. To make this photo private or public, edit the visibility of the associated album.</p>",
  277. buttons: {
  278. action: {
  279. title: 'Show Album',
  280. fn: action
  281. },
  282. cancel: {
  283. title: 'Cancel',
  284. fn: basicModal.close
  285. }
  286. }
  287. });
  288. return false;
  289. }
  290. if (visible.photo()) {
  291. photo.json.public = (photo.json.public==0) ? 1 : 0;
  292. view.photo.public();
  293. if (photo.json.public==1) contextMenu.sharePhoto(photoID, e);
  294. }
  295. album.json.content[photoID].public = (album.json.content[photoID].public==0) ? 1 : 0;
  296. view.album.content.public(photoID);
  297. albums.refresh();
  298. params = 'setPhotoPublic&photoID=' + photoID;
  299. lychee.api(params, function(data) {
  300. if (data!==true) lychee.error(null, params, data);
  301. });
  302. }
  303. photo.setDescription = function(photoID) {
  304. var oldDescription = photo.json.description.replace("'", '&apos;'),
  305. action;
  306. action = function(data) {
  307. var params,
  308. description = data.description;
  309. basicModal.close();
  310. // Remove html from input
  311. description = lychee.removeHTML(description);
  312. if (visible.photo()) {
  313. photo.json.description = description;
  314. view.photo.description();
  315. }
  316. params = 'setPhotoDescription&photoID=' + photoID + '&description=' + escape(encodeURI(description));
  317. lychee.api(params, function(data) {
  318. if (data!==true) lychee.error(null, params, data);
  319. });
  320. }
  321. basicModal.show({
  322. body: "<p>Enter a description for this photo: <input class='text' data-name='description' type='text' maxlength='800' placeholder='Description' value='" + oldDescription + "'></p>",
  323. buttons: {
  324. action: {
  325. title: 'Set Description',
  326. fn: action
  327. },
  328. cancel: {
  329. title: 'Cancel',
  330. fn: basicModal.close
  331. }
  332. }
  333. });
  334. }
  335. photo.editTags = function(photoIDs) {
  336. var oldTags = '',
  337. msg = '',
  338. input = '';
  339. if (!photoIDs) return false;
  340. if (photoIDs instanceof Array===false) photoIDs = [photoIDs];
  341. // Get tags
  342. if (visible.photo()) oldTags = photo.json.tags;
  343. if (visible.album()&&photoIDs.length===1) oldTags = album.json.content[photoIDs].tags;
  344. if (visible.album()&&photoIDs.length>1) {
  345. var same = true;
  346. photoIDs.forEach(function(id, index, array) {
  347. if(album.json.content[id].tags===album.json.content[photoIDs[0]].tags&&same===true) same = true;
  348. else same = false;
  349. });
  350. if (same) oldTags = album.json.content[photoIDs[0]].tags;
  351. }
  352. // Improve tags
  353. oldTags = oldTags.replace(/,/g, ', ');
  354. action = function(data) {
  355. basicModal.close();
  356. photo.setTags(photoIDs, data.tags);
  357. }
  358. input = "<input class='text' data-name='tags' type='text' maxlength='800' placeholder='Tags' value='" + oldTags + "'>";
  359. if (photoIDs.length===1) msg = "<p>Enter your tags for this photo. You can add multiple tags by separating them with a comma: " + input + "</p>";
  360. else msg = "<p>Enter your tags for all " + photoIDs.length + " selected photos. Existing tags will be overwritten. You can add multiple tags by separating them with a comma: " + input + "</p>";
  361. basicModal.show({
  362. body: msg,
  363. buttons: {
  364. action: {
  365. title: 'Set Tags',
  366. fn: action
  367. },
  368. cancel: {
  369. title: 'Cancel',
  370. fn: basicModal.close
  371. }
  372. }
  373. });
  374. }
  375. photo.setTags = function(photoIDs, tags) {
  376. var params;
  377. if (!photoIDs) return false;
  378. if (photoIDs instanceof Array===false) photoIDs = [photoIDs];
  379. // Parse tags
  380. tags = tags.replace(/(\ ,\ )|(\ ,)|(,\ )|(,{1,}\ {0,})|(,$|^,)/g, ',');
  381. tags = tags.replace(/,$|^,|(\ ){0,}$/g, '');
  382. // Remove html from input
  383. tags = lychee.removeHTML(tags);
  384. if (visible.photo()) {
  385. photo.json.tags = tags;
  386. view.photo.tags();
  387. }
  388. photoIDs.forEach(function(id, index, array) {
  389. album.json.content[id].tags = tags;
  390. });
  391. params = 'setPhotoTags&photoIDs=' + photoIDs + '&tags=' + tags;
  392. lychee.api(params, function(data) {
  393. if (data!==true) lychee.error(null, params, data);
  394. });
  395. }
  396. photo.deleteTag = function(photoID, index) {
  397. var tags;
  398. // Remove
  399. tags = photo.json.tags.split(',');
  400. tags.splice(index, 1);
  401. // Save
  402. photo.json.tags = tags.toString();
  403. photo.setTags([photoID], photo.json.tags);
  404. }
  405. photo.share = function(photoID, service) {
  406. var link = '',
  407. url = photo.getViewLink(photoID),
  408. filename = 'unknown';
  409. switch (service) {
  410. case 0:
  411. link = 'https://twitter.com/share?url=' + encodeURI(url);
  412. break;
  413. case 1:
  414. link = 'http://www.facebook.com/sharer.php?u=' + encodeURI(url) + '&t=' + encodeURI(photo.json.title);
  415. break;
  416. case 2:
  417. link = 'mailto:?subject=' + encodeURI(photo.json.title) + '&body=' + encodeURI(url);
  418. break;
  419. case 3:
  420. lychee.loadDropbox(function() {
  421. filename = photo.json.title + '.' + photo.getDirectLink().split('.').pop();
  422. Dropbox.save(photo.getDirectLink(), filename);
  423. });
  424. break;
  425. default:
  426. link = '';
  427. break;
  428. }
  429. if (link.length>5) location.href = link;
  430. }
  431. photo.getSize = function() {
  432. // Size can be 'big', 'medium' or 'small'
  433. // Default is big
  434. // Small is centered in the middle of the screen
  435. var size = 'big',
  436. scaled = false,
  437. hasMedium = photo.json.medium!=='',
  438. pixelRatio = window.devicePixelRatio,
  439. view = {
  440. width: $(window).width()-60,
  441. height: $(window).height()-100
  442. };
  443. // Detect if the photo will be shown scaled,
  444. // because the screen size is smaller than the photo
  445. if (photo.json.width>view.width||
  446. photo.json.height>view.height) scaled = true;
  447. // Calculate pixel ratio of screen
  448. if (pixelRatio!==undefined&&pixelRatio>1) {
  449. view.width = view.width * pixelRatio;
  450. view.height = view.height * pixelRatio;
  451. }
  452. // Medium available and
  453. // Medium still bigger than screen
  454. if (hasMedium===true&&
  455. (1920>view.width&&1080>view.height)) size = 'medium';
  456. // Photo not scaled
  457. // Photo smaller then screen
  458. if (scaled===false&&
  459. (photo.json.width<view.width&&
  460. photo.json.width<view.height)) size = 'small';
  461. return size;
  462. }
  463. photo.getArchive = function(photoID) {
  464. var link,
  465. url = 'php/api.php?function=getPhotoArchive&photoID=' + photoID;
  466. if (location.href.indexOf('index.html')>0) link = location.href.replace(location.hash, '').replace('index.html', url);
  467. else link = location.href.replace(location.hash, '') + url;
  468. if (lychee.publicMode) link += '&password=' + password.value;
  469. location.href = link;
  470. }
  471. photo.getDirectLink = function() {
  472. var url = '';
  473. if (photo.json&&
  474. photo.json.url&&
  475. photo.json.url!=='') url = photo.json.url;
  476. return url;
  477. }
  478. photo.getViewLink = function(photoID) {
  479. var url = 'view.php?p=' + photoID;
  480. if (location.href.indexOf('index.html')>0) return location.href.replace('index.html' + location.hash, url);
  481. else return location.href.replace(location.hash, url);
  482. }