photo.js 15 KB

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