scripts.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. document.addEventListener('DOMContentLoaded', function() {
  2. tinymce.init({
  3. license_key: 'gpl',
  4. remove_script_host: false,
  5. relative_urls: false,
  6. entity_encoding: 'raw',
  7. valid_elements: '*[*]',
  8. content_css: false,
  9. force_br_newlines: true,
  10. entities: '160,nbsp',
  11. remove_linebreaks: false,
  12. selector: '.secondary-content',
  13. encoding: 'UTF-8',
  14. plugins: 'image link media code table lists',
  15. toolbar: 'undo redo | styleselect | bold italic | bullist numlist | alignleft aligncenter alignright alignjustify | outdent indent | link image media table | code',
  16. menubar: 'file edit view insert format tools table help',
  17. contextmenu: 'table',
  18. images_upload_url: '/editor/uploads/',
  19. automatic_uploads: true,
  20. file_picker_types: 'image',
  21. images_reuse_filename: true,
  22. paste_as_text: true,
  23. setup: function (editor) {
  24. editor.on('GetContent', function (e) {
  25. e.content = e.content.replace(/<img([^>]*?)>/g, function(match, attrs) {
  26. if (!attrs.includes('loading=')) {
  27. return match.replace('>', ' loading="lazy">');
  28. }
  29. return match;
  30. });
  31. });
  32. },
  33. table_default_attributes: {
  34. border: '1',
  35. },
  36. table_default_styles: {
  37. width: '100%',
  38. borderCollapse: 'collapse',
  39. },
  40. file_picker_callback: function(callback, value, meta) {
  41. if (meta.filetype === 'image') {
  42. const input = document.createElement('input');
  43. input.setAttribute('type', 'file');
  44. input.setAttribute('accept', 'image/*');
  45. input.onchange = function() {
  46. const file = this.files[0];
  47. const formData = new FormData();
  48. formData.append('file', file);
  49. fetch('/editor/uploads/', {
  50. method: 'POST',
  51. body: formData
  52. })
  53. .then(response => response.json())
  54. .then(result => {
  55. callback(result.location);
  56. })
  57. .catch(() => alert('Ошибка при загрузке изображения.'));
  58. };
  59. input.click();
  60. }
  61. },
  62. content_style: `
  63. body { font-family: Arial, sans-serif; font-size: 14px; }
  64. table { border-collapse: collapse; width: 100%; }
  65. th, td { border: 1px solid #ddd; padding: 8px; }
  66. th { background-color: #f2f2f2; text-align: left; }
  67. `
  68. });
  69. });
  70. // Color picker functionality
  71. document.addEventListener('DOMContentLoaded', function () {
  72. const colorPickers = document.querySelectorAll('.inp-color');
  73. const textInputs = document.querySelectorAll('.inp-color-text');
  74. colorPickers.forEach(colorPicker => {
  75. colorPicker.addEventListener('input', function () {
  76. const textInput = this.nextElementSibling;
  77. if (textInput && textInput.classList.contains('inp-color-text')) {
  78. textInput.value = this.value;
  79. }
  80. });
  81. });
  82. textInputs.forEach(textInput => {
  83. textInput.addEventListener('input', function () {
  84. const colorPicker = this.previousElementSibling;
  85. if (colorPicker && colorPicker.classList.contains('inp-color')) {
  86. if (/^#([0-9A-F]{3}|[0-9A-F]{6})$/i.test(this.value)) {
  87. colorPicker.value = this.value;
  88. }
  89. }
  90. });
  91. const colorPicker = textInput.previousElementSibling;
  92. if (colorPicker && colorPicker.classList.contains('inp-color')) {
  93. if (/^#([0-9A-F]{3}|[0-9A-F]{6})$/i.test(textInput.value)) {
  94. colorPicker.value = textInput.value;
  95. }
  96. }
  97. });
  98. });
  99. // Casino repeater functionality
  100. function addCasinoItem() {
  101. const container = document.getElementById('casinoRepeater');
  102. const tpl = document.getElementById('casinoTemplate').content.cloneNode(true);
  103. container.appendChild(tpl);
  104. }
  105. function uploadImage(input, wrapper) {
  106. const file = input.files[0];
  107. const formData = new FormData();
  108. formData.append('image', file);
  109. fetch('upload_repeater_image.php', {
  110. method: 'POST',
  111. body: formData
  112. })
  113. .then(resp => resp.json())
  114. .then(data => {
  115. if (data.success) {
  116. wrapper.querySelector('.image').value = data.path;
  117. } else {
  118. alert('Ошибка загрузки изображения: ' + data.message);
  119. }
  120. });
  121. }
  122. function saveRepeater() {
  123. const items = [];
  124. document.querySelectorAll('#casinoRepeater .casino-item').forEach((item, index) => {
  125. items.push({
  126. id: item.dataset.id || null,
  127. sort_order: index,
  128. image: item.querySelector('.image').value,
  129. alt: item.querySelector('.alt').value,
  130. title: item.querySelector('.title').value,
  131. heading: item.querySelector('.heading').value,
  132. text: item.querySelector('.text').value,
  133. button: item.querySelector('.button').value,
  134. keitaro: item.querySelector('.keitaro').value,
  135. });
  136. });
  137. document.getElementById('casinoRepeaterData').value = JSON.stringify(items);
  138. return true;
  139. }
  140. // Drag and Drop functionality for casino items
  141. document.addEventListener('DOMContentLoaded', function() {
  142. const container = document.getElementById('casinoRepeater');
  143. if (!container) return;
  144. let draggedElement = null;
  145. let placeholder = null;
  146. function updateOrderNumbers() {
  147. const items = container.querySelectorAll('.casino-item');
  148. items.forEach((item, index) => {
  149. const numberElement = item.querySelector('.casino-order-number');
  150. if (numberElement) {
  151. numberElement.textContent = index + 1;
  152. }
  153. });
  154. }
  155. function createPlaceholder() {
  156. const div = document.createElement('div');
  157. div.className = 'drag-placeholder';
  158. div.style.cssText = 'height: 60px; margin: 10px 0; border: 2px dashed #007bff; background: #f0f8ff; opacity: 0.5;';
  159. return div;
  160. }
  161. function initAccordion() {
  162. const items = container.querySelectorAll('.casino-item');
  163. items.forEach(item => {
  164. const header = item.querySelector('.casino-item-header');
  165. const toggleBtn = item.querySelector('.toggle-accordion');
  166. const content = item.querySelector('.casino-item-content');
  167. const dragHandle = item.querySelector('.drag-handle');
  168. if (!header || !toggleBtn || !content) return;
  169. const toggleAccordion = (e) => {
  170. if (e.target.closest('.drag-handle')) {
  171. return;
  172. }
  173. const isOpen = content.style.display !== 'none' && content.style.display !== '';
  174. if (isOpen) {
  175. content.style.display = 'none';
  176. toggleBtn.textContent = '▼';
  177. } else {
  178. content.style.display = 'flex';
  179. toggleBtn.textContent = '▲';
  180. }
  181. };
  182. header.addEventListener('click', toggleAccordion);
  183. header.addEventListener('mousedown', (e) => {
  184. if (!e.target.closest('.drag-handle')) {
  185. item.setAttribute('draggable', 'false');
  186. }
  187. });
  188. header.addEventListener('mouseup', () => {
  189. item.setAttribute('draggable', 'true');
  190. });
  191. dragHandle.addEventListener('mousedown', (e) => {
  192. e.stopPropagation();
  193. item.setAttribute('draggable', 'true');
  194. });
  195. const headingInput = item.querySelector('.heading');
  196. if (headingInput) {
  197. headingInput.addEventListener('input', function() {
  198. const titleElement = item.querySelector('.casino-item-title');
  199. if (titleElement) {
  200. titleElement.textContent = this.value || 'New Casino Item';
  201. }
  202. });
  203. }
  204. });
  205. }
  206. function initDragAndDrop() {
  207. const items = container.querySelectorAll('.casino-item');
  208. items.forEach(item => {
  209. item.setAttribute('draggable', 'true');
  210. const dragHandle = item.querySelector('.drag-handle');
  211. dragHandle.addEventListener('mousedown', function(e) {
  212. item.setAttribute('draggable', 'true');
  213. });
  214. item.addEventListener('dragstart', function(e) {
  215. if (!e.target.querySelector('.drag-handle')) {
  216. e.preventDefault();
  217. return;
  218. }
  219. draggedElement = this;
  220. this.classList.add('dragging');
  221. e.dataTransfer.effectAllowed = 'move';
  222. e.dataTransfer.setData('text/html', '');
  223. setTimeout(() => {
  224. this.style.display = 'none';
  225. }, 0);
  226. });
  227. item.addEventListener('dragend', function(e) {
  228. this.style.display = '';
  229. this.classList.remove('dragging');
  230. if (placeholder && placeholder.parentNode) {
  231. placeholder.parentNode.removeChild(placeholder);
  232. }
  233. placeholder = null;
  234. draggedElement = null;
  235. updateOrderNumbers();
  236. });
  237. });
  238. }
  239. container.addEventListener('dragover', function(e) {
  240. e.preventDefault();
  241. e.dataTransfer.dropEffect = 'move';
  242. if (!draggedElement) return;
  243. const afterElement = getDragAfterElement(container, e.clientY);
  244. const currentItems = [...container.querySelectorAll('.casino-item:not(.dragging)')];
  245. if (!placeholder) {
  246. placeholder = createPlaceholder();
  247. }
  248. if (afterElement == null) {
  249. container.appendChild(placeholder);
  250. } else {
  251. container.insertBefore(placeholder, afterElement);
  252. }
  253. });
  254. container.addEventListener('drop', function(e) {
  255. e.preventDefault();
  256. if (!draggedElement || !placeholder) return;
  257. if (placeholder.parentNode) {
  258. placeholder.parentNode.insertBefore(draggedElement, placeholder);
  259. placeholder.parentNode.removeChild(placeholder);
  260. }
  261. draggedElement.style.display = '';
  262. placeholder = null;
  263. });
  264. function getDragAfterElement(container, y) {
  265. const draggableElements = [...container.querySelectorAll('.casino-item:not(.dragging)')];
  266. return draggableElements.reduce((closest, child) => {
  267. const box = child.getBoundingClientRect();
  268. const offset = y - box.top - box.height / 2;
  269. if (offset < 0 && offset > closest.offset) {
  270. return { offset: offset, element: child };
  271. } else {
  272. return closest;
  273. }
  274. }, { offset: Number.NEGATIVE_INFINITY }).element;
  275. }
  276. initAccordion();
  277. initDragAndDrop();
  278. updateOrderNumbers();
  279. container.addEventListener('itemRemoved', function() {
  280. updateOrderNumbers();
  281. });
  282. window.originalAddCasinoItem = window.addCasinoItem;
  283. window.addCasinoItem = function() {
  284. if (window.originalAddCasinoItem) {
  285. window.originalAddCasinoItem();
  286. } else {
  287. const container = document.getElementById('casinoRepeater');
  288. const tpl = document.getElementById('casinoTemplate').content.cloneNode(true);
  289. container.appendChild(tpl);
  290. }
  291. setTimeout(() => {
  292. initAccordion();
  293. initDragAndDrop();
  294. updateOrderNumbers();
  295. }, 10);
  296. };
  297. });