CDClientLib.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. /*!
  2. Client lib for manipulating on DOM elements.
  3. Author: CrazyDoctor (Oleg Karataev)
  4. */
  5. function isEmpty(val) {
  6. return val === null || val === undefined ||
  7. val === '' || val.length === 0;
  8. }
  9. function isJsonString(str) {
  10. try {
  11. JSON.parse(str);
  12. } catch (e) {
  13. return false;
  14. }
  15. return true;
  16. }
  17. class CDElement {
  18. constructor(el) {
  19. this.element = el;
  20. el.cdelement = this;
  21. try {
  22. this.events = Object.keys(getEventListeners(el));
  23. } catch(e) {
  24. this.events = [];
  25. }
  26. }
  27. static get(el) {
  28. if(el.cdelement)
  29. return el.cdelement;
  30. return el.cdelement = new CDElement(el);
  31. }
  32. get() {
  33. return this.element;
  34. }
  35. copy() {
  36. return new CDElement(this.get().cloneNode(true));
  37. }
  38. getFirstChild(selector) {
  39. const children = Array.from(this.get().children);
  40. if (children.length == 0)
  41. return null;
  42. if (isEmpty(selector))
  43. return CDElement.get(children[0]);
  44. const child = this.get().querySelector(selector);
  45. if(child)
  46. return CDElement.get(child);
  47. return null;
  48. }
  49. hasChildren() {
  50. return Array.from(this.get().children).length > 0;
  51. }
  52. getChildren(selector) {
  53. if (isEmpty(selector))
  54. return Array.from(this.get().children).map((element) => CDElement.get(element));
  55. return Array.from(this.get().querySelectorAll(selector)).map((element) => CDElement.get(element));
  56. }
  57. getChildrenRecursive() {
  58. let children = this.getChildren();
  59. for(const child of children) {
  60. if(this.hasChildren())
  61. children = children.concat(child.getChildrenRecursive());
  62. }
  63. return children;
  64. }
  65. getParent() {
  66. return CDElement.get(this.get().parentElement);
  67. }
  68. getValue() {
  69. return this.get().value || this.getInnerHTML();
  70. }
  71. append(element) {
  72. this.get().append(element.get());
  73. }
  74. prepend(element) {
  75. this.get().prepend(element.get());
  76. }
  77. remove() {
  78. this.get().remove();
  79. }
  80. disable() {
  81. this.removeCssProperty('display');
  82. this.addClass('disabled');
  83. return this;
  84. }
  85. enable(display) {
  86. this.removeClass('disabled');
  87. this.get().style.display = isEmpty(display) ? 'block' : display;
  88. return this;
  89. }
  90. minimizeHeight() {
  91. this.get().style.height = '0px';
  92. return this;
  93. }
  94. minimizeWidth() {
  95. this.get().style.width = '0px';
  96. return this;
  97. }
  98. expandHeight(size) {
  99. if (isEmpty(size))
  100. return this;
  101. this.get().style.height = size + 'px';
  102. return this;
  103. }
  104. expandWidth(size){
  105. if (isEmpty(size))
  106. return this;
  107. this.get().style.width = size + 'px';
  108. return this;
  109. }
  110. isMinimized(el) {
  111. return isEmpty(this.get().style.height);
  112. }
  113. isDisabled(el) {
  114. return isEmpty(this.get().style.display);
  115. }
  116. setId(id) {
  117. if (isEmpty(id))
  118. return this;
  119. this.get().id = id;
  120. return id;
  121. }
  122. nextSibling() {
  123. return CDElement.get(this.get().nextElementSibling);
  124. }
  125. addClass(cls) {
  126. if (isEmpty(cls))
  127. return this;
  128. cls.split(' ').forEach((c) => {
  129. if(c.length > 0)
  130. this.get().classList.add(c);
  131. });
  132. return this;
  133. }
  134. getClass() {
  135. if (isEmpty(this.get().classList))
  136. return '';
  137. let classList = '';
  138. this.get().classList.forEach((cls) => {
  139. classList += cls + " ";
  140. });
  141. return classList.trim();
  142. }
  143. removeClass(cls) {
  144. if (isEmpty(cls))
  145. return this;
  146. this.get().classList.remove(cls);
  147. return this;
  148. }
  149. hasClass(cls) {
  150. if (isEmpty(this.get().classList))
  151. return false;
  152. let has = false;
  153. this.get().classList.forEach((c) => {
  154. if (c === cls) has = true;
  155. });
  156. return has;
  157. }
  158. removeClass(cls) {
  159. if (isEmpty(cls))
  160. return this;
  161. this.get().classList.remove(cls);
  162. return this;
  163. }
  164. switchClass(cls, condition) {
  165. if(condition != null)
  166. return condition ? this.addClass(cls) : this.removeClass(cls);
  167. return this.hasClass(cls) ? this.removeClass(cls) : this.addClass(cls);
  168. }
  169. removeCssProperty(prop) {
  170. if (isEmpty(prop))
  171. return this;
  172. this.get().style.setProperty(prop, '');
  173. return this;
  174. }
  175. setAttribute(attr, value) {
  176. this.get().setAttribute(attr, isEmpty(value) ? '' : value);
  177. return this;
  178. }
  179. getAttribute(attr) {
  180. return this.get().getAttribute(attr);
  181. }
  182. setInnerHTML(value) {
  183. this.get().innerHTML = isEmpty(value) ? '' : value;
  184. return this;
  185. }
  186. getInnerHTML() {
  187. return this.get().innerHTML;
  188. }
  189. getInnerWidth() {
  190. return this.get().innerWidth;
  191. }
  192. getInnerHeight() {
  193. return this.get().innerHeight;
  194. }
  195. getOffsetTop() {
  196. return this.get().offsetTop;
  197. }
  198. scrollTo(selector) {
  199. const child = this.getFirstChild(selector);
  200. if(child)
  201. this.get().scrollTop = child.getOffsetTop() - this.getOffsetTop();
  202. }
  203. style(property, value, priority) {
  204. this.get().style.setProperty(property, value, priority);
  205. return this;
  206. }
  207. on(event, callback) {
  208. if (isEmpty(event) || isEmpty(callback))
  209. return this;
  210. this.get().addEventListener(event, callback);
  211. return this;
  212. }
  213. un(event, callback) {
  214. if (isEmpty(event))
  215. return this;
  216. this.get().removeEventListener(event, callback);
  217. return this;
  218. }
  219. }
  220. class Url {
  221. constructor(url) {
  222. this.url = new URL(url || location);
  223. this.urlSearch = new URLSearchParams(this.url.search);
  224. }
  225. static getHash() {
  226. return new Url().getHash();
  227. }
  228. static setHash(hash) {
  229. return new Url().setHash(hash);
  230. }
  231. static goTo(url) {
  232. window.location = url;
  233. }
  234. getHash() {
  235. const hash = this.url.hash.substring(1);
  236. return hash.length > 0 ? hash : null;
  237. }
  238. setHash(hash) {
  239. this.url.hash = !hash || hash.length == 0 ? '' : `#${hash}`;
  240. return this;
  241. }
  242. toString() {
  243. this.url.search = this.urlSearch.toString();
  244. return this.url.toString();
  245. }
  246. toLocalString() {
  247. this.url.search = this.urlSearch.toString();
  248. return this.toString().substring(this.url.origin.length);
  249. }
  250. updateLocation() {
  251. history.replaceState(null, null, this.toLocalString());
  252. }
  253. }
  254. class Style {
  255. static apply(element, styleStr) {
  256. element = element instanceof CDElement ? element : CDElement.get(element);
  257. const propertiesMap = this.getCssPropertiesMap(styleStr);
  258. for(const prop of Object.keys(propertiesMap))
  259. element.style(prop, propertiesMap[prop].value, propertiesMap[prop].priority);
  260. }
  261. static getCssPropertiesMap(styleStr) {
  262. const parts = styleStr.split(';');
  263. const map = {};
  264. for(let part of parts) {
  265. part = part.trim();
  266. if(part.length == 0)
  267. continue;
  268. const propVal = part.split(':');
  269. const property = propVal[0].trim();
  270. const value = propVal[1].trim().split('!');
  271. map[property] = { value: value[0], priority: value.length > 1 ? value[1] : '' };
  272. }
  273. return map;
  274. }
  275. }
  276. class DOM {
  277. static Events = {
  278. Click: 'click',
  279. Load: 'load',
  280. KeyDown: 'keydown',
  281. KeyUp: 'keyup'
  282. };
  283. static Tags = {
  284. Div: 'div',
  285. Span: 'span',
  286. H1: 'h1',
  287. H2: 'h2',
  288. H3: 'h3',
  289. P: 'p'
  290. };
  291. static get(selector) {
  292. if (isEmpty(selector))
  293. throw "DOM.get() invalid selector.";
  294. const element = document.querySelector(selector);
  295. if (isEmpty(element))
  296. return null;
  297. return CDElement.get(element);
  298. }
  299. static getAll(selector) {
  300. if (isEmpty(selector))
  301. throw "DOM.getAll() invalid selector.";
  302. const elements = document.querySelectorAll(selector);
  303. if(isEmpty(elements))
  304. return [];
  305. return Array.from(elements).map((element) => CDElement.get(element));
  306. }
  307. static create(config, container, prepend) {
  308. if (isEmpty(config) || isEmpty(config.tag))
  309. return;
  310. const element = CDElement.get(document.createElement(config.tag));
  311. if (!isEmpty(config.attr)) {
  312. Object.keys(config.attr).forEach((name) => {
  313. element.setAttribute(name, config.attr[name]);
  314. });
  315. }
  316. if (!isEmpty(config.cls)) element.addClass(config.cls);
  317. if (!isEmpty(config.id)) element.setId(config.id);
  318. if (!isEmpty(config.style)) Style.apply(element, config.style);
  319. if (!isEmpty(config.cn)) {
  320. config.cn.forEach((el) => {
  321. if (el instanceof Element) {
  322. element.append(CDElement.get(el));
  323. } else if (el instanceof CDElement) {
  324. element.append(el);
  325. } else this.create(el, element);
  326. });
  327. }
  328. // innerHTML appends after cn
  329. if (!isEmpty(config.innerHTML)) element.setInnerHTML(element.getInnerHTML() + config.innerHTML);
  330. if (!isEmpty(container))
  331. (prepend === true ? container.prepend(element) : container.append(element));
  332. return element;
  333. }
  334. static append(container, element) {
  335. if (isEmpty(element) || isEmpty(container) ||
  336. (!(element instanceof Element) && !(element instanceof CDElement)) ||
  337. (!(container instanceof Element) && !(container instanceof CDElement)))
  338. return;
  339. (container instanceof CDElement ? container.get() : container).append((element instanceof CDElement ? element.get() : element));
  340. }
  341. static setTitle(title) {
  342. document.title = title;
  343. }
  344. static setCookie(name, value, hours) {
  345. document.cookie = name + "=" + JSON.stringify(value) + "; path=/; expires=" + (new Date(Date.now() + hours * 3600000).toGMTString());
  346. }
  347. static getCookie(name) {
  348. const cookies = {};
  349. document.cookie.split(';').forEach(function(el) {
  350. const [key, value] = el.split('=');
  351. cookies[key.trim()] = value;
  352. });
  353. const cookieValue = cookies[name];
  354. if(isEmpty(cookieValue))
  355. return null;
  356. if(isJsonString(cookieValue))
  357. return JSON.parse(cookieValue);
  358. else
  359. return cookieValue;
  360. }
  361. static getCookieProperty(name, property) {
  362. const cookie = DOM.getCookie(name);
  363. if(cookie && !(cookie instanceof Object))
  364. throw 'DOM.getCookieProperty(): cookie value is not a JSON';
  365. return cookie ? cookie[property] : null;
  366. }
  367. static setCookieProperty(name, property, value, hours) {
  368. const cookie = DOM.getCookie(name);
  369. if(cookie) {
  370. if(!(cookie instanceof Object))
  371. throw 'DOM.setCookieProperty(): initial cookie value is not a JSON';
  372. cookie[property] = value;
  373. DOM.setCookie(name, cookie, hours || 24);
  374. } else {
  375. DOM.setCookie(name, { [property]: value }, hours || 24);
  376. }
  377. }
  378. }
  379. class CDUtils {
  380. /**
  381. * Returns string representation of <b>x</b> with leading zeros.<br/>
  382. * Length of the resulting string will be equal to <b>length</b> or <b>2</b> if <b>length</b> was not specified.<br/>
  383. * If length of the initial number is greater or equal to <b>length</b> parameter, nothing will be changed.
  384. * <br/><br/>
  385. * Examples:<br/>CDUtils.pad(5) -> "05"<br/>CDUtils.pad(21) -> "21"<br/>CDUtils.pad(21, 3) -> "021"
  386. * @param {number|String} x
  387. * @param {number} [length]
  388. * @return string
  389. */
  390. static pad(x, length) {
  391. const count = length != null && length >= 2 ? length : 2;
  392. const str = x.toString();
  393. const diff = count - str.length;
  394. return `${"0".repeat(diff < 0 ? 0 : diff)}${str}`;
  395. }
  396. /**
  397. * Returns formatted date. <br/>
  398. * H - hours<br/>I - minutes<br/>S - seconds<br/>s - milliseconds<br/>D - day<br/>M - month<br/>Y - year (0000)<br/>y - year (00)
  399. * @param {number} timestamp
  400. * @param {string} format
  401. * @return string
  402. */
  403. static dateFormat(timestamp, format) {
  404. const date = new Date(timestamp);
  405. const day = date.getDate();
  406. const month = date.getMonth()+1;
  407. const year = date.getFullYear();
  408. const hours = date.getHours();
  409. const minutes = date.getMinutes();
  410. const seconds = date.getSeconds();
  411. const mseconds = date.getMilliseconds();
  412. return format.replaceAll('H', CDUtils.pad(hours.toString()))
  413. .replaceAll('I', CDUtils.pad(minutes.toString()))
  414. .replaceAll('S', CDUtils.pad(seconds.toString()))
  415. .replaceAll('s', CDUtils.pad(mseconds.toString()))
  416. .replaceAll('D', CDUtils.pad(day.toString()))
  417. .replaceAll('M', CDUtils.pad(month.toString()))
  418. .replaceAll('Y', CDUtils.pad(year.toString(), 4))
  419. .replaceAll('y', CDUtils.pad((year % 100).toString()));
  420. }
  421. static dateFormatUTC(date, timeZoneOffsetHours, format) {
  422. date = date instanceof Date ? date : new Date(date);
  423. date.setUTCHours(date.getUTCHours() + timeZoneOffsetHours);
  424. var year = date.getUTCFullYear();
  425. var month = date.getUTCMonth() + 1;
  426. var day = date.getUTCDate();
  427. var hours = date.getUTCHours();
  428. var minutes = date.getUTCMinutes();
  429. var seconds = date.getUTCSeconds();
  430. return format.replaceAll('H', CDUtils.pad(hours.toString()))
  431. .replaceAll('I', CDUtils.pad(minutes.toString()))
  432. .replaceAll('S', CDUtils.pad(seconds.toString()))
  433. .replaceAll('D', CDUtils.pad(day.toString()))
  434. .replaceAll('M', CDUtils.pad(month.toString()))
  435. .replaceAll('Y', CDUtils.pad(year.toString(), 4))
  436. .replaceAll('y', CDUtils.pad((year % 100).toString())) + ` UTC${timeZoneOffsetHours > 0 ? '+' : '-'}${timeZoneOffsetHours}`;
  437. }
  438. static repeat(str, n) {
  439. let res = '';
  440. for(let i = 0; i < n; i++)
  441. res += str;
  442. return res;
  443. }
  444. static randInt(min, max) {
  445. const minCeiled = Math.ceil(min);
  446. const maxFloored = Math.floor(max);
  447. return Math.floor(Math.random() * (maxFloored - minCeiled) + minCeiled);
  448. }
  449. static arrayShuffle(unshuffled) {
  450. return unshuffled
  451. .map(value => ({ value, sort: Math.random() }))
  452. .sort((a, b) => a.sort - b.sort)
  453. .map(({ value }) => value);
  454. }
  455. static toHtml(str) {
  456. return str.replace(/(?:\r\n|\r|\n)/g, '<br>');
  457. }
  458. }
  459. const window_ = CDElement.get(window);