0
|
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 } );
|