Mercurial
comparison .cms/js/admin.js @ 0:78edf6b517a0 draft
24.10
author | Coffee CMS <info@coffee-cms.ru> |
---|---|
date | Fri, 11 Oct 2024 22:40:23 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:78edf6b517a0 |
---|---|
1 // Две функции для чтения и установки cookie. | |
2 // Cookie используются для авторизации на сайте, | |
3 // для запоминания количества отображаемых страниц в пейджере | |
4 // и выбранной темы. | |
5 // Другие модули так же могут их использовать | |
6 // для запоминания своих настроек. | |
7 function get_cookie( name ) { | |
8 let cookies = document.cookie.split( ";" ); | |
9 for ( let line of cookies ) { | |
10 let cookie = line.split( "=" ); | |
11 // TODO: Нужно ли decodeURIComponent( cookie[ 0 ].trim() )? | |
12 // Имена указываются обычно латиницей, без специальных знаков... | |
13 if ( name == cookie[ 0 ].trim() ) { | |
14 return decodeURIComponent( cookie[ 1 ] ); | |
15 } | |
16 } | |
17 return ""; | |
18 } | |
19 | |
20 // ;Path=/ в админке нет смысла указывать, но можно экономить байты куков | |
21 // браузер сам по умолчанию возьмет текущий путь | |
22 function set_cookie( name, value ) { | |
23 document.cookie = encodeURIComponent( name ) + "=" + encodeURIComponent( value ) + ";SameSite=Lax"; | |
24 } | |
25 | |
26 function set_cookie_expires( name, value ) { | |
27 let expires = ( new Date( Date.now() + 365 * 86400 * 1000 ) ).toUTCString(); | |
28 document.cookie = encodeURIComponent( name ) + "=" + encodeURIComponent( value ) + ";SameSite=Lax;expires=" + expires; | |
29 } | |
30 | |
31 // Чтобы удалить куки нужно установить отрицательную дату истечения срока действия | |
32 function del_cookie( name ) { | |
33 document.cookie = encodeURIComponent( name ) + "=;SameSite=Lax;max-age=-1"; | |
34 } | |
35 | |
36 // Notifications | |
37 | |
38 // Уведомления, отображающиеся в правом верхнем. Обычно в течении 5 сек. | |
39 // Через 5 секунд к уведомлению добавляется класс timeout, | |
40 // благодаря ему происходит исчезновение уведомления. | |
41 function notify( message, classes, msec ) { | |
42 let bulb = document.createElement( "div" ); | |
43 bulb.innerHTML = message; | |
44 bulb.className = classes; | |
45 document.querySelector( ".log-info-box" ).appendChild( bulb ); | |
46 let h = bulb.offsetHeight; | |
47 // Чтобы анимировать схлопывание | |
48 bulb.setAttribute( "style", `height:${h}px` ); | |
49 if ( msec ) { | |
50 setTimeout( function() { | |
51 bulb.classList.add( "timeout" ); | |
52 }, msec); | |
53 } | |
54 } | |
55 | |
56 // Translate | |
57 // module: "admin.mod.php" or "pages.mod.php" etc | |
58 | |
59 // Шаблон admin добавляет глобальную js-переменную | |
60 // в которой содержится текущая локаль и переводы, | |
61 // загруженные из файлов .cms/lang/... | |
62 // Поэтому ими можно пользоваться и на стороне админки. | |
63 function __( str, module ) { | |
64 if ( cms && cms.locale && cms.lang && cms.lang[module] && cms.lang[module][cms.locale] && cms.lang[module][cms.locale][str] ) { | |
65 return cms.lang[module][cms.locale][str]; | |
66 } else { | |
67 api( { fn: "no_translation", str: str, module: module } ); | |
68 return str; | |
69 } | |
70 } | |
71 | |
72 // Call server side API | |
73 | |
74 // Передаваемые на сервер данные упаковываются как положено | |
75 // и можно передавать даже массивы. | |
76 // Массивы вложенные в массивы воспринимаются как объекты | |
77 // и кодируются в JSON. | |
78 // После того, как сервер вернет ответ, вызывается функция rfn | |
79 // И ответ передается ей в качестве параметра. | |
80 function api( data, rfn, efn ) { | |
81 const formData = new FormData(); | |
82 buildFormData( formData, data ); | |
83 // send data | |
84 // По умолчанию запросы отправляются асинхронно, | |
85 // но если нужно дождаться ответа и затем изменить полученные данные, | |
86 // то перед вызовом обновляющих функций нужно дописать строчку | |
87 // cms.async_api = false; | |
88 let ajax = new XMLHttpRequest(); | |
89 ajax.addEventListener( "load", function( event ) { | |
90 let data = {}; | |
91 try { | |
92 data = JSON.parse( event.target.responseText ); | |
93 } catch { | |
94 notify( __( "server_error", "admin.mod.php" ), "info-error", 5000 ); | |
95 if ( efn ) { | |
96 efn( event ); | |
97 } | |
98 } | |
99 if ( rfn ) { | |
100 rfn( data ); | |
101 } | |
102 } ); | |
103 ajax.addEventListener( "error", function( event ) { | |
104 notify( __( "network_error", "admin.mod.php" ), "info-error", 5000 ); | |
105 if ( efn ) { | |
106 efn( event ); | |
107 } | |
108 } ); | |
109 ajax.open( "POST", cms.api, cms.async_api ); | |
110 ajax.send( formData ); | |
111 cms.async_api = true; | |
112 } | |
113 function buildFormData( formData, data, parentKey ) { | |
114 if ( data && typeof data === 'object' && ! ( data instanceof Date ) && ! ( data instanceof File ) ) { | |
115 Object.keys( data ).forEach( key => { | |
116 buildFormData( formData, data[key], parentKey ? `${parentKey}[${key}]` : key ); | |
117 } ); | |
118 } else { | |
119 const value = data == null ? '' : data; | |
120 formData.append( parentKey, value ); | |
121 } | |
122 } | |
123 | |
124 | |
125 // Create and connect Codemirror | |
126 function codemirror_connect( selector, name, options = {} ) { | |
127 | |
128 if ( window[name] ) return; | |
129 | |
130 let default_options = { | |
131 mode: "application/x-httpd-php", | |
132 styleActiveLine: true, | |
133 lineNumbers: true, | |
134 lineWrapping: true, | |
135 autoCloseBrackets: true, | |
136 smartIndent: true, | |
137 indentUnit: 4, | |
138 tabSize: 4, | |
139 matchBrackets: true, | |
140 foldGutter: true, | |
141 gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"], | |
142 autoCloseTags: { | |
143 whenClosing: true, | |
144 whenOpening: true, | |
145 indentTags: [ "div", "ul", "ol", "script", "style" ], | |
146 }, | |
147 phrases: { | |
148 "Search:": __( "codemirror_search", "admin.mod.php" ), | |
149 "(Use /re/ syntax for regexp search)" : __( "codemirror_re", "admin.mod.php" ), | |
150 "Replace all:": __( "codemirror_replace_all", "admin.mod.php" ), | |
151 "With:": __( "codemirror_replace_with", "admin.mod.php" ), | |
152 "Replace:": __( "codemirror_replace_replace", "admin.mod.php" ), | |
153 "Replace?": __( "codemirror_replace_confirm", "admin.mod.php" ), | |
154 "Yes": __( "codemirror_yes", "admin.mod.php" ), | |
155 "No": __( "codemirror_no", "admin.mod.php" ), | |
156 "All": __( "codemirror_all", "admin.mod.php" ), | |
157 "Stop": __( "codemirror_stop", "admin.mod.php" ), | |
158 }, | |
159 extraKeys: { "Ctrl-Space": "autocomplete" } | |
160 } | |
161 | |
162 let txtarea = document.querySelector( selector ); | |
163 options = Object.assign( default_options, options ); | |
164 window[name] = CodeMirror.fromTextArea( txtarea, options ); | |
165 | |
166 if ( window[name].showHint ) { | |
167 window[name].on( "keydown", function( editor, event ) { | |
168 if ( event.ctrlKey == true ) { return } // Ctrl+S call Hint | |
169 let isAlphaKey = /^[a-zA-Z]$/.test( event.key ); | |
170 if ( window[name].state.completionActive && isAlphaKey ) { | |
171 return; | |
172 } | |
173 | |
174 // Prevent autocompletion in string literals or comments | |
175 let cursor = window[name].getCursor(); | |
176 let token = window[name].getTokenAt( cursor ); | |
177 if ( token.type === "string" || token.type === "comment" ) { | |
178 return; | |
179 } | |
180 | |
181 let lineBeforeCursor = window[name].doc.getLine( cursor.line ); | |
182 if ( typeof lineBeforeCursor !== "string" ) { | |
183 return; | |
184 } | |
185 lineBeforeCursor = lineBeforeCursor.substring( 0, cursor.ch ); | |
186 | |
187 // disable autoclose tag before text | |
188 let charAfterCursor = window[name].doc.getLine( cursor.line ); | |
189 charAfterCursor = charAfterCursor.substring( cursor.ch, cursor.ch + 1 ); | |
190 window[name].options.autoCloseTags.dontCloseTags = null; | |
191 if ( charAfterCursor.match( /\S/ ) && charAfterCursor != "<" ) { | |
192 if ( lineBeforeCursor.match( /<[^>]+$/ ) ) { | |
193 let tag = lineBeforeCursor.match( /<(\w+)\b[^>]*$/ ); | |
194 if ( tag ) { | |
195 tag = tag[1]; | |
196 window[name].options.autoCloseTags.dontCloseTags = [tag]; | |
197 } | |
198 } | |
199 } | |
200 | |
201 let m = CodeMirror.innerMode( window[name].getMode(), token.state ); | |
202 let innerMode = m.mode.name; | |
203 let shouldAutocomplete; | |
204 if ( innerMode === "html" || innerMode === "xml" ) { | |
205 shouldAutocomplete = event.key === "<" || | |
206 event.key === "/" && token.type === "tag" || | |
207 isAlphaKey && token.type === "tag" || | |
208 isAlphaKey && token.type === "attribute" || | |
209 token.string === "=" && token.state.htmlState && token.state.htmlState.tagName; | |
210 } else if ( innerMode === "css" ) { | |
211 shouldAutocomplete = isAlphaKey || | |
212 event.key === ":" || | |
213 event.key === " " && /:\s+$/.test( lineBeforeCursor ); | |
214 } else if ( innerMode === "javascript" ) { | |
215 shouldAutocomplete = isAlphaKey || event.key === "."; | |
216 } else if ( innerMode === "clike" && window[name].options.mode === "php" ) { | |
217 shouldAutocomplete = token.type === "keyword" || token.type === "variable"; | |
218 } | |
219 if ( shouldAutocomplete ) { | |
220 window[name].showHint( { completeSingle: false } ); | |
221 } | |
222 } ); | |
223 } | |
224 | |
225 // Replace < with < in code context | |
226 // https://stackoverflow.com/a/36388061/20443861 | |
227 window[name].on( "inputRead", function( cm, event ) { | |
228 if ( event.origin == "paste" ) { | |
229 if ( cm.paste_context == "code" ) { | |
230 let text = event.text.join( "\n" ); // pasted string | |
231 let new_text = text.replaceAll( /</g, "<" ); | |
232 new_text = new_text.replaceAll( /&&/g, "&&" ); | |
233 cm.execCommand( "undo" ); | |
234 cm.replaceSelection( new_text ); | |
235 } | |
236 } | |
237 } ); | |
238 window[name].on( "paste", async function( editor, event ) { | |
239 let cursor = window[name].getCursor(); | |
240 let token = window[name].getTokenAt( cursor ); | |
241 if ( token && token.state && token.state.html && token.state.html.htmlState && token.state.html.htmlState.context && token.state.html.htmlState.context.tagName == "code" ) { | |
242 window[name].paste_context = "code"; | |
243 } else { | |
244 window[name].paste_context = ""; | |
245 } | |
246 } ); | |
247 | |
248 } | |
249 | |
250 | |
251 document.addEventListener( "DOMContentLoaded", function( event ) { | |
252 | |
253 function _( str ) { | |
254 return __( str, "admin.mod.php" ); | |
255 } | |
256 | |
257 // Mob Menu | |
258 document.querySelectorAll( "header .burger, .milk" ).forEach( function( el ) { | |
259 el.onclick = function() { | |
260 document.body.classList.toggle( "mobile-menu-open" ); | |
261 } | |
262 } ); | |
263 | |
264 // Навигация стрелками браузера - фикс активного пункта | |
265 window.addEventListener( "popstate", function( e ) { | |
266 // Очищаем без всяких проверок чтобы не подсвечивало на #start | |
267 document.querySelectorAll( "aside a" ).forEach( function( page ) { | |
268 page.classList.remove( "active" ); | |
269 } ); | |
270 // Выделяем нужную ссылку | |
271 let target = window.location.hash; | |
272 let a = document.querySelector( `aside a[href="${target}"]` ); | |
273 if ( a ) { | |
274 a.classList.add( "active" ); | |
275 } | |
276 // надо ли непонятно, скопировал из предыдущего обработчика кликов на всякий случай | |
277 document.body.classList.remove( "mobile-menu-open" ); | |
278 } ); | |
279 | |
280 // Theme switcher | |
281 document.querySelectorAll( ".theme-switcher" ).forEach( function( el ) { | |
282 el.addEventListener( "click", function( event ) { | |
283 event.preventDefault(); | |
284 let n = get_cookie( "theme" ) || 0; | |
285 let styles2 = admin_styles[n]; | |
286 let styles = styles2.split( " " ); | |
287 styles.forEach( function( style ) { | |
288 document.documentElement.classList.remove( style ); | |
289 } ); | |
290 n = (+n+1) % admin_styles.length; | |
291 styles2 = admin_styles[n]; | |
292 styles = styles2.split( " " ); | |
293 styles.forEach( function( style ) { | |
294 document.documentElement.classList.add( style ); | |
295 } ); | |
296 notify( admin_styles[n], "info-success", 5000 ); | |
297 set_cookie( "theme" , n ); | |
298 theme_event = new Event( "theme" ); | |
299 document.dispatchEvent( theme_event ); | |
300 } ); | |
301 } ); | |
302 | |
303 // Logout | |
304 document.querySelectorAll( "[data-logout]" ).forEach( function ( logoutBtn ) { | |
305 logoutBtn.addEventListener( "click", function() { | |
306 // Отправляем на бекенд чтобы закрыло сессию | |
307 api( { fn: "logout" }, | |
308 // Перезагрузка страницы в случае если админка ответила | |
309 function() { | |
310 window.location.reload( true ); | |
311 }, | |
312 // Даже если нет связи, удаляем куки и перезагружаем страницу | |
313 function() { | |
314 del_cookie( "sess" ); | |
315 window.location.reload( true ); | |
316 } | |
317 ); | |
318 }); | |
319 } ); | |
320 | |
321 // Highlight active menu | |
322 if ( document.body.classList.contains( "logged" ) ) { | |
323 let page = window.location.hash; | |
324 // Зачем тут исключение #start? | |
325 if ( page && page != "#start" ) { | |
326 let el = document.querySelector( `a[href="${page}"]` ); | |
327 if ( el ) el.classList.add( "active" ); | |
328 } else if ( document.querySelector( "#start a[href='#base']" ) ) { | |
329 // Приветственная страничка с указанием подключиться к БД | |
330 window.location.hash = "#start"; | |
331 } else { | |
332 // Если никакой пункт не выбран, выбираем по умолчанию Страницы | |
333 window.location.hash = "#pages"; | |
334 } | |
335 } | |
336 | |
337 // Clear Cache | |
338 document.querySelectorAll( ".clear-cache" ).forEach( function( el ) { | |
339 el.addEventListener( "click", function( e ){ | |
340 e.preventDefault(); | |
341 api( { fn: "clear_cache" }, function( r ){ | |
342 if ( r.info_text ) { | |
343 notify( r.info_text, r.info_class, r.info_time ); | |
344 } | |
345 }); | |
346 }); | |
347 } ); | |
348 | |
349 | |
350 // Admin section, Save properties | |
351 document.querySelectorAll( "[data-am-save]" ).forEach( function( saveButton ) { | |
352 saveButton.addEventListener( "click", function( e ) { | |
353 let el = this.closest( "[data-am-item]" ); | |
354 let item = el.getAttribute( "data-am-item" ); | |
355 let selector = `#admin_menu [data-am-item="${item}"]`; | |
356 let title = document.querySelector( `${selector} [name=title]` ); | |
357 if ( title ) { | |
358 title = title.value; | |
359 } | |
360 let section = document.querySelector( `${selector} .section-select-grid .field-select` ); | |
361 if ( section ) { | |
362 section = section.getAttribute( "data-section" ); | |
363 } | |
364 let data = { | |
365 fn: "admin_menu_save", | |
366 type: el.getAttribute( "data-am-type" ), | |
367 module: el.getAttribute( "data-am-module" ), | |
368 item: item, | |
369 title: title, | |
370 sort: document.querySelector( `${selector} [name=sort]` ).value, | |
371 section: section, | |
372 reset: this.hasAttribute( "data-am-reset" ), | |
373 } | |
374 api( data, function( r ) { | |
375 if ( r.ok == "true" ) { | |
376 window.location.reload( true ); | |
377 } | |
378 } ); | |
379 } ); | |
380 } ); | |
381 | |
382 // Admin section, Delete Container | |
383 document.querySelectorAll( "[data-am-delete]" ).forEach( function( button ) { | |
384 button.addEventListener( "click", function( e ){ | |
385 let item = this.closest( "[data-am-item]" ).getAttribute( "data-am-item" ); | |
386 let childs = document.querySelectorAll( `[data-am-childs="${item}"] > div` ).length; | |
387 if ( childs ) { | |
388 notify( _( "not_empty_section" ), "info-error", 2000 ); | |
389 return; | |
390 } | |
391 if ( ! confirm( _( "confirm_delete" ) ) ) return; | |
392 let data = { | |
393 fn: "admin_menu_del", | |
394 item: item, | |
395 } | |
396 api( data, function( r ) { | |
397 if ( r.info_text ) { | |
398 notify( r.info_text, r.info_class, r.info_time ); | |
399 if ( r.info_time ) { | |
400 setTimeout( function() { | |
401 window.location.reload( true ); | |
402 }, r.info_time ); | |
403 } | |
404 } | |
405 } ); | |
406 } ); | |
407 } ); | |
408 | |
409 // Admin section, Hide | |
410 document.querySelectorAll( "[data-am-sw]" ).forEach( function( button ) { | |
411 button.addEventListener( "click", function( e ) { | |
412 let el = this.closest( "[data-am-item]" ); | |
413 let data = { | |
414 fn: "admin_menu_hide", | |
415 type: el.getAttribute( "data-am-type" ), | |
416 module: el.getAttribute( "data-am-module" ), | |
417 item: el.getAttribute( "data-am-item" ), | |
418 hide: el.classList.contains( "showed" ), | |
419 } | |
420 if ( data.item == "admin_menu" ) { | |
421 if ( ! confirm( _( "hide_admin_settings" ) ) ) return false; | |
422 } | |
423 api( data, function( r ) { | |
424 if ( r.ok == "true" ) { | |
425 window.location.reload( true ); | |
426 } | |
427 } ); | |
428 } ); | |
429 } ); | |
430 | |
431 // Admin section, Add Section | |
432 document.querySelectorAll( "#admin_menu .main-footer .add-section" ).forEach( function( button ) { | |
433 button.addEventListener( "click", function( e ) { | |
434 api( { fn: "admin_menu_add_section" }, function( r ) { | |
435 if ( r.info_text ) { | |
436 notify( r.info_text, r.info_class, r.info_time ); | |
437 if ( r.info_time ) | |
438 setTimeout( function() { | |
439 window.location.reload( true ); | |
440 }, r.info_time ); | |
441 } | |
442 } ); | |
443 } ); | |
444 } ); | |
445 | |
446 | |
447 // Reset all items in Admin Menu | |
448 document.querySelectorAll( "#admin_menu .main-footer .reset-all" ).forEach( function( button ) { | |
449 button.addEventListener( "click", function( e ) { | |
450 api( { fn: "reset_admin_menu_items" }, function( r ) { | |
451 if ( r.info_text ) { | |
452 notify( r.info_text, r.info_class, r.info_time ); | |
453 if ( r.info_time ) | |
454 setTimeout( function() { | |
455 window.location.reload( true ); | |
456 }, r.info_time ); | |
457 } | |
458 } ); | |
459 } ); | |
460 } ); | |
461 | |
462 | |
463 // Disable Modules | |
464 document.querySelectorAll( "#modules .module-sw-btn" ).forEach( function( button ) { | |
465 button.addEventListener( "click", function( e ) { | |
466 let closest = this.closest( "[data-module]" ); | |
467 let data = { | |
468 fn: "module_disable", | |
469 disable: closest.classList.contains( "enabled" ), | |
470 module: closest.getAttribute( "data-module" ), | |
471 } | |
472 api( data, function( r ) { | |
473 if ( r.info_text ) { | |
474 notify( r.info_text, r.info_class, r.info_time ); | |
475 } else { | |
476 window.location.reload( true ); | |
477 } | |
478 } ); | |
479 } ); | |
480 } ); | |
481 | |
482 // Delete Module | |
483 document.querySelectorAll( "#modules .module-del-btn" ).forEach( function( button ) { | |
484 button.addEventListener( "click", function( e ) { | |
485 let module = this.closest( "[data-module]" ).getAttribute( "data-module" ); | |
486 let data = { | |
487 fn: "module_del", | |
488 module: module, | |
489 } | |
490 api( data, function( r ) { | |
491 if ( r.info_text ) { | |
492 notify( r.info_text, r.info_class, r.info_time ); | |
493 } | |
494 } ); | |
495 } ); | |
496 } ); | |
497 | |
498 // Close Sessions | |
499 document.querySelectorAll( "#auth [data-login]" ).forEach( function( button ) { | |
500 button.addEventListener( "click", function( e ) { | |
501 e.preventDefault(); | |
502 if ( ! confirm( _( "confirm_logout" ) ) ) return; | |
503 let parent = button.parentElement; | |
504 var data = { | |
505 fn: "logout", | |
506 sess: this.getAttribute( "data-login" ), | |
507 } | |
508 api( data, function( r ) { | |
509 if ( r.info_text ) { | |
510 notify( r.info_text, r.info_class, r.info_time ); | |
511 if ( r.result == "refresh" ) { | |
512 // refresh приходит если мы закрываем свою сессию | |
513 window.location.reload( true ); | |
514 } else if ( r.result == "ok" ) { | |
515 // Перемещаем сессию в закрытые путем копирования html, | |
516 // предварительно удалив класс del-sess чтобы не отображался крестик. | |
517 // А старый элемент удаляем. | |
518 // При копировании html так же удаляется привязанная функция. | |
519 button.classList.remove( "del-sess" ); | |
520 let html = parent.outerHTML; | |
521 parent.remove(); | |
522 document.querySelector( ".history-sess .sess-table" ).insertAdjacentHTML( "afterbegin", html ); | |
523 } | |
524 } | |
525 } ); | |
526 } ); | |
527 } ); | |
528 | |
529 // Show/Hide password | |
530 document.querySelectorAll( ".password-eye" ).forEach( function( eye ) { | |
531 eye.addEventListener( "click", function( e ) { | |
532 this.classList.toggle( "showed" ); | |
533 let inp = this.previousElementSibling; | |
534 let t = inp.getAttribute( "type" ); | |
535 if ( t == "password" ) { | |
536 inp.setAttribute( "type", "text" ); | |
537 } else { | |
538 inp.setAttribute( "type", "password" ); | |
539 } | |
540 inp.focus(); | |
541 } ); | |
542 } ); | |
543 | |
544 // Install module (upload module) | |
545 let input = document.querySelector( "#module-upload" ); | |
546 if ( input ) input.addEventListener( "change", async function( e ) { | |
547 const formData = new FormData(); | |
548 formData.append( "fn", "install_module" ); | |
549 for ( let i = 0; i < input.files.length; i++ ) { | |
550 formData.append( "myfile[]", input.files[i] ); | |
551 } | |
552 let ajax = new XMLHttpRequest(); | |
553 /* | |
554 ajax.upload.addEventListener( "progress", function( event ) { | |
555 let percent = Math.round( (event.loaded / event.total) * 100 ); | |
556 bar.style.width = percent + "%"; | |
557 }, false ); | |
558 | |
559 ajax.addEventListener( "error", function( event ) { | |
560 notify( _( "error_upload_file" ), "info-error", 3600000 ); | |
561 bar.style = ""; | |
562 }, false ); | |
563 | |
564 ajax.addEventListener( "abort", function( event ) { | |
565 notify( _( "error_upload_file" ), "info-error", 3600000 ); | |
566 bar.style = ""; | |
567 }, false ); | |
568 */ | |
569 let google_chrome_fix = this; | |
570 ajax.addEventListener( "load", function( event ) { | |
571 google_chrome_fix.value = ""; | |
572 let r = JSON.parse( event.target.responseText ); | |
573 if ( r.info_text ) { | |
574 notify( r.info_text, r.info_class, r.info_time ); | |
575 if ( r.info_class === "info-success" ) setTimeout( function() { | |
576 window.location.reload( true ); | |
577 }, r.info_time ); | |
578 } | |
579 } ); | |
580 ajax.open( "POST", cms.api ); | |
581 ajax.send( formData ); | |
582 } ); | |
583 | |
584 // БД. Открытие дополнительных настроек. | |
585 document.querySelectorAll( "#base .pro-btn" ).forEach( function( pro ) { | |
586 pro.addEventListener( "click", function( e ) { | |
587 document.querySelector( "#base form" ).classList.toggle( "show-pro" ); | |
588 } ); | |
589 } ); | |
590 | |
591 // Выбор секции Админского меню. Select | |
592 document.querySelectorAll( "#admin_menu .field-select" ).forEach( function( select ) { | |
593 select.addEventListener( "click", function( e ) { | |
594 e.stopPropagation(); | |
595 this.parentElement.classList.toggle( "open" ); | |
596 | |
597 // это можно убрать если переставить стили на родителя | |
598 select.nextElementSibling.classList.toggle( "open" ); | |
599 } ); | |
600 } ); | |
601 | |
602 // Выбор секции Админского меню. Option | |
603 document.querySelectorAll( "#admin_menu .field-options .option" ).forEach( function( select ) { | |
604 select.addEventListener( "click", function( e ) { | |
605 let input = this.closest( ".section-select-grid" ).querySelector( ".field-select" ); | |
606 input.querySelector( ".value" ).innerText = this.innerText; | |
607 input.setAttribute( "data-section", this.getAttribute( "value" ) ); | |
608 } ); | |
609 } ); | |
610 // Select | |
611 // Закрытие выпадающих списков при кликах вне их, а так же по ним | |
612 document.body.addEventListener( "click", function( e ) { | |
613 document.querySelectorAll( "#admin_menu .section-select-grid" ).forEach( function( list ) { | |
614 list.classList.remove( "open" ); | |
615 } ); | |
616 | |
617 // это можно убрать если переставить стили на родителя | |
618 document.querySelectorAll( "#admin_menu .field-options" ).forEach( function( list ) { | |
619 list.classList.remove( "open" ); | |
620 } ); | |
621 } ); | |
622 | |
623 | |
624 /* Обновление и проверка файлов */ | |
625 | |
626 document.querySelectorAll( "#modules [data-fn]" ).forEach( function( button ) { | |
627 button.addEventListener( "click", function() { | |
628 let fn = this.getAttribute( "data-fn" ); | |
629 let data = { | |
630 fn: fn | |
631 } | |
632 api( data, function( r ) { | |
633 if ( r.answer ) { | |
634 switch ( fn ) { | |
635 case "cms_update": | |
636 document.querySelector( "#modules .update-window" ).insertAdjacentHTML( "beforeend", r.answer ); | |
637 break; | |
638 case "create_zip": | |
639 document.querySelector( "#modules .dev-window" ).insertAdjacentHTML( "beforeend", r.answer ); | |
640 break; | |
641 case "cms_check_update": | |
642 case "cms_check_dev_update": | |
643 document.querySelector( "#modules .check-answer" ).innerHTML = r.answer; | |
644 break; | |
645 case "cms_changed_files": | |
646 let div = document.querySelector( "#modules .changed-files" ); | |
647 div.innerHTML = r.answer; | |
648 div.style.display = ""; | |
649 break; | |
650 } | |
651 } | |
652 if ( r.info_text ) { | |
653 notify( r.info_text, r.info_class, r.info_time ); | |
654 } | |
655 if ( r.reload ) { | |
656 setTimeout( function() { | |
657 window.location.reload( true ); | |
658 }, r.info_time ); | |
659 } | |
660 } ); | |
661 } ); | |
662 } ); | |
663 | |
664 // show update from dev buttons | |
665 document.querySelectorAll( "#modules [data-show-dev]" ).forEach( function( btn ) { | |
666 btn.addEventListener( "click", function() { | |
667 let dev = document.querySelector( "#modules .developers_only" ); | |
668 if ( dev ) { | |
669 dev.classList.remove( "developers_only" ); | |
670 if ( window.location.host == "dev.coffee-cms.ru" ) { | |
671 dev.querySelector( "#modules [data-fn='create_zip']" ).removeAttribute( "style" ); | |
672 } | |
673 } | |
674 this.remove(); | |
675 } ); | |
676 } ); | |
677 | |
678 } ); |