jquery.galleryManager.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. (function ($) {
  2. 'use strict';
  3. var galleryDefaults = {
  4. csrfToken: $('meta[name=csrf-token]').attr('content'),
  5. csrfTokenName: $('meta[name=csrf-param]').attr('content'),
  6. nameLabel: 'Name',
  7. descriptionLabel: 'Description',
  8. hasName: true,
  9. hasDesc: true,
  10. uploadUrl: '',
  11. deleteUrl: '',
  12. updateUrl: '',
  13. arrangeUrl: '',
  14. photos: []
  15. };
  16. function galleryManager(el, options) {
  17. //Extending options:
  18. var opts = $.extend({}, galleryDefaults, options);
  19. //code
  20. var csrfParams = opts.csrfToken ? '&' + opts.csrfTokenName + '=' + opts.csrfToken : '';
  21. var photos = {}; // photo elements by id
  22. var $gallery = $(el);
  23. if (!opts.hasName) {
  24. if (!opts.hasDesc) {
  25. $gallery.addClass('no-name-no-desc');
  26. $('.edit_selected', $gallery).hide();
  27. }
  28. else $gallery.addClass('no-name');
  29. } else if (!opts.hasDesc)
  30. $gallery.addClass('no-desc');
  31. var $sorter = $('.sorter', $gallery);
  32. var $images = $('.images', $sorter);
  33. var $editorModal = $('.editor-modal', $gallery);
  34. var $progressOverlay = $('.progress-overlay', $gallery);
  35. var $uploadProgress = $('.upload-progress', $progressOverlay);
  36. var $editorForm = $('.form', $editorModal);
  37. function htmlEscape(str) {
  38. return String(str)
  39. .replace(/&/g, '&')
  40. .replace(/"/g, '"')
  41. .replace(/'/g, ''')
  42. .replace(/</g, '&lt;')
  43. .replace(/>/g, '&gt;');
  44. }
  45. function createEditorElement(id, src, name, description) {
  46. var html = '<div class="photo-editor row">';
  47. if( typeof src === 'string' || src instanceof String ){
  48. html += '<div class="col-xs-4">' +
  49. '<img src="' + htmlEscape(src) + '" style="max-width:100%;">' +
  50. '</div>';
  51. }
  52. html += '<div class="col-xs-8">' +
  53. (opts.hasName
  54. ?
  55. '<div class="form-group">' +
  56. '<label class="control-label" for="photo_name_' + id + '">' + opts.nameLabel + ':</label>' +
  57. '<input class="form-control" type="text" name="photo[' + id + '][name]" class="input-xlarge" value="' + htmlEscape(name) + '" id="photo_name_' + id + '"/>' +
  58. '</div>' : '') +
  59. (opts.hasDesc
  60. ?
  61. '<div class="form-group">' +
  62. '<label class="control-label" for="photo_description_' + id + '">' + opts.descriptionLabel + ':</label>' +
  63. '<textarea class="form-control" name="photo[' + id + '][description]" rows="3" cols="40" class="input-xlarge" id="photo_description_' + id + '">' + htmlEscape(description) + '</textarea>' +
  64. '</div>' : '') +
  65. '</div>' +
  66. '</div>';
  67. return $(html);
  68. }
  69. var photoTemplate = '<div class="photo">' + '<div class="image-preview"><img src=""/></div><div class="caption">';
  70. if (opts.hasName) {
  71. photoTemplate += '<h5></h5>';
  72. }
  73. if (opts.hasDesc) {
  74. photoTemplate += '<p></p>';
  75. }
  76. photoTemplate += '</div><div class="actions">';
  77. if (opts.hasName || opts.hasDesc) {
  78. photoTemplate += '<span class="editPhoto btn btn-primary btn-xs"><i class="fas fa-edit"></i></span> ';
  79. }
  80. photoTemplate += '<span class="deletePhoto btn btn-danger btn-xs"><i class="fas fa-trash"></i></span>' +
  81. '</div><input type="checkbox" class="photo-select"/></div>';
  82. function addPhoto(id, src, name, description, rank) {
  83. var photo = $(photoTemplate);
  84. photos[id] = photo;
  85. photo.data('id', id);
  86. photo.data('rank', rank);
  87. if( typeof src === 'string' || src instanceof String ){
  88. $('img', photo).attr('src', src);
  89. }else{
  90. $('.image-preview', photo).css('display', 'none');
  91. $('.caption p', photo).css('height', '9em');
  92. }
  93. if (opts.hasName){
  94. // name = '';
  95. $('.caption h5', photo).text(name);
  96. }
  97. if (opts.hasDesc){
  98. $('.caption p', photo).text(description);
  99. }
  100. $images.append(photo);
  101. return photo;
  102. }
  103. function editPhotos(ids) {
  104. var l = ids.length;
  105. var form = $editorForm.empty();
  106. for (var i = 0; i < l; i++) {
  107. var id = ids[i];
  108. var photo = photos[id],
  109. src = $('img', photo).attr('src'),
  110. name = $('.caption h5', photo).text(),
  111. description = $('.caption p', photo).text();
  112. form.append(createEditorElement(id, src, name, description));
  113. }
  114. if (l > 0){
  115. $editorModal.modal('show');
  116. }
  117. }
  118. function removePhotos(ids) {
  119. $.ajax({
  120. type: 'POST',
  121. url: opts.deleteUrl,
  122. data: 'id[]=' + ids.join('&id[]=') + csrfParams,
  123. success: function (t) {
  124. if (t == 'OK') {
  125. for (var i = 0, l = ids.length; i < l; i++) {
  126. photos[ids[i]].remove();
  127. delete photos[ids[i]];
  128. }
  129. } else {
  130. alert(t);
  131. }
  132. }
  133. });
  134. }
  135. function deleteClick(e) {
  136. e.preventDefault();
  137. var photo = $(this).closest('.photo');
  138. var id = photo.data('id');
  139. // here can be question to confirm delete
  140. // if (!confirm(deleteConfirmation)) return false;
  141. removePhotos([id]);
  142. return false;
  143. }
  144. function editClick(e) {
  145. e.preventDefault();
  146. var photo = $(this).closest('.photo');
  147. var id = photo.data('id');
  148. editPhotos([id]);
  149. return false;
  150. }
  151. function updateButtons() {
  152. var selectedCount = $('.photo.selected', $sorter).length;
  153. $('.select_all', $gallery).prop('checked', $('.photo', $sorter).length == selectedCount);
  154. if (selectedCount == 0) {
  155. $('.edit_selected, .remove_selected', $gallery).addClass('disabled');
  156. } else {
  157. $('.edit_selected, .remove_selected', $gallery).removeClass('disabled');
  158. }
  159. }
  160. function selectChanged() {
  161. var $this = $(this);
  162. if ($this.is(':checked'))
  163. $this.closest('.photo').addClass('selected');
  164. else
  165. $this.closest('.photo').removeClass('selected');
  166. updateButtons();
  167. }
  168. function sending(xhr, fd, fixer){
  169. console.log(fixer);
  170. if( fixer.CurUploaded <= fixer.MaxUploaded ){
  171. xhr.send(fd);
  172. fixer.CurUploaded++;
  173. }else{
  174. setTimeout( sending,5000, xhr, fd, fixer);
  175. }
  176. }
  177. $images
  178. .on('click', '.photo .deletePhoto', deleteClick)
  179. .on('click', '.photo .editPhoto', editClick)
  180. .on('click', '.photo .photo-select', selectChanged);
  181. $('.images', $sorter).sortable({tolerance: "pointer"}).disableSelection().bind("sortstop", function () {
  182. var data = [];
  183. $('.photo', $sorter).each(function () {
  184. var t = $(this);
  185. data.push('order[' + t.data('id') + ']=' + t.data('rank'));
  186. });
  187. $.ajax({
  188. type: 'POST',
  189. url: opts.arrangeUrl,
  190. data: data.join('&') + csrfParams,
  191. dataType: "json"
  192. }).done(function (data) {
  193. for (var id in data[id]) {
  194. photos[id].data('rank', data[id]);
  195. }
  196. // order saved!
  197. // we can inform user that order saved
  198. });
  199. });
  200. if (window.FormData !== undefined) { // if XHR2 available
  201. var uploadFileName = $('.afile', $gallery).attr('name');
  202. var multiUpload = function (files) {
  203. if (files.length == 0) return;
  204. $progressOverlay.show();
  205. $uploadProgress.css('width', '5%');
  206. var filesCount = files.length;
  207. var uploadedCount = 0;
  208. var ids = [];
  209. var poper = new Map();
  210. var fixer = {MaxUploaded:3, CurUploaded:0, MaxScreen:12};
  211. for (var i = 0; i < filesCount; i++) {
  212. var fd = new FormData();
  213. console.log(files[i]);
  214. if( files[i]['size'] < 5242880 ){
  215. fd.append(uploadFileName, files[i]);
  216. if (opts.csrfToken) {
  217. fd.append(opts.csrfTokenName, opts.csrfToken);
  218. }
  219. let xhr = new XMLHttpRequest();
  220. xhr.open('POST', opts.uploadUrl, true);
  221. xhr.onload = function () {
  222. uploadedCount++;
  223. fixer.CurUploaded--;
  224. if (this.status == 200) {
  225. var resp = JSON.parse(this.response);
  226. addPhoto(resp['id'], resp['preview'], '', resp['description'], resp['rank']);
  227. ids.push(resp['id']);
  228. var poperm = poper.get(resp['name']);
  229. poperm.toast('hide');
  230. setTimeout(() => {
  231. poperm.remove();
  232. },2000, poperm);
  233. } else {
  234. // exception !!!
  235. }
  236. $uploadProgress.css('width', '' + (5 + 95 * uploadedCount / filesCount) + '%');
  237. if (uploadedCount === filesCount) {
  238. $uploadProgress.css('width', '100%');
  239. $progressOverlay.hide();
  240. if (opts.hasName || opts.hasDesc) editPhotos(ids);
  241. }
  242. };
  243. var templ = $("#noty0").clone();
  244. var fname = files[i].name;
  245. xhr.nn = fname;
  246. $(xhr.upload).bind('progress', function (e) {
  247. // xhr.upload.addEventListener("progress", (event) => {
  248. let percent = Math.round(event.loaded *100/ event.total);
  249. // console.log(xhr.nn);
  250. // console.log(xhr);
  251. // console.log(percent);
  252. var el = poper.get(xhr.nn);
  253. el.find(".progress-bar").css("width", percent+"%");
  254. if( percent > 98 ){
  255. el.find(".text-muted").text("Обработка");
  256. }
  257. });
  258. templ.find(".toast-body").text(files[i].name);
  259. $("#notycont").prepend(templ);
  260. templ.toast('show');
  261. poper.set(files[i].name, templ);
  262. console.log(fixer);
  263. if( fixer.CurUploaded >= fixer.MaxUploaded ){
  264. console.log("pause");
  265. setTimeout( sending,5000, xhr, fd, fixer);
  266. }else{
  267. fixer.CurUploaded ++;
  268. xhr.send(fd);
  269. }
  270. }else{
  271. alert(files[i].name+' Превышен допустимый размер 5Мб');
  272. uploadedCount++;
  273. if (uploadedCount === filesCount) {
  274. $uploadProgress.css('width', '100%');
  275. $progressOverlay.hide();
  276. if (opts.hasName || opts.hasDesc) editPhotos(ids);
  277. }
  278. }
  279. }
  280. };
  281. (function () { // add drag and drop
  282. var el = $gallery[0];
  283. var isOver = false;
  284. var lastIsOver = false;
  285. setInterval(function () {
  286. if (isOver != lastIsOver) {
  287. if (isOver) el.classList.add('over');
  288. else el.classList.remove('over');
  289. lastIsOver = isOver
  290. }
  291. }, 30);
  292. function handleDragOver(e) {
  293. e.preventDefault();
  294. isOver = true;
  295. return false;
  296. }
  297. function handleDragLeave() {
  298. isOver = false;
  299. return false;
  300. }
  301. function handleDrop(e) {
  302. e.preventDefault();
  303. e.stopPropagation();
  304. var files = e.dataTransfer.files;
  305. multiUpload(files);
  306. isOver = false;
  307. return false;
  308. }
  309. function handleDragEnd() {
  310. isOver = false;
  311. }
  312. el.addEventListener('dragover', handleDragOver, false);
  313. el.addEventListener('dragleave', handleDragLeave, false);
  314. el.addEventListener('drop', handleDrop, false);
  315. el.addEventListener('dragend', handleDragEnd, false);
  316. })();
  317. $('.afile', $gallery).attr('multiple', 'true').on('change', function (e) {
  318. e.preventDefault();
  319. multiUpload(this.files);
  320. $(this).val(null);
  321. });
  322. } else {
  323. $('.afile', $gallery).on('change', function (e) {
  324. e.preventDefault();
  325. var ids = [];
  326. $progressOverlay.show();
  327. $uploadProgress.css('width', '5%');
  328. var data = {};
  329. if (opts.csrfToken)
  330. data[opts.csrfTokenName] = opts.csrfToken;
  331. $.ajax({
  332. type: 'POST',
  333. url: opts.uploadUrl,
  334. data: data,
  335. files: $(this),
  336. iframe: true,
  337. processData: false,
  338. dataType: "json"
  339. }).done(function (resp) {
  340. addPhoto(resp['id'], resp['preview'], '', resp['description'], resp['rank']);
  341. ids.push(resp['id']);
  342. $uploadProgress.css('width', '100%');
  343. $progressOverlay.hide();
  344. if (opts.hasName || opts.hasDesc) editPhotos(ids);
  345. });
  346. });
  347. }
  348. $('.save-changes', $editorModal).click(function (e) {
  349. e.preventDefault();
  350. $.post(opts.updateUrl, $('input, textarea', $editorForm).serialize() + csrfParams, function (data) {
  351. var count = data.length;
  352. for (var key = 0; key < count; key++) {
  353. var p = data[key];
  354. var photo = photos[p.id];
  355. $('img', photo).attr('src', p['src']);
  356. if (opts.hasName)
  357. $('.caption h5', photo).text(p['name']);
  358. if (opts.hasDesc)
  359. $('.caption p', photo).text(p['description']);
  360. }
  361. $editorModal.modal('hide');
  362. //deselect all items after editing
  363. $('.photo.selected', $sorter).each(function () {
  364. $('.photo-select', this).prop('checked', false)
  365. }).removeClass('selected');
  366. $('.select_all', $gallery).prop('checked', false);
  367. updateButtons();
  368. }, 'json');
  369. });
  370. $('.edit_selected', $gallery).click(function (e) {
  371. e.preventDefault();
  372. var ids = [];
  373. $('.photo.selected', $sorter).each(function () {
  374. ids.push($(this).data('id'));
  375. });
  376. editPhotos(ids);
  377. return false;
  378. });
  379. $('.remove_selected', $gallery).click(function (e) {
  380. e.preventDefault();
  381. var ids = [];
  382. $('.photo.selected', $sorter).each(function () {
  383. ids.push($(this).data('id'));
  384. });
  385. removePhotos(ids);
  386. });
  387. $('.select_all', $gallery).change(function () {
  388. if ($(this).prop('checked')) {
  389. $('.photo', $sorter).each(function () {
  390. $('.photo-select', this).prop('checked', true)
  391. }).addClass('selected');
  392. } else {
  393. $('.photo.selected', $sorter).each(function () {
  394. $('.photo-select', this).prop('checked', false)
  395. }).removeClass('selected');
  396. }
  397. updateButtons();
  398. });
  399. for (var i = 0, l = opts.photos.length; i < l; i++) {
  400. var resp = opts.photos[i];
  401. addPhoto(resp['id'], resp['preview'], resp['name'], resp['description'], resp['rank']);
  402. }
  403. let selfo = addPhoto;
  404. $('.savehtml', $gallery).click(function (e) {
  405. slSave($(e.target).attr('rel-mid'),$(e.target).attr('rel-id'), selfo);
  406. ids,push($(e.target).attr('rel-id'));
  407. });
  408. }
  409. // The actual plugin
  410. $.fn.galleryManager = function (options) {
  411. if (this.length) {
  412. this.each(function () {
  413. galleryManager(this, options);
  414. });
  415. }
  416. };
  417. })(jQuery);