photo.js 15 KB

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