newhelper-js 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ BSD 2-Clause License
2
+
3
+ Copyright (c) 2026, MIOBOMB
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README-en.md ADDED
@@ -0,0 +1,17 @@
1
+ # newHelper.js
2
+ - [русская версия](README.md)
3
+ - A library for creating ultra-lightweight yet highly functional admin panels, one of the best examples of Object Hub. The total bundle of the entire site is only 280 kb of pure code, and the internal admin panel with all HTML+CSS+JS weighs only 25 kilobytes (with gzip, the size is even smaller). I try to keep the library in a "Modular Monolith" state.
4
+ Current version - 2.1, 13.6kb
5
+
6
+ # Features
7
+ - The library uses a classic connection with a script tag and requires no bundlers/compilers. But if you really want to, you can configure it, but why? It's the assembly language of the web world.
8
+ - Dependencies? Nope, it's vanilla ES6.
9
+ - The library size mentioned at the beginning is the size of the source code, so minification + gzip will reduce the size even further.
10
+ - Due to the minimalistic code, you can freely override any built-in method to suit your needs. In extreme cases, you can resort to modifying the library's code, but we don't recommend this if you plan to update to the latest versions.
11
+ - All the code is written inside a single "_" and uses only three global event listeners. Because of this, conflicts with other libraries are minimal. If you're willing, you can try running newHelper alongside jQuery, or if you really want reactivity, try it in combination with ultra-light reactive frameworks (the only decent one I know of is alpine.js).
12
+ - If you're a follower of "14-kilobyte sites," you can safely run the library through a minifier. The minified code of the library shouldn't weigh more than 5 kilobytes, and if you discard modules you don't need, even less (for example, the window engine code makes up approximately 50% of the entire library code; if you don't need the window engine, you're left with a 2-kilobyte gzip core).
13
+ - If you learn to use lazy loading properly, your already lightweight sites will become many times lighter (for instance, the initial traffic of Object Hub decreased from 330kb to 180kb at startup due to splitting everything into lazyload modules).
14
+ - A couple of my sites not related to admin panels are based on this library - GDPS Helper (a catalog of private Geometry Dash servers, but it also has an admin panel for demon lists), Object Hub (a wiki-engine-like catalog and HR platform for the OSC community; the wiki engine heavily uses windows instead of MediaWiki crutches). If you want, you can not only assemble an admin panel in 2 hours but also write a mega-site in a couple of weeks.
15
+ - The library was born 3 years ago as an internal tool for the GDPS Helper website. It evolved due to my disdain for the heaviness of React. Later, the GDPS Helper source code became the foundation for Object Hub, during the development of which the window engine, hotkeys, and more powerful lazy loading appeared.
16
+ - As a developer, I try to follow the Unix philosophy. For example, simplicity over complexity - there are only 600 lines of code. Or modularity - even though my modules are slightly interconnected, each module is responsible for its own thing.
17
+ - Continuing the previous point, you can use newHelper.js anywhere and everywhere. Its application is limited only by your imagination. Want to make a cool admin panel? The library is aimed at that. Want to write a full-fledged web forum? If you still remember GDPS Helper or Object Hub, it's entirely possible. Want to write your own frontend for the Yandex API? If you have a lot of time, please go ahead.
package/README.md ADDED
@@ -0,0 +1,17 @@
1
+ # newHelper.js
2
+ - [english version](README-en.md)
3
+ - Библиотека для создания сверхлёгких но очень функциональных админ панелей, один из лучших примеров Object hub, Общий бандл всего сайта составляет всего 280 кб чистого кода, внутренная админ панель со всем html+css+js весит всего 25 килобайт (при gzip размер ещё ниже). Я стараюсь придерживать библиотеку в состоянии "Модульного монолита".
4
+ Текущая версия - 2.1, 13.6кб
5
+
6
+ Особенности
7
+ - Библиотека использует классическое подключение с тегом script и не требует никаких сборщиков/компиляторов. Но если очень хочется вы можете настроить его, но зачем? это же ассемблер из мира веб
8
+ - Зависимости? неа, это ванильный ES6
9
+ - Указанный в начале размер библиотеки является размером исходного кода, так что минификация+gzip уменьшат размер ещё сильнее
10
+ - Из-за минималистичности кода вы можете спокойно переопределить любой встроенный метод под свои нужны. В крайнем случае можно прибегнуть к модификации кода библиотеки, но такое мы не рекомендуем делать в случае если вы собираетесь обновляться до самых последних версий
11
+ - Весь код написан внутри одного "_" и использованием всего трёх глобальных читателей событий. Из-за этого конфликты с остальными библиотеками минимальны, если вы готовы то можете пробовать запустить newHelper вместе с jQuery или если вы капец как хотите реактивность то пробуйте в связке с ультралёгкими реактивными фреймворками (у меня на слуху из адекватных alpine.js)
12
+ - Если вы последователь "14 килобайтных сайтов" то можете смело прогнать библиотеку через минификатор, минифицированный код библиотеки не должен весить больше 5 килобайт, а если отбросить ненужные вам модули и то меньше (например код движка окон составляет приблизительно 50% всего кода библиотеки, если вам не нужен движок окон - оставляйте 2 килобайта gzip-ядра)
13
+ - Если вы научитесь грамотно использовать ленивую загрузку, то ваши и без того лёгкие сайты станут в разы легче (тот же стартовый трафик Object Hub уменьшился 330кб до 180кб при старте из-за разбития всего на lazyload модули)
14
+ - На базе этой библиотеки работает парочка моих сайтов не связанных с админ панелями - GDPS Helper (каталог приватных серверов geometry dash, но там тоже идёт админка для демонлистов), Object hub (каталог с элементами вики движка и hr-платформы для OSC сообщества, вики движок во всю использует окна вместо костылей mediawiki). Если захотеть можно не только админку за 2 часа собрать, но и написать мегасайт за пару недель
15
+ - Библиотека родилась 3 года назад как внутренний инструмент веб сайта GDPS Helper, развивалась она из-за презрения к тяжеловесности react, позже исходный код GDPS Helper стал основоположником Object Hub, в процессе развития которого как раз таки и появились движок окон, горячих клавиш, и более мощная ленивая загрузка
16
+ - Я как разработчик стараюсь следовать философии Unix систем, как пример простота превыше сложности - в коде всего 600 строк кода, или модульность, пусть у меня модули и чуть чуть связаны но каждый модуль отвечает за свою вещь
17
+ - Продолжая прошлый пункт, вы можете использовать newHelper.js везде и всюду, его применение ограничено лишь вашей фантазией, хотите сделать классную админку? либа на это и целится. хотите написать полноценный веб форум? если вы ещё помните GDPS Helper или Object hub это вполне возможно. хотите написать свой фронтенд к Яндекс API? если у вас много времени пожалуйста
package/docs.md ADDED
@@ -0,0 +1,283 @@
1
+ Базовая документация (если поможет)
2
+ - Внимание! документация предоставлена к внутренней версии 2.0.9, возможны расхождения с версией 2.1, рекомендуем сверяться с исходным кодом
3
+
4
+ # Роутер (_.link)
5
+ Роутер здесь завязан на особенном от популярных решений поведении. Берётся query строка, разбивается по "&", и из полученного массива дальше с этой строкой роутер работает обособленно. Сначала идёт элемент 0 который отвечает за страницы и вложенность, последующие элементы являются командами, на них вы можете навешать открытие окон, модалки, или просто всякие дебаг штучки (например отключение проверки наличия капчи при логине или смену языка)
6
+ ## API:
7
+ ### Конфиг:
8
+ - basePage() - fallback страница на случай если страницы не существует, рекомендую ставить клиентскую страницу "404"
9
+ - defTitle - отображаемое названия вкладки при открытии страницы
10
+ - actions - словарь функций с ссылками
11
+ - commands - словарь функций команд
12
+ ### Базовые методы:
13
+ - set(page, title) - устанавливает страницу
14
+ - add(cmd) - добавляет команду в ссылку (если есть повторка добавления не будет)
15
+ - remove(cmd) - удаляет команду из ссылки, если она есть
16
+ ### Внутренние методы/свойства:
17
+ - get() - обработать ссылку и вызвать нужные страницы и команды (допустим открыть ту или иную страницу, и вызвать модалки если их нет). Рекомендуется вызывать при старте приложения, автоматически срабатывает при событии popstate
18
+ - _i - флаг запрещающий функции set() ставить новую страницу, по умолчанию true, но после каждого вызова set() становится false
19
+ - _cmd - прослойка для сохранения команд между popstate событиями
20
+ - compileLink() - возвращает скомпилированную ссылку в формате ["ключ=значение","ключ=значение"]. Не рекомендуется к использованию вообще
21
+ ### Пример конфигурации (Object Hub):
22
+ ```
23
+ _.link.defTitle = 'Object Hub';
24
+ _.link.basePage = ()=>{innerMain(pageMain())};
25
+ _.lang.vars = {
26
+ 'helperStrVer':helperStrVer,
27
+ 'helperBuildNum':helperBuildNum,
28
+ 'helperUrl':helperUrl
29
+ };
30
+ _.link.commands = {
31
+ en: ()=> {_.lang.replace('EN',1)},
32
+ ru: ()=> {_.lang.replace('RU',1)},
33
+ ua: ()=> {_.lang.replace('UA',1)},
34
+ de: ()=> {_.lang.replace('DE',1)},
35
+ dev: ()=> {debugWindow()},
36
+ ignoreCap: ()=> {ignoreCap = true}
37
+ };
38
+ _.link.actions = {
39
+ '': ()=> {innerMain(pageMain())},
40
+ find: ()=> {pageFind()},
41
+
42
+ camp: (campId)=> {getCamp(campId)},
43
+ show: (showId)=> {getShow(showId)},
44
+ pere: (pereId)=> {getPere(pereId)},
45
+ news: ()=> {globalNews()},
46
+ 'news/': {
47
+ '': ()=> {globalNews()},
48
+ comms: (postId)=> {let d = postId.split('|');getNewsWithComments(d[0], d[1], d[2])},
49
+ list: (gdpsId)=> {let d = gdpsId.split('|');helperNews(d[0], d[1])},
50
+ },
51
+ about: ()=> {innerMain(helperAbout())},
52
+
53
+ profiles: (userId)=> {otherProfile(userId,'pageFind(0)')},
54
+ 'profiles/': {
55
+ '': (userId)=> {otherProfile(userId,'pageFind(0)')},
56
+ camps: (userId)=> {otherProfile(userId,'pageFind(0)',otherCampsWindow)},
57
+ shows: (userId)=> {otherProfile(userId,'pageFind(1)',otherShowsWindow)},
58
+ peres: (userId)=> {otherProfile(userId,'pageFind(2)',otherPeresWindow)},
59
+ wikis: (userId)=> {otherProfile(userId,'pageFind(0)',otherWikisWindow)},
60
+ },
61
+ };
62
+ ```
63
+
64
+ # Ленивый загрузчик (_.lazy)
65
+ Ленивый загрузчик скриптов позволяет подгружать ненужные прямо сейчас части кода потом (как пример, панель управления вики в object hub подгружается только когда пользователь открывает её). Также позволяет грузить любые newHelper.js-совместимые библиотеки, но об этом позже
66
+ ## API:
67
+ ### Базовые методы:
68
+ - load(url, ...args) - функция немедленной загрузки скрипта, как пример подгрузка капчи на object hub работает напрямую через неё. Возвращает промис, резолв которого происходит сразу после загрузки скрипта
69
+ - register(scr, funcs) - регистрация lazy функций в конфиге, где первым аргументом идёт ссылка на ленивый скрипт, а вторым массив функций-точек входа (названия функций должны идти как строки). При регистрации функций пишет в консоль какие функции были зарегистрированы. Вешайта функции-точки входа на window, иначе функцмя объявится не глобально и у вас будет ошибка!
70
+ ### Внутренние свойства/методы:
71
+ - _(scr, fn) - надстройка для register() которая является прокси для загрузки.
72
+ - loaded- объект с загруженными скриптами, каждая загруженная ссылка может быть промисом, что означает что она ещё загружается, или true
73
+ ### Пример конфигурации (и запуска сайта, Object Hub):
74
+ ```
75
+ _.lazy.register('./static/devpanel.js',[
76
+ 'debugWindow'
77
+ ]);
78
+ _.lazy.register('./static/publicWiki.js',[
79
+ 'pageGuides',
80
+ 'getGuide',
81
+ ]);
82
+ _.lazy.load('./static/ojhub.js')
83
+ .then(e=>{
84
+ reStart();
85
+ });
86
+ ```
87
+
88
+ # Локализация (_.lang)
89
+ Локализация в newHelper.js реализована довольно просто но мощно
90
+ ## API:
91
+ ### Конфиг:
92
+ - addr - строка с адресом на папку где хранятся все языковые пакеты, обязательно перезаписывать в конфиге
93
+ - vars - Объект подменяемых переменных, где в качестве ключа вы указываете нужную для подстановки переменную
94
+ - main - Словарь с языковым пакетом
95
+ ### Базовые методы:
96
+ - load(name) - скачивает языковой пакет
97
+ - parse(packet) - подменяет переменные из vars на их значения в пакете. Учтите что ваши ключи из vars должны быть обёрнуты знаками плюса (+переменная+)
98
+ - replace(name) - немедленно скачать языковой пакет и распарсив его перевести сайт
99
+ - win(i) - получить значение ключа пакета для окон
100
+ ### Методы получения значений пакета:
101
+ ВНИМАНИЕ! для краткости кода все методы ниже кроме from() самостоятельно формируют кусок html, принято такое решение было для краткости кода.
102
+ - from(i) - получить голое значение ключа или сам ключ если его нет
103
+ - text(i) - получить значение ключа пакета для обычных текстовых элементов
104
+ - submit(i) - получить значение ключа пакета для <input type=submit>
105
+ - input(i) - получить значение ключа пакета для обычного инпута
106
+ - textarea(i) - получить значение ключа пакета для обычной textarea
107
+ - img(i) - получить значение ключа пакета для картинки, где значением является адрес на картинку (будет полезно для создания меню языков, где языки будут флагами)
108
+ ### Пример конфига и использования:
109
+ ```
110
+ _.lang.addr = `./languages/`;
111
+ await _.lang.replace('RU'); // скачает `./languages/RU.json`
112
+ console.log(_.lang.main) // Object {hi:'Привет мир!', langImage:'./static/ru.png'}
113
+
114
+ console.log(`<h1${_.lang.text('hi')}/h1>`) // <h1 data-trans="hi">Привет мир!</h1>
115
+ console.log(`<img${_.lang.img('langImage')}>`) // <img data-trans="langImage" src="./static/ru.png">
116
+
117
+ await _.lang.replace('EN');
118
+
119
+ console.log(`<h1${_.lang.text('hi')}/h1>`) // <h1 data-trans="hi">Hello world!</h1>
120
+ console.log(`<img${_.lang.img('langImage')}>`) // <img data-trans="langImage" src="./static/en.png">
121
+ ```
122
+
123
+ # Кастомные горячие клавишы (_.hotkeys)
124
+ Горячие клавишы реализованы через систему "зажал отпустил", каждое действие вызывает колбек, но вы можете сделать и обычные хоткеи оставив дейсвтие "отпустил" пустым
125
+ ## API:
126
+ ### Конфиг:
127
+ - keys - Объект ключей, в теории его можно перезаписывать и устанавливать значение из localStorage, но на практике пока никто так не делал
128
+ - on(combo, press, release) - Зарегистрировать горячую клавишу на сочетание, принимает в качестве имени клавишы KeyboardEvent.code, комбинируется разделителем + ('ShiftLeft+KeyZ' для зажатия шифта и клавишы "я"), вторым аргументом идёт колбек на нажатие или зажатие, третьим аргументом идёт колбек на отжатие
129
+ - off(combo) - Удалить все хоткеи повешанные на комбинацию
130
+ Если вы хотите сделать хоткей который будет работать на простое нажатие, просто оставьте третий аргумент в on() пустым
131
+ ### Внутренние методы:
132
+ - _parse - распарсить комбинацию в массив клавиш
133
+ - _match - проверить зажаты ли нужные клавишы
134
+ - _init - запустить движок путём навешивания чтения событий для keyDown и keyUp, плюсом идёт событие 'blur' которое отключает все запущенные хоткеи
135
+ - _i - внутренная переменная для _init которая не даёт запустить движок дважды
136
+ ### Пример конфига (Object hub):
137
+ ```
138
+ _.hotkeys.on('ShiftLeft + F1', debugWindow);
139
+ _.hotkeys.on(
140
+ 'ShiftLeft+KeyZ',
141
+ ()=>keyBindsList(),
142
+ ()=>_.$.qa('[keybindslist]').forEach(e=>_.win.close(e.id))
143
+ );
144
+ ```
145
+
146
+
147
+ # Движок окон (_.win)
148
+ Вот она главная фишка библиотеки! Учтите что мы НЕ советуем вам переопределять встроенные методы, т.к. у движка окон ещё большой задел под оптимизацию кода или расширение функционала, никто же ведь не хочет словить краш на старте вашего сайта из-за появления возможности переименовывать окна?
149
+ ## API:
150
+ ### Конфиг:
151
+ - manager - DOM элемент с открытыми окнами
152
+ - hider - DOM элемент с открытыми окнами
153
+ - winAttrs - Атрибуты открытых окон, рекомендуем указывать класс с анимацией окна и стилии
154
+ - dragAttrs - Атрибуты элемента драга
155
+ - titleAttrs - Атрибуты назнвания окна
156
+ - renameAttrs - Атрибуты инпута при переименовании окна
157
+ - btnAttrs - Атрибуты кнопок окон, рекомендую отключать задний фон, желательно через классы
158
+ - defBtns - Кастомные кнопки, по умолчанию уже имеет кнопки скрытия, разворота на весь экран и закрытия
159
+ - hiderAttrs - Атрибуты кнопок для раскрытия свёрнутых окон
160
+ - animOpen - Название класса с анимацией открытия
161
+ - animClose - Название класса с анимацией закрытия
162
+ - animHide - Название класса с анимацией сворачивания окна
163
+ - animShow - Название класса с анимацией разворачивания окна
164
+ - animFullOn - Название класса с входом в режим полного окна
165
+ - animFullOff - Название класса с выходом из режима полного окна
166
+ ### Базовые методы:
167
+ - open(name, content, customAttrs) - Открыть окно
168
+ - toggleFull(winId) - развернуть окно на весь экран, если уже развернуто вернуть в исходное состояние
169
+ - setTitle(winId, newTitle) - Переименовать окно. Осторожно, при переименовании окна смена языка не сможет изменить название окна
170
+ - close(winId) - закрыть окно
171
+ - hide(winId) - скрыть окно и создать "хайдер"
172
+ - show(winId) - развернуть окно и удалить "хайдер"
173
+ ### Внутренние методы на this:
174
+ - _winBtn(winIdtext, func) - кнопка окна, в будущем функционал кнопок будет расширен для создания кастомных кнопок
175
+ - _hiderBtn(text) - кнопка для разворота окна
176
+ - _initWin() - запустить драггер окна
177
+ - _ID() - сгенерировать уникальный айди окна
178
+ Внешние свойства:
179
+ - _.wins - коллекция открытых окон
180
+
181
+ # Ошибки (_.err)
182
+ Ошибки обрабатываются всегда и глобально несмотря ни на что, только если они были пойманы обработчиками. Рекомендую загружать другие библиотеки через ленивую загрузку (обычным load() прямо в конфиге()) чтобы потом модуль ошибок не упадал в саморекурсию из-за отсутствия в стеке разделителя "&helper:"
183
+ ## API:
184
+ ### Базовые свойства/методы:
185
+ - errors - объект со всеми ошибками случившимися за сессию
186
+ - _c - счётчик ошибок
187
+ - log(err, addr) - сохраняет ошибку и добавляет к счётчику единичку
188
+ ### Методы отлова (по умолчанию глобальные, имеют возможность находить точную строку ошибки):
189
+ - handleGlobal(message, source, line, column, error) - словить обычную ошибку и сохранить её
190
+ - handleRejection(e) - словить отклонение промиса если не было поймано
191
+ ### Конфиг:
192
+ - print(errID, err, addr) - ваш кастомный обработчик ошибок. Мы вам очень рекомендуем для дев среды назначить вот такой обработчик:
193
+ ```js
194
+ let windowBtns = [
195
+ ['COPY ERROR', `navigator.clipboard.writeText(_.$.id('errText{errID}').innerText)`],
196
+ ['FULL RESTART', `location.reload()`],
197
+ // ваши прочие нужные методы, например перезапуск приложения без перезагрузки страницы, или сброс storage и перезапуск приложения
198
+ ];
199
+ _.err.print = (errID, errText, addr = '')=>{
200
+ let buttonErr = (i, clck)=>`<button style=background-color:#333 onclick="${clck}">${i}</button> `,
201
+ buttons = '',
202
+ html = _.html`<div id=debug${errID}>
203
+ <p align=center>DEBUG INFO</p>
204
+ ERROR<br>
205
+ <pre style=width:100%;white-space:pre-line id=debugMega${errID}></pre>
206
+ <br><br>
207
+ <center id=windows${errID}>
208
+ </center>
209
+ </div>`;
210
+ console.log(html);
211
+ _.$.q('#debugMega'+errID, html).textContent = `LOCATION: ${location}\n`+errText+`\n`+
212
+ (addr === '' ? '' : `\n${addr}`);
213
+
214
+ windowBtns.forEach(btn=>{
215
+ buttons += buttonErr(btn[0], btn[1].replaceAll('{errID}', errID));
216
+ });
217
+ _.$.q('#windows'+errID, html).innerHTML = buttons;
218
+
219
+ let winId = _.win.open('debug'+errID,
220
+ html
221
+ , `iserror style=width:300px;height:350px`);
222
+ ```
223
+
224
+ # Прочие утилиты, которым не особо нужно описание:
225
+ ## http-клиент на базе xhr
226
+ - defaultHeaders - Хедеры которые всегда будут назначаться при запросах, вы можете назначить туда токен пользователя
227
+ - req(method, url, data, headers, fileProgressElement) - сделать запрос с различным методом, fileProgressElement требуется указывать лишь в случае если нужно отслеживать прогресс загрузки файла, не рекомендуется задавать свойства min и max
228
+ ## DOM утилиты:
229
+ - _.$.q(i,p=document), алиас к document.querySelector(i,p), где p может быть любым DOM пространством
230
+ - _.$.qa, алиас к document.querySelectorAll(i,p), где p может быть любым DOM пространством
231
+ - _.$.id(i) - алиас к document.getElementById(i), у неё нет аргумента p так как он не работает в других пространствах имён
232
+ - _.html - превратить шаблонную строку в DOM элемент(ы)
233
+ - _.storage - обычная изолирующая обёртка над классом Storage, имеет почти идентичные методы, разве что прямое обращение и перезапись недоступны
234
+
235
+ # Плагины?..
236
+ Да! и ещё как! поскольку newHelper.js является обычным набором javascript инструментов, помещённых в объект "_" вы можете спокойно загрузить вообще любой скрипт через _.lazy, даже не просто загрузить а повешать на лень если они вам не нужны прямо сейчас!
237
+
238
+ # Чеклист конфигов
239
+ _.link
240
+ - basePage() - fallback страница на случай если страницы не существует
241
+ - defTitle - отображаемое названия вкладки при открытии страницы
242
+ - actions - словарь функций с ссылками
243
+ - commands - словарь функций команд
244
+
245
+ _.lazy
246
+ - load(url, ...args) - функция немедленной загрузки скрипта
247
+ - register(scr, funcs) - регистрация lazy функций в конфиге
248
+
249
+ _.lang
250
+ - addr - строка с адресом на папку где хранятся все языковые пакеты
251
+ - vars - Объект подменяемых переменных
252
+ - main - Словарь с языковым пакетом
253
+
254
+ _.hotkeys
255
+ - keys - Объект ключей
256
+ - on(combo, press, release) - Зарегистрировать горячую клавишу на сочетание
257
+ - off(combo) - Удалить все хоткеи повешанные на комбинацию
258
+
259
+ _.err
260
+ - print(errID, err, addr) - ваш кастомный обработчик ошибок
261
+
262
+ _.http
263
+ - defaultHeaders
264
+
265
+ _.storage
266
+ сам себе конфиг
267
+
268
+ _.win
269
+ - manager - DOM элемент с открытыми окнами
270
+ - hider - DOM элемент с открытыми окнами
271
+ - winAttrs - Атрибуты открытых окон
272
+ - dragAttrs - Атрибуты элемента драга
273
+ - titleAttrs - Атрибуты назнвания окна
274
+ - renameAttrs - Атрибуты инпута при переименовании окна
275
+ - btnAttrs - Атрибуты кнопок окон
276
+ - defBtns - Кастомные кнопки, по умолчанию уже имеет кнопки
277
+ - hiderAttrs - Атрибуты кнопок для раскрытия свёрнутых окон
278
+ - animOpen - Название класса с анимацией открытия
279
+ - animClose - Название класса с анимацией закрытия
280
+ - animHide - Название класса с анимацией сворачивания окна
281
+ - animShow - Название класса с анимацией разворачивания окна
282
+ - animFullOn - Название класса с входом в режим полного окна
283
+ - animFullOff - Название класса с выходом из режима полного окна
package/history.md ADDED
@@ -0,0 +1,31 @@
1
+ История newHelper.js
2
+ # 1.7
3
+ ### 01:06:2024 как часть GDPS Helper
4
+ Всё берётся с GDPS Helper 1.6. Само по себе обновление 1.6 дало мне базу работы с javascript чтобы я потом выпустил 1.7, которая уже реально использует наработки ставшие первой весией newHelper.js:
5
+ - helperRequest (нынеший _.http)
6
+ - примитивная интерационализация, с сохранением всех пакетов в глобальном объекте (нынешний _.lang)
7
+
8
+ # 1.8
9
+ ### 24:08:2024 как часть GDPS Helper
10
+ Эта версия привнесла обновленную систему локализации, она стала походить на нынешний _.lang из-за скачивания json с сервера
11
+
12
+ # 1.9
13
+ ### 17:11:2024 как часть GDPS Helper
14
+ - Появилася helperStorage, класс-обёртка над localStorage (нынешний _.storage, но тогда принимал только localStorage)
15
+ - Добавлена функция loadScript, которая позже станет частью _.lazy.load
16
+
17
+ # 2.0
18
+ ### 20:05:2025 как часть Object hub
19
+ - Инкапсуляция всего в $ (ранее было 30+ глобальных переменных в коде)
20
+ - Движок окон в почти полном его виде
21
+ - глобальный error handler который указывает точную строку с ошибкой (иногда может работать не совсем правильно)
22
+ - самописная и лёгкая система lazy load
23
+ - Создание конфиг скрипта в котором можно изменять некоторые базовые значения newHelper.js без прямой перезаписи функций
24
+
25
+ # 2.1
26
+ ### 23:02:2026 как самостоятельная библиотека
27
+ - Перенос $ в _
28
+ - Дополнение движка окон возможностью переименовать окно
29
+ - Движок горячих клавиш
30
+ - DOM хелперы (_.$, _.http)
31
+ - Минимизация размера кода (2.0.1 29.4 кб => 2.1 13.6 кб)
package/newHelper.js ADDED
@@ -0,0 +1,603 @@
1
+ _={};
2
+ _.ver='2.1';
3
+ _.link={
4
+ basePage:()=>{},
5
+ defTitle:'',
6
+ actions:{},
7
+ commands:{},
8
+ _cmd:[],
9
+
10
+ _i: true,
11
+ compile:()=>location.search.replace('?','').split('&'),
12
+ set(page,title=_.link.defTitle){
13
+ if (title) _.$.D.title=title;
14
+ if (!this._i){
15
+ let link=this.compile();
16
+ link[0]=page;
17
+ history.pushState(null,null,'?'+link.join('&'));
18
+ }
19
+ this._i=false;
20
+ },
21
+ add(cmd){
22
+ let link=this.compile();
23
+ if (!link.includes(cmd)){
24
+ link.push(cmd);
25
+ this._cmd.push(cmd);
26
+ history.replaceState(null,null,'?'+link.join('&'));
27
+ }
28
+ },
29
+ remove(cmd){
30
+ let link=this.compile();
31
+ if (link.includes(cmd)){
32
+ let c=this._cmd;
33
+ link.splice(link.indexOf(cmd),1);
34
+ c.splice(c.indexOf(cmd),1);
35
+ history.replaceState(null,null,'?'+link.join('&'));
36
+ }
37
+ },
38
+ get(){
39
+ let cfg=_.link,
40
+ links=this.compile(),
41
+ [fKey,fVal]=links[0].split('='),
42
+ cmds=links.slice(1);
43
+ try {
44
+ let dirs=fKey.split('/'),
45
+ dir=cfg.actions,
46
+ main=dir[fKey];
47
+ if (!fKey.includes('/')){
48
+ main(fVal);
49
+ } else {
50
+ for (let p of dirs){
51
+ let kDir=dir[p+'/'];
52
+ if (kDir)
53
+ dir=kDir;
54
+ else{
55
+ dir[p](fVal);
56
+ break;
57
+ }
58
+ }
59
+ }
60
+ }catch(e){
61
+ _.link.basePage();
62
+ throw e;
63
+ }
64
+ _.link._cmd=cmds;
65
+ cmds.forEach(kPre=>{
66
+ let [key,val]=kPre.split('=');
67
+ let cmd=cfg.commands[key];
68
+ if (cmd)
69
+ cmd(val);
70
+ });
71
+ },
72
+ };
73
+ _.lazy={
74
+ loaded:{},
75
+ load(url,...args){
76
+ let key=url.split('?')[0],
77
+ state=this.loaded,
78
+ c=state[key];
79
+
80
+ if (c=== true) return Promise.resolve(args);
81
+ if (c instanceof Promise) return c.then(()=>args);
82
+
83
+ let pr=new Promise((resolve,reject)=>{
84
+ let scr=_.$.D.createElement('script');
85
+ scr.src=url;
86
+ scr.onload=()=>{
87
+ state[key]=true;
88
+ resolve(args);
89
+ };
90
+ scr.onerror=()=>{
91
+ delete state[key];
92
+ reject(new Error('Failed to load '+url));
93
+ };
94
+ _.$.D.head.append(scr);
95
+ });
96
+ state[key]=pr;
97
+ return pr;
98
+ },
99
+ register(scr,funcs){
100
+ if (!Array.isArray(funcs)) return new Error('Array required for register');
101
+
102
+ for (let fn of funcs)
103
+ window[fn]=(...a)=>_.lazy._(scr,fn).then(f=>f(...a));
104
+
105
+ console.info('_.lazy> Applied lazy '+scr+' with this functions:',funcs);
106
+ },
107
+ async _(scr,fn){
108
+ let w=window,
109
+ wrpr=w[fn];
110
+ try{await _.lazy.load(scr)}
111
+ catch(e){throw e}
112
+ if (wrpr!== w[fn]) return w[fn];
113
+ throw new Error(`Function ${fn} not loaded from ${scr}`);
114
+ },
115
+ };
116
+ _.lang={
117
+ addr:'',
118
+ vars:{},
119
+ main:{},
120
+
121
+ _:(i)=>` data-trans="${i}"`,
122
+ load(name){
123
+ return new Promise((resolve,reject)=>{
124
+ _.http.req('GET',_.lang.addr+name+'.json',false,{'Cache-Control':'no-cache,no-store,max-age=0'})
125
+ .then(data=>resolve(data));
126
+ })
127
+ },
128
+ parse:(packet,vars=_.lang.vars)=>
129
+ packet.replace(/\+([^+]+)\+/g,(match,key)=>{
130
+ let v=vars[key];
131
+ return v!== undefined ? v : match;
132
+ }),
133
+ async replace(name){
134
+ const p=await this.load(name);
135
+ _.lang.main=JSON.parse(this.parse(p));
136
+ for (let e of _.$.qa('[data-trans]')){
137
+ let key=e.dataset.trans,
138
+ text=_.lang.main[key] || `<code>_.lang.get('${key}')</code>`,
139
+ tag=e.tagName;
140
+
141
+ if (tag=== 'IMG') e.src=text;
142
+ else if (['INPUT','TEXTAREA'].includes(tag))
143
+ e[e.type=== 'submit' ? 'value' :'placeholder']=text;
144
+ else e.innerHTML=text;
145
+ }
146
+ return p;
147
+ },
148
+ from:i=>_.lang.main[i] || console.warn(`_.lang> ${i} is undefined`) || i,
149
+
150
+ text: i=>_.lang._(i)+`>${_.lang.from(i)}<`,
151
+ submit: i=>_.lang._(i)+`value="${_.lang.from(i)}">`,
152
+ input: i=>_.lang._(i)+`placeholder="${_.lang.from(i)}">`,
153
+ textarea: i=>_.lang._(i)+`placeholder="${_.lang.from(i)}"><`,
154
+ img: i=>_.lang._(i)+`src="${_.lang.from(i)}"`,
155
+ win(i){
156
+ let text=_.lang.from(i),
157
+ dT=_.lang._(i);
158
+ if (text== null || text== ''){
159
+ text=i;
160
+ dT='';
161
+ }
162
+ return `${dT}>${text}<`;
163
+ },
164
+ };
165
+ _.http={
166
+ defaultHeaders:{},
167
+ req(method,url,data='',headers={},fileProgressElement=false){
168
+ return new Promise((resolve,reject)=>{
169
+ let xhr=new XMLHttpRequest();
170
+
171
+ xhr.open(method,url);
172
+
173
+ let allHeaders={..._.http.defaultHeaders,...headers};
174
+ for (let header in allHeaders)
175
+ xhr.setRequestHeader(header,allHeaders[header]);
176
+
177
+ if (fileProgressElement)
178
+ xhr.upload.onprogress=(e)=>{
179
+ if (e.lengthComputable){
180
+ let percentage=(e.loaded / e.total);
181
+ fileProgressElement.setAttribute('value',percentage);
182
+ }
183
+ };
184
+
185
+ xhr.onreadystatechange=()=>{
186
+ if (xhr.readyState=== 4)
187
+ if (xhr.status >= 200 && xhr.status < 300) resolve(xhr.response);
188
+ else reject(new Error(`${xhr.status} - ${xhr.statusText}`),xhr);
189
+ };
190
+ xhr.onerror=()=>reject(new Error('Network error'),xhr);
191
+
192
+ xhr.send(data);
193
+ });
194
+ },
195
+ };
196
+ _.$={
197
+ D: document,
198
+
199
+ id:(i)=>_.$.D.getElementById(i),
200
+ q:(i,p=_.$.D)=>p.querySelector(i),
201
+ qa:(i,p=_.$.D)=>p.querySelectorAll(i),
202
+
203
+ on:(el,ev,fn,opts)=>el.addEventListener(ev,fn,opts),
204
+ off:(el,ev,fn,opts)=>el.removeEventListener(ev,fn,opts),
205
+ };
206
+ _.html=(strs,...args)=>{
207
+ let strF=[];
208
+ for (let i=0; i < args.length; i++)
209
+ strF.push(strs[i],args[i]);
210
+ strF.push(strs[strs.length - 1]);
211
+ strF=strF.join('').trim().replace(/\s+/g,' ');
212
+
213
+ const template=_.$.D.createElement('template');
214
+ template.innerHTML=strF;
215
+
216
+ const content=template.content;
217
+ if (content.children.length=== 1)
218
+ return content.firstChild;
219
+ return content;
220
+ };
221
+ _.storage=class{
222
+ constructor(strg,name){
223
+ this._=strg;
224
+ this.n=name;
225
+ }
226
+ get=(key)=>this._.getItem(this.n+key);
227
+ set=(key,value)=>this._.setItem(this.n+key,value);
228
+ remove=(key)=>this._.removeItem(this.n+key);
229
+ clear=()=>Object.keys(this._)
230
+ .filter(k=>k.startsWith(this.n))
231
+ .forEach(k=>this._.removeItem(k));
232
+ };
233
+ _.err={
234
+ print:()=>{},
235
+
236
+ errors:{},
237
+ _c: 0,
238
+ log(err){
239
+ _.err.print(_.err._c,err);
240
+ _.err._c++;
241
+ _.err.errors[_.err._c]=err;
242
+ },
243
+ handleGlobal(message,source,line,column,error){
244
+ console.error(message,source+':'+line+':'+column,error)
245
+ _.err.log(message+`\n IN ${source} ON LINE ${line} IN COLUMN ${column}`);
246
+ },
247
+ handleRejection(e){
248
+ const err=e.reason || e;
249
+ console.error(err);
250
+ _.err.log(
251
+ `PROMISE ERROR\n`+
252
+ `${e.stack || e}`
253
+ );
254
+ },
255
+ };
256
+ _.hotkeys={
257
+ keys:{},
258
+ _holds:new Set(),
259
+ _:false,
260
+
261
+ _parse:combo=>combo.split('+').map(k=>k.trim()),
262
+ _match(keys) {
263
+ for (let k of keys) if (!this._holds.has(k)) return false;
264
+ return true;
265
+ },
266
+ _init() {
267
+ if (this._) return;
268
+ _.$.on(_.$.D,'keydown',e=>{
269
+ this._holds.add(e.code);
270
+
271
+ for (let combo in this.keys) {
272
+ let h=this.keys[combo];
273
+ if (!this._match(h.keys)) continue;
274
+
275
+ if (h.press && !h.active) {
276
+ h.active=true;
277
+ h.press(e);
278
+ }
279
+ }
280
+ });
281
+ _.$.on(_.$.D,'keyup',e=>{
282
+ this._holds.delete(e.code);
283
+
284
+ for (let combo in this.keys) {
285
+ let h=this.keys[combo];
286
+ if (h.active && !this._match(h.keys)) {
287
+ h.active=false;
288
+ h.release(e);
289
+ }
290
+ }
291
+ });
292
+ _.$.on(window,'blur',()=>{
293
+ for (let combo in this.keys) {
294
+ let h=this.keys[combo];
295
+ if (h.active) {
296
+ h.active=false;
297
+ h.release();
298
+ }
299
+ }
300
+ this._holds.clear();
301
+ });
302
+ this._=true;
303
+ },
304
+ on(combo,press,release) {
305
+ this._init();
306
+ let keys=this._parse(combo);
307
+
308
+ this.keys[combo]={
309
+ keys,
310
+ press: press || (()=>{}),
311
+ release: release || (()=>{}),
312
+ active: false
313
+ };
314
+
315
+ return this;
316
+ },
317
+ off(combo) {
318
+ delete this.keys[combo];
319
+ return this;
320
+ }
321
+ };
322
+ _.win={
323
+ manager:false,
324
+ hider:false,
325
+
326
+ winAttrs:'',
327
+ dragAttrs:'',
328
+ titleAttrs:'',
329
+ renameAttrs:'',
330
+ btnAttrs:'',
331
+ hiderAttrs:'',
332
+
333
+ defBtns:[
334
+ ['–',w=>_.win.hide(w)],
335
+ ['=',w=>_.win.toggleFull(w)],
336
+ ['X',w=>_.win.close(w)],
337
+ ],
338
+
339
+ animOpen:'',
340
+ animClose:'',
341
+ animHide:'',
342
+ animShow:'',
343
+ animFullOn:'',
344
+ animFullOff:'',
345
+
346
+ _ID(){
347
+ let id;
348
+ do id=Math.random().toString(36).substring(2,8);
349
+ while (_.wins[id]);
350
+ return id;
351
+ },
352
+ _winBtn(win,text,func){
353
+ let b=_.html`<button ${_.win.btnAttrs}>${text}</button>`;
354
+ _.$.on(b,'click',()=>func(win));
355
+ return b;
356
+ },
357
+ _hiderBtn(win){
358
+ let title=win.langs!== false ? _.lang.win('WINDOW-'+win.langs) : `>${win.name}<`,
359
+ b=_.html`<button id=hider${win.id} ${_.win.hiderAttrs}${title}/button>`;
360
+ _.$.on(b,'click',()=>_.win.show(win));
361
+ return b;
362
+ },
363
+ _initWin(win){
364
+ let wEl=win.elem,
365
+ x1=0,y1=0,x2=0,y2=0,
366
+ startW=e=>{
367
+ let targ=e.target;
368
+ if (['BUTTON','INPUT'].includes(targ.tagName) || targ.closest('button,input')){
369
+ return;
370
+ }
371
+ _.win.manager.appendChild(wEl);
372
+
373
+ e.preventDefault();
374
+ x2=e.clientX || e.touches[0].clientX;
375
+ y2=e.clientY || e.touches[0].clientY;
376
+
377
+ _.$.D.onmouseup=_.$.D.ontouchend=stopW;
378
+
379
+ _.$.D.onmousemove=_.$.D.ontouchmove=moveW;
380
+ },
381
+ moveW=e=>{
382
+ e.preventDefault();
383
+ let cX=e.clientX || e.touches[0].clientX,
384
+ cY=e.clientY || e.touches[0].clientY;
385
+
386
+ x1=x2 - cX;
387
+ y1=y2 - cY;
388
+ x2=cX;
389
+ y2=cY;
390
+
391
+ wEl.style.top=(wEl.offsetTop - y1) + "px";
392
+ wEl.style.left=(wEl.offsetLeft - x1) + "px";
393
+ },
394
+ stopW=()=>{
395
+ ['mouseup','touchend','mousemove','touchmove']
396
+ .forEach(e=>_.$.D['on'+e]=null)
397
+ },
398
+ drag=win.drag;
399
+ drag.onmousedown=drag.ontouchstart=startW;
400
+ },
401
+ open(name,content='',customAttrs=''){
402
+ if (!_.win.manager || !_.win.hider)
403
+ throw new Error('Window managers not inited');
404
+ let winId=_.win._ID();
405
+ _.wins[winId]={};
406
+ let s=_.wins[winId];
407
+ s.id=winId;
408
+ s.name=name;
409
+ s.langs=name;
410
+ s.state='opened';
411
+ s.full=false;
412
+ s.inRename=false;
413
+ s.onUnfull={top:0,left:0,width:0,height:0,};
414
+
415
+ s.setTitle = newTitle=>_.win.setTitle(s,newTitle);
416
+ s.toggleFull = e=>_.win.toggleFull(s);
417
+ s.close = e=>_.win.close(s);
418
+ s.hide = e=>_.win.hide(s);
419
+ s.show = e=>_.win.show(s);
420
+
421
+ let html =
422
+ _.html`<div id=${winId} ${_.win.winAttrs} ${customAttrs}>
423
+ <div style="display:flex;justify-content:space-between;align-items:center"
424
+ ${_.win.dragAttrs} id=DRAGGER${winId}>
425
+ <span ${_.win.titleAttrs} id=title${winId}${_.lang.win('WINDOW-'+name)}/span>
426
+ <div id=btns${winId}></div>
427
+ </div>
428
+ <div id=content${winId} style=overflow:auto;width:100%;height:100%>
429
+ ${content.replace(/\{winId\}/g,winId)}
430
+ </div>
431
+ </div>`,
432
+ btns=_.$.q(`#btns${winId}`,html);
433
+ for(let b of _.win.defBtns) btns.append(_.win._winBtn(s,...b));
434
+
435
+ html.style.overflow='hidden';
436
+ html.style.resize='both';
437
+
438
+ let anim=_.win.animOpen;
439
+ if (anim)
440
+ _.$.on(html,'animationend',()=>html.classList.remove(anim),{ once: true })
441
+ _.win.manager.append(html);
442
+
443
+ let win=s.elem=_.$.id(winId),
444
+ c=_.$.id('content'+winId).getBoundingClientRect(), r=win.getBoundingClientRect(),
445
+ padX=r.width - c.width, padY=r.height - c.height;
446
+ s.drag=_.$.id('DRAGGER'+winId);
447
+ s.content=_.$.id('content'+winId);
448
+
449
+ if (!customAttrs.includes('top')) {
450
+ win.style.top=win.offsetTop - (win.offsetHeight / 2) + 'px';
451
+ win.style.left=win.offsetLeft - (win.offsetWidth / 2) + 'px';
452
+ }
453
+ if (!customAttrs.includes('width')) win.style.height=(win.offsetHeight - padX) + 'px';
454
+ if (!customAttrs.includes('height')) win.style.width=(win.offsetWidth - padY) + 'px';
455
+
456
+ _.$.on(s.drag,'dblclick',(e)=>{
457
+ if(e.target.closest('button')) return;
458
+ let wT=_.$.id('title'+winId);
459
+ if (!s.inRename){
460
+ wT.innerHTML=`<input ${_.win.renameAttrs} id=rename${winId} value="${wT.textContent}">`;
461
+ s.inRename=true;
462
+ }else{
463
+ _.win.setTitle(s,_.$.id('rename'+winId).value);
464
+ s.inRename=false;
465
+ }
466
+ });
467
+
468
+ _.win._initWin(s);
469
+
470
+ return s;
471
+ },
472
+ setTitle(win,newT){
473
+ win.langs=false;
474
+ win.name=newT;
475
+ let t=_.$.id('title'+win.id),
476
+ h=_.$.id('hider'+win.id);
477
+ t.innerHTML=newT;
478
+ t.removeAttribute('data-trans');
479
+ if (h){
480
+ h.innerHTML=newT;
481
+ h.removeAttribute('data-trans');
482
+ }
483
+ },
484
+ toggleFull(win){
485
+ let wEl=win.elem,
486
+ ws=wEl.style,
487
+ wc=wEl.classList,
488
+ cont=_.$.id('content'+win.id).getBoundingClientRect(),
489
+ rect=wEl.getBoundingClientRect(),
490
+ padX=rect.width - cont.width,
491
+ padY=rect.height - cont.height,
492
+ aOn=_.win.animFullOn,
493
+ aOff=_.win.animFullOff,
494
+ fd={
495
+ top: rect.top, left: rect.left,
496
+ width: cont.width, height: cont.height,
497
+ },
498
+ unful=()=>{
499
+ ws.top=old.top + 'px';
500
+ ws.left=old.left + 'px';
501
+ ws.width=old.width + 'px';
502
+ ws.height=old.height + 'px';
503
+ },
504
+ doFul=()=>{
505
+ if (aOn) wc.remove(aOn);
506
+ win.full=true;
507
+ win.onUnfull=fd;
508
+ ws.top=0;
509
+ ws.left=0;
510
+ ws.width=`calc(100% - ${padX}px)`;
511
+ ws.height=`calc(100% - ${padY}px)`;
512
+ win.drag.onmousedown=null;
513
+ win.drag.ontouchstart=null;
514
+ },
515
+ doUnful=()=>{
516
+ if (aOff) wc.remove(aOff);
517
+ unful();
518
+ win.full=false;
519
+ _.win._initWin(win);
520
+ },
521
+ old=win.onUnfull;
522
+ if (!win.full) {
523
+ if (aOn) {
524
+ wc.add(aOn);
525
+ _.$.on(wEl,'animationend',doFul,{ once: true });
526
+ }else doFul();
527
+ } else {
528
+ if (aOff) {
529
+ wc.add(aOff);
530
+ unful();
531
+ _.$.on(wEl,'animationend',doUnful,{ once: true });
532
+ }else doUnful();
533
+ }
534
+ },
535
+ close(win){
536
+ let w=win.elem,
537
+ remover=()=>{
538
+ let drag=win.drag;
539
+ drag.onmousedown=null;
540
+ drag.ontouchstart=null;
541
+ w.remove();
542
+ delete _.wins[win.id];
543
+ };
544
+ if (w.style.display== 'none'){
545
+ _.$.id('hider'+win.id).remove();
546
+ remover();
547
+ }else{
548
+ let anim=_.win.animClose;
549
+ if(anim){
550
+ w.classList.add(anim);
551
+ _.$.on(w,'animationend',remover,{ once: true });
552
+ }else
553
+ remover();
554
+ }
555
+ },
556
+ hide(win){
557
+ let wEl = win.elem,
558
+ wc=wEl.classList,
559
+ anim=_.win.animHide,
560
+ hider=()=>{
561
+ wEl.style.display='none';
562
+ if(anim)wc.remove(anim);
563
+ win.state='hidened';
564
+ _.win.hider.append(_.win._hiderBtn(win));
565
+ }
566
+ if(anim){
567
+ wc.add(anim);
568
+ _.$.on(wEl,'animationend',hider,{ once: true });
569
+ }else
570
+ hider();
571
+ },
572
+ show(win){
573
+ let wEl = win.elem,
574
+ wc=wEl.classList,
575
+ anim=_.win.animShow,
576
+ hider=_.$.id('hider'+win.id),
577
+ shower=()=>{
578
+ if(anim)wc.remove(anim);
579
+ win.state='opened';
580
+ }
581
+ wEl.style.display='';
582
+ hider.remove();
583
+ if(anim){
584
+ wc.add(anim);
585
+ _.$.on(wEl,'animationend',shower,{ once: true });
586
+ }else
587
+ shower()
588
+ },
589
+ };
590
+ _.wins={};
591
+
592
+ _.$.on(window,'error',_.err.handleGlobal);
593
+ _.$.on(window,'unhandledrejection',_.err.handleRejection);
594
+ _.$.on(window,'popstate',()=>{
595
+ let l=_.link
596
+ if (!l._i) {
597
+ let nUrl='?' + [l.compile()[0],...l._cmd].join('&');
598
+ history.replaceState(null,null,nUrl);
599
+ l._i=true;
600
+ l.get();
601
+ } else
602
+ l._i=false;
603
+ });
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "newhelper-js",
3
+ "version": "2.1.0",
4
+ "description": "сверх лёгкая библиотека для построения админок",
5
+ "keywords": [
6
+ "admin",
7
+ "ui",
8
+ "jquery",
9
+ "modal",
10
+ "dialog",
11
+ "router",
12
+ "window-manager",
13
+ "i18n",
14
+ "internationalization",
15
+ "lazy-loading",
16
+ "hotkeys",
17
+ "lightweight",
18
+ "vanilla-js",
19
+ "frontend",
20
+ "zero-dependencies"
21
+ ],
22
+ "homepage": "https://github.com/MIOBOMB/newHelper-js#readme",
23
+ "bugs": {
24
+ "url": "https://github.com/MIOBOMB/newHelper-js/issues"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/MIOBOMB/newHelper-js.git"
29
+ },
30
+ "license": "BSD-2-Clause",
31
+ "author": "MIOBOMB",
32
+ "type": "commonjs",
33
+ "main": "newHelper.js",
34
+ "files": [
35
+ "newHelper.js",
36
+ "README.md",
37
+ "README-en.md",
38
+ "docs.md",
39
+ "history.md"
40
+ ],
41
+ "scripts": {
42
+ "test": "echo \"Error: no test specified\" && exit 1"
43
+ },
44
+ "unpkg": "newHelper.js"
45
+ }