You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1110 lines
39 KiB

3 years ago
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
  1. /*
  2. * Document : helpers.js
  3. * Author : pixelcave
  4. * Description: Various helpers for plugin inits or helper functionality
  5. *
  6. */
  7. // Helper variables
  8. let jqSparklineResize = false;
  9. let jqSparklineTimeout;
  10. // Helpers
  11. export default class Helpers {
  12. /*
  13. * Run helpers
  14. *
  15. */
  16. static run(helpers, options = {}) {
  17. let helperList = {
  18. // Bootstrap
  19. 'bs-tooltip': () => this.bsTooltip(),
  20. 'bs-popover': () => this.bsPopover(),
  21. // Dashmix
  22. 'dm-toggle-class': () => this.dmToggleClass(),
  23. 'dm-year-copy': () => this.dmYearCopy(),
  24. 'dm-ripple': () => this.dmRipple(),
  25. 'dm-print': () => this.dmPrint(),
  26. 'dm-table-tools-sections': () => this.dmTableToolsSections(),
  27. 'dm-table-tools-checkable': () => this.dmTableToolsCheckable(),
  28. // JavaScript
  29. 'js-ckeditor': () => this.jsCkeditor(),
  30. 'js-ckeditor5': () => this.jsCkeditor5(),
  31. 'js-simplemde': () => this.jsSimpleMDE(),
  32. 'js-highlightjs': () => this.jsHighlightjs(),
  33. 'js-flatpickr': () => this.jsFlatpickr(),
  34. // jQuery
  35. 'jq-appear': () => this.jqAppear(),
  36. 'jq-magnific-popup': () => this.jqMagnific(),
  37. 'jq-slick': () => this.jqSlick(),
  38. 'jq-datepicker': () => this.jqDatepicker(),
  39. 'jq-masked-inputs': () => this.jqMaskedInputs(),
  40. 'jq-select2': () => this.jqSelect2(),
  41. 'jq-notify': (options) => this.jqNotify(options),
  42. 'jq-easy-pie-chart': () => this.jqEasyPieChart(),
  43. 'jq-maxlength': () => this.jqMaxlength(),
  44. 'jq-rangeslider': () => this.jqRangeslider(),
  45. 'jq-pw-strength': () => this.jqPwStrength(),
  46. 'jq-sparkline': () => this.jqSparkline(),
  47. 'jq-validation': () => this.jqValidation()
  48. };
  49. if (helpers instanceof Array) {
  50. for (let index in helpers) {
  51. if (helperList[helpers[index]]) {
  52. helperList[helpers[index]](options);
  53. }
  54. }
  55. } else {
  56. if (helperList[helpers]) {
  57. helperList[helpers](options);
  58. }
  59. }
  60. }
  61. /*
  62. ********************************************************************************************
  63. *
  64. * Init helpers for Bootstrap plugins
  65. *
  66. *********************************************************************************************
  67. */
  68. /*
  69. * Bootstrap Tooltip, for more examples you can check out https://getbootstrap.com/docs/5.0/components/tooltips/
  70. *
  71. * Helpers.run('bs-tooltip');
  72. *
  73. * Example usage:
  74. *
  75. * <button type="button" class="btn btn-primary" data-bs-toggle="tooltip" title="Tooltip Text">Example</button> or
  76. * <button type="button" class="btn btn-primary js-bs-tooltip" title="Tooltip Text">Example</button>
  77. *
  78. */
  79. static bsTooltip() {
  80. let elements = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]:not(.js-bs-tooltip-enabled), .js-bs-tooltip:not(.js-bs-tooltip-enabled)'));
  81. window.helperBsTooltips = elements.map(el => {
  82. // Add .js-bs-tooltip-enabled class to tag it as activated
  83. el.classList.add('js-bs-tooltip-enabled');
  84. // Init Bootstrap Tooltip
  85. return new bootstrap.Tooltip(el, {
  86. container: el.dataset.bsContainer || '#page-container',
  87. animation: el.dataset.bsAnimation && el.dataset.bsAnimation.toLowerCase() == 'true' ? true : false,
  88. })
  89. });
  90. }
  91. /*
  92. * Bootstrap Popover, for more examples you can check out https://getbootstrap.com/docs/5.0/components/popovers/
  93. *
  94. * Helpers.run('bs-popover');
  95. *
  96. * Example usage:
  97. *
  98. * <button type="button" class="btn btn-primary" data-bs-toggle="popover" title="Popover Title" data-bs-content="This is the content of the Popover">Example</button> or
  99. * <button type="button" class="btn btn-primary js-bs-popover" title="Popover Title" data-bs-content="This is the content of the Popover">Example</button>
  100. *
  101. */
  102. static bsPopover() {
  103. let elements = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]:not(.js-bs-popover-enabled), .js-bs-popover:not(.js-bs-popover-enabled)'));
  104. window.helperBsPopovers = elements.map(el => {
  105. // Add .js-bs-popover-enabled class to tag it as activated
  106. el.classList.add('js-bs-popover-enabled');
  107. // Init Bootstrap Popover
  108. return new bootstrap.Popover(el, {
  109. container: el.dataset.bsContainer || '#page-container',
  110. animation: el.dataset.bsAnimation && el.dataset.bsAnimation.toLowerCase() == 'true' ? true : false,
  111. trigger: el.dataset.bsTrigger || 'hover focus',
  112. })
  113. });
  114. }
  115. /*
  116. ********************************************************************************************
  117. *
  118. * JS helpers to add custom functionality
  119. *
  120. *********************************************************************************************
  121. */
  122. /*
  123. * Toggle class on element click
  124. *
  125. * Helpers.run('dm-toggle-class');
  126. *
  127. * Example usage (on button click, "exampleClass" class is toggled on the element with id "elementID"):
  128. *
  129. * <button type="button" class="btn btn-primary" data-toggle="class-toggle" data-target="#elementID" data-class="exampleClass">Toggle</button>
  130. *
  131. * or
  132. *
  133. * <button type="button" class="btn btn-primary js-class-toggle" data-target="#elementID" data-class="exampleClass">Toggle</button>
  134. *
  135. */
  136. static dmToggleClass() {
  137. let elements = document.querySelectorAll('[data-toggle="class-toggle"]:not(.js-class-toggle-enabled), .js-class-toggle:not(.js-class-toggle-enabled)');
  138. elements.forEach(el => {
  139. el.addEventListener('click', () => {
  140. // Add .js-class-toggle-enabled class to tag it as activated
  141. el.classList.add('js-class-toggle-enabled');
  142. // Get all classes
  143. let cssClasses = el.dataset.class ? el.dataset.class.split(' ') : false;
  144. // Toggle class on target elements
  145. document.querySelectorAll(el.dataset.target).forEach(targetEl => {
  146. if (cssClasses) {
  147. cssClasses.forEach(cls => {
  148. targetEl.classList.toggle(cls);
  149. });
  150. }
  151. });
  152. });
  153. });
  154. }
  155. /*
  156. * Add the correct copyright year to an element
  157. *
  158. * Helpers.run('dm-year-copy');
  159. *
  160. * Example usage (it will get populated with current year if empty or will append it to specified year if needed):
  161. *
  162. * <span data-toggle="year-copy"></span> or
  163. * <span data-toggle="year-copy">2018</span>
  164. *
  165. */
  166. static dmYearCopy() {
  167. let elements = document.querySelectorAll('[data-toggle="year-copy"]:not(.js-year-copy-enabled)');
  168. elements.forEach(el => {
  169. let date = new Date();
  170. let currentYear = date.getFullYear();
  171. let baseYear = el.textContent || currentYear;
  172. // Add .js-year-copy-enabled class to tag it as activated
  173. el.classList.add('js-year-copy-enabled');
  174. // Set the correct year
  175. el.textContent = (parseInt(baseYear) >= currentYear) ? currentYear : baseYear + '-' + currentYear.toString().substr(2, 2);
  176. });
  177. }
  178. /*
  179. * Ripple effect fuctionality
  180. *
  181. * Helpers.run('dm-ripple');
  182. *
  183. * Example usage:
  184. *
  185. * <button type="button" class="btn btn-primary" data-toggle="click-ripple">Click Me!</button>
  186. *
  187. */
  188. static dmRipple() {
  189. let elements = document.querySelectorAll('[data-toggle="click-ripple"]:not(.js-click-ripple-enabled)');
  190. elements.forEach(el => {
  191. // Add .js-click-ripple-enabled class to tag it as activated and init it
  192. el.classList.add('js-click-ripple-enabled');
  193. // Add custom CSS styles
  194. el.style.overflow = 'hidden';
  195. el.style.position = 'relative';
  196. el.style.zIndex = 1;
  197. // On click create and render the ripple
  198. el.addEventListener('click', e => {
  199. let cssClass = 'click-ripple';
  200. let ripple = el.querySelector('.' + cssClass);
  201. let d, x, y;
  202. // If the ripple element exists in this element, remove .animate class from ripple element..
  203. if (ripple) {
  204. ripple.classList.remove('animate');
  205. }
  206. else { // ..else add it
  207. let elChild = document.createElement('span')
  208. elChild.classList.add(cssClass)
  209. el.insertBefore(elChild, el.firstChild);
  210. }
  211. // Get the ripple element
  212. ripple = el.querySelector('.' + cssClass);
  213. // If the ripple element doesn't have dimensions, set them accordingly
  214. if ((getComputedStyle(ripple).height === '0px') || (getComputedStyle(ripple).width === '0px')) {
  215. d = Math.max(el.offsetWidth, el.offsetHeight)
  216. ripple.style.height = d + 'px'
  217. ripple.style.width = d + 'px'
  218. }
  219. // Get coordinates for our ripple element
  220. x = e.pageX - (el.getBoundingClientRect().left + window.scrollX) - parseFloat(getComputedStyle(ripple).width.replace('px', '')) / 2
  221. y = e.pageY - (el.getBoundingClientRect().top + window.scrollY) - parseFloat(getComputedStyle(ripple).height.replace('px', '')) / 2
  222. // Position the ripple element and add the class .animate to it
  223. ripple.style.top = y + 'px'
  224. ripple.style.left = x + 'px'
  225. ripple.classList.add('animate')
  226. });
  227. });
  228. }
  229. /*
  230. * Print Page functionality
  231. *
  232. * Helpers.run('dm-print');
  233. *
  234. */
  235. static dmPrint() {
  236. // Store all #page-container classes
  237. let lPage = document.getElementById('page-container');
  238. let pageCls = lPage.className;
  239. console.log(pageCls);
  240. // Remove all classes from #page-container
  241. lPage.classList = '';
  242. // Print the page
  243. window.print();
  244. // Restore all #page-container classes
  245. lPage.classList = pageCls;
  246. }
  247. /*
  248. * Table sections functionality
  249. *
  250. * Helpers.run('dm-table-tools-sections');
  251. *
  252. * Example usage:
  253. *
  254. * Please check out the Table Helpers page for complete markup examples
  255. *
  256. */
  257. static dmTableToolsSections() {
  258. let tables = document.querySelectorAll('.js-table-sections:not(.js-table-sections-enabled)');
  259. tables.forEach(table => {
  260. // Add .js-table-sections-enabled class to tag it as activated
  261. table.classList.add('js-table-sections-enabled');
  262. // When a row is clicked in tbody.js-table-sections-header
  263. table.querySelectorAll('.js-table-sections-header > tr').forEach(tr => {
  264. tr.addEventListener('click', e => {
  265. if (e.target.type !== 'checkbox'
  266. && e.target.type !== 'button'
  267. && e.target.tagName.toLowerCase() !== 'a'
  268. && e.target.parentNode.nodeName.toLowerCase() !== 'a'
  269. && e.target.parentNode.nodeName.toLowerCase() !== 'button'
  270. && e.target.parentNode.nodeName.toLowerCase() !== 'label'
  271. && !e.target.parentNode.classList.contains('custom-control')) {
  272. let tbody = tr.parentNode;
  273. let tbodyAll = table.querySelectorAll('tbody');
  274. if (!tbody.classList.contains('show')) {
  275. if (tbodyAll) {
  276. tbodyAll.forEach(tbodyEl => {
  277. tbodyEl.classList.remove('show');
  278. tbodyEl.classList.remove('table-active');
  279. });
  280. }
  281. }
  282. tbody.classList.toggle('show');
  283. tbody.classList.toggle('table-active');
  284. }
  285. });
  286. });
  287. });
  288. }
  289. /*
  290. * Checkable table functionality
  291. *
  292. * Helpers.run('dm-table-tools-checkable');
  293. *
  294. * Example usage:
  295. *
  296. * Please check out the Table Helpers page for complete markup examples
  297. *
  298. */
  299. static dmTableToolsCheckable() {
  300. let tables = document.querySelectorAll('.js-table-checkable:not(.js-table-checkable-enabled)');
  301. tables.forEach(table => {
  302. // Add .js-table-checkable-enabled class to tag it as activated
  303. table.classList.add('js-table-checkable-enabled');
  304. // When a checkbox is clicked in thead
  305. table.querySelector('thead input[type=checkbox]').addEventListener('click', e => {
  306. // Check or uncheck all checkboxes in tbody
  307. table.querySelectorAll('tbody input[type=checkbox]').forEach(checkbox => {
  308. checkbox.checked = e.currentTarget.checked;
  309. // Update Row classes
  310. this.tableToolscheckRow(checkbox, e.currentTarget.checked);
  311. });
  312. });
  313. // When a checkbox is clicked in tbody
  314. table.querySelectorAll('tbody input[type=checkbox], tbody input + label').forEach(checkbox => {
  315. checkbox.addEventListener('click', e => {
  316. let checkboxHead = table.querySelector('thead input[type=checkbox]');
  317. // Adjust checkbox in thead
  318. if (!checkbox.checked) {
  319. checkboxHead.checked = false
  320. } else {
  321. if (table.querySelectorAll('tbody input[type=checkbox]:checked').length === table.querySelectorAll('tbody input[type=checkbox]').length) {
  322. checkboxHead.checked = true;
  323. }
  324. }
  325. // Update Row classes
  326. this.tableToolscheckRow(checkbox, checkbox.checked);
  327. });
  328. });
  329. // When a row is clicked in tbody
  330. table.querySelectorAll('tbody > tr').forEach(tr => {
  331. tr.addEventListener('click', e => {
  332. if (e.target.type !== 'checkbox'
  333. && e.target.type !== 'button'
  334. && e.target.tagName.toLowerCase() !== 'a'
  335. && e.target.parentNode.nodeName.toLowerCase() !== 'a'
  336. && e.target.parentNode.nodeName.toLowerCase() !== 'button'
  337. && e.target.parentNode.nodeName.toLowerCase() !== 'label'
  338. && !e.target.parentNode.classList.contains('custom-control')) {
  339. let checkboxHead = table.querySelector('thead input[type=checkbox]');
  340. let checkbox = e.currentTarget.querySelector('input[type=checkbox]');
  341. // Update row's checkbox status
  342. checkbox.checked = !checkbox.checked;
  343. // Update Row classes
  344. this.tableToolscheckRow(checkbox, checkbox.checked);
  345. // Adjust checkbox in thead
  346. if (!checkbox.checked) {
  347. checkboxHead.checked = false
  348. } else {
  349. if (table.querySelectorAll('tbody input[type=checkbox]:checked').length === table.querySelectorAll('tbody input[type=checkbox]').length) {
  350. checkboxHead.checked = true;
  351. }
  352. }
  353. }
  354. });
  355. });
  356. });
  357. }
  358. // Checkable table functionality helper - Checks or unchecks table row
  359. static tableToolscheckRow(checkbox, checkedStatus) {
  360. if (checkedStatus) {
  361. checkbox.closest('tr').classList.add('table-active');
  362. } else {
  363. checkbox.closest('tr').classList.remove('table-active');
  364. }
  365. }
  366. /*
  367. ********************************************************************************************
  368. *
  369. * Init helpers for pure JS libraries
  370. *
  371. ********************************************************************************************
  372. */
  373. /*
  374. * CKEditor init, for more examples you can check out http://ckeditor.com/
  375. *
  376. * Helpers.run('jsCkeditor');
  377. *
  378. * Example usage:
  379. *
  380. * <textarea id="js-ckeditor" name="ckeditor">Hello CKEditor!</textarea> or
  381. * <div id="js-ckeditor-inline">Hello inline CKEditor!</div>
  382. *
  383. */
  384. static jsCkeditor() {
  385. let ckeditorInline = document.querySelector('#js-ckeditor-inline:not(.js-ckeditor-inline-enabled)');
  386. let ckeditorFull = document.querySelector('#js-ckeditor:not(.js-ckeditor-enabled)');
  387. // Init inline text editor
  388. if (ckeditorInline) {
  389. ckeditorInline.setAttribute('contenteditable', 'true');
  390. CKEDITOR.inline('js-ckeditor-inline');
  391. // Add .js-ckeditor-inline-enabled class to tag it as activated
  392. ckeditorInline.classList.add('js-ckeditor-inline-enabled');
  393. }
  394. // Init full text editor
  395. if (ckeditorFull) {
  396. CKEDITOR.replace('js-ckeditor');
  397. // Add .js-ckeditor-enabled class to tag it as activated
  398. ckeditorFull.classList.add('js-ckeditor-enabled');
  399. }
  400. }
  401. /*
  402. * CKEditor 5 init, for more examples you can check out http://ckeditor.com/
  403. *
  404. * Helpers.run('js-ckeditor5');
  405. *
  406. * Example usage:
  407. *
  408. * <div id="js-ckeditor5-classic">Hello classic CKEditor 5!</div>
  409. * ..or..
  410. * <div id="js-ckeditor5-inline">Hello inline CKEditor 5!</div>
  411. *
  412. */
  413. static jsCkeditor5() {
  414. let ckeditor5Inline = document.querySelector('#js-ckeditor5-inline');
  415. let ckeditor5Full = document.querySelector('#js-ckeditor5-classic');
  416. // Init inline text editor
  417. if (ckeditor5Inline) {
  418. InlineEditor
  419. .create(document.querySelector('#js-ckeditor5-inline'))
  420. .then(editor => {
  421. window.editor = editor;
  422. })
  423. .catch(error => {
  424. console.error('There was a problem initializing the inline editor.', error);
  425. });
  426. }
  427. // Init full text editor
  428. if (ckeditor5Full) {
  429. ClassicEditor
  430. .create(document.querySelector('#js-ckeditor5-classic'))
  431. .then(editor => {
  432. window.editor = editor;
  433. })
  434. .catch(error => {
  435. console.error('There was a problem initializing the classic editor.', error);
  436. });
  437. }
  438. }
  439. /*
  440. * SimpleMDE init, for more examples you can check out https://github.com/NextStepWebs/simplemde-markdown-editor
  441. *
  442. * Helpers.run('js-simplemde');
  443. *
  444. * Example usage:
  445. *
  446. * <textarea class="js-simplemde" id="simplemde" name="simplemde">Hello SimpleMDE!</textarea>
  447. *
  448. */
  449. static jsSimpleMDE() {
  450. let elements = document.querySelectorAll('.js-simplemde');
  451. elements.forEach(el => {
  452. // Init editor
  453. new SimpleMDE({ element: el, autoDownloadFontAwesome: false });
  454. });
  455. // Fix: Change SimpleMDE's Font Awesome 4 Icons with Font Awesome 6
  456. if (elements) {
  457. document.querySelector('.editor-toolbar > a.fa-header').classList.replace('fa-header', 'fa-heading');
  458. document.querySelector('.editor-toolbar > a.fa-picture-o').classList.replace('fa-picture-o', 'fa-image');
  459. }
  460. }
  461. /*
  462. * Highlight.js, for more examples you can check out https://highlightjs.org/usage/
  463. *
  464. * Helpers.run('js-highlightjs');
  465. *
  466. * Example usage:
  467. *
  468. * Please check out the Syntax Highlighting page in Components for complete markup examples
  469. *
  470. */
  471. static jsHighlightjs() {
  472. // Init Highlight.js
  473. if (!hljs.isHighlighted) {
  474. hljs.initHighlighting();
  475. }
  476. }
  477. /*
  478. * Flatpickr init, for more examples you can check out https://github.com/flatpickr/flatpickr
  479. *
  480. * Helpers.run('js-flatpickr');
  481. *
  482. * Example usage:
  483. *
  484. * <input type="text" class="js-flatpickr form-control">
  485. *
  486. */
  487. static jsFlatpickr() {
  488. let elements = document.querySelectorAll('.js-flatpickr:not(.js-flatpickr-enabled)');
  489. elements.forEach(el => {
  490. // Add .js-flatpickr-enabled class to tag it as activated
  491. el.classList.add('js-flatpickr-enabled');
  492. // Init it
  493. flatpickr(el);
  494. });
  495. }
  496. /*
  497. ********************************************************************************************
  498. *
  499. * Init helpers for jQuery plugins
  500. *
  501. ********************************************************************************************
  502. */
  503. /*
  504. * jQuery Appear, for more examples you can check out https://github.com/bas2k/jquery.appear
  505. *
  506. * Helpers.run('jq-appear');
  507. *
  508. * Example usage (the following div will appear on scrolling, remember to add the invisible class):
  509. *
  510. * <div class="invisible" data-toggle="appear">...</div>
  511. *
  512. */
  513. static jqAppear() {
  514. // Add a specific class on elements (when they become visible on scrolling)
  515. jQuery('[data-toggle="appear"]:not(.js-appear-enabled)').each((index, element) => {
  516. let windowW = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
  517. let el = jQuery(element);
  518. let elCssClass = el.data('class') || 'animated fadeIn';
  519. let elOffset = el.data('offset') || 0;
  520. let elTimeout = (windowW < 992) ? 0 : (el.data('timeout') ? el.data('timeout') : 0);
  521. // Add .js-appear-enabled class to tag it as activated and init it
  522. el.addClass('js-appear-enabled').appear(() => {
  523. setTimeout(() => {
  524. el.removeClass('invisible').addClass(elCssClass);
  525. }, elTimeout);
  526. }, { accY: elOffset });
  527. });
  528. }
  529. /*
  530. * Magnific Popup functionality, for more examples you can check out http://dimsemenov.com/plugins/magnific-popup/
  531. *
  532. * Helpers.run('jq-magnific-popup');
  533. *
  534. * Example usage:
  535. *
  536. * Please check out the Gallery page in Components for complete markup examples
  537. *
  538. */
  539. static jqMagnific() {
  540. // Gallery init
  541. jQuery('.js-gallery:not(.js-gallery-enabled)').each((index, element) => {
  542. // Add .js-gallery-enabled class to tag it as activated and init it
  543. jQuery(element).addClass('js-gallery-enabled').magnificPopup({
  544. delegate: 'a.img-lightbox',
  545. type: 'image',
  546. gallery: {
  547. enabled: true
  548. }
  549. });
  550. });
  551. }
  552. /*
  553. * Slick init, for more examples you can check out http://kenwheeler.github.io/slick/
  554. *
  555. * Helpers.run('jq-slick');
  556. *
  557. * Example usage:
  558. *
  559. * <div class="js-slider">
  560. * <div>Slide #1</div>
  561. * <div>Slide #2</div>
  562. * <div>Slide #3</div>
  563. * </div>
  564. *
  565. */
  566. static jqSlick() {
  567. // Get each slider element (with .js-slider class)
  568. jQuery('.js-slider:not(.js-slider-enabled)').each((index, element) => {
  569. let el = jQuery(element);
  570. // Add .js-slider-enabled class to tag it as activated and init it
  571. el.addClass('js-slider-enabled').slick({
  572. arrows: el.data('arrows') || false,
  573. dots: el.data('dots') || false,
  574. slidesToShow: el.data('slides-to-show') || 1,
  575. centerMode: el.data('center-mode') || false,
  576. autoplay: el.data('autoplay') || false,
  577. autoplaySpeed: el.data('autoplay-speed') || 3000,
  578. infinite: typeof el.data('infinite') === 'undefined' ? true : el.data('infinite')
  579. });
  580. });
  581. }
  582. /*
  583. * Bootstrap Datepicker init, for more examples you can check out https://github.com/eternicode/bootstrap-datepicker
  584. *
  585. * Helpers.run('jq-datepicker');
  586. *
  587. * Example usage:
  588. *
  589. * <input type="text" class="js-datepicker form-control">
  590. *
  591. */
  592. static jqDatepicker() {
  593. // Init datepicker (with .js-datepicker and .input-daterange class)
  594. jQuery('.js-datepicker:not(.js-datepicker-enabled)').add('.input-daterange:not(.js-datepicker-enabled)').each((index, element) => {
  595. let el = jQuery(element);
  596. // Add .js-datepicker-enabled class to tag it as activated and init it
  597. el.addClass('js-datepicker-enabled').datepicker({
  598. weekStart: el.data('week-start') || 0,
  599. autoclose: el.data('autoclose') || false,
  600. todayHighlight: el.data('today-highlight') || false,
  601. startDate: el.data('start-date') || false,
  602. container: el.data('container') || '#page-container',
  603. orientation: 'bottom' // Position issue when using BS5, set it to bottom until officially supported
  604. });
  605. });
  606. }
  607. /*
  608. * Masked Inputs, for more examples you can check out https://github.com/digitalBush/jquery.maskedinput
  609. *
  610. * Helpers.run('jq-masked-inputs');
  611. *
  612. * Example usage:
  613. *
  614. * Please check out the Form plugins page for complete markup examples
  615. *
  616. */
  617. static jqMaskedInputs() {
  618. // Init Masked Inputs
  619. // a - Represents an alpha character (A-Z,a-z)
  620. // 9 - Represents a numeric character (0-9)
  621. // * - Represents an alphanumeric character (A-Z,a-z,0-9)
  622. jQuery('.js-masked-date:not(.js-masked-enabled)').mask('99/99/9999');
  623. jQuery('.js-masked-date-dash:not(.js-masked-enabled)').mask('99-99-9999');
  624. jQuery('.js-masked-phone:not(.js-masked-enabled)').mask('(999) 999-9999');
  625. jQuery('.js-masked-phone-ext:not(.js-masked-enabled)').mask('(999) 999-9999? x99999');
  626. jQuery('.js-masked-taxid:not(.js-masked-enabled)').mask('99-9999999');
  627. jQuery('.js-masked-ssn:not(.js-masked-enabled)').mask('999-99-9999');
  628. jQuery('.js-masked-pkey:not(.js-masked-enabled)').mask('a*-999-a999');
  629. jQuery('.js-masked-time:not(.js-masked-enabled)').mask('99:99');
  630. jQuery('.js-masked-date')
  631. .add('.js-masked-date-dash')
  632. .add('.js-masked-phone')
  633. .add('.js-masked-phone-ext')
  634. .add('.js-masked-taxid')
  635. .add('.js-masked-ssn')
  636. .add('.js-masked-pkey')
  637. .add('.js-masked-time')
  638. .addClass('js-masked-enabled');
  639. }
  640. /*
  641. * Select2, for more examples you can check out https://github.com/select2/select2
  642. *
  643. * Helpers.run('jq-select2');
  644. *
  645. * Example usage:
  646. *
  647. * <select class="js-select2 form-control" style="width: 100%;" data-placeholder="Choose one..">
  648. * <option></option><!-- Required for data-placeholder attribute to work with Select2 plugin -->
  649. * <option value="1">HTML</option>
  650. * <option value="2">CSS</option>
  651. * <option value="3">Javascript</option>
  652. * </select>
  653. *
  654. */
  655. static jqSelect2() {
  656. // Init Select2 (with .js-select2 class)
  657. jQuery('.js-select2:not(.js-select2-enabled)').each((index, element) => {
  658. let el = jQuery(element);
  659. // Add .js-select2-enabled class to tag it as activated and init it
  660. el.addClass('js-select2-enabled').select2({
  661. placeholder: el.data('placeholder') || false,
  662. dropdownParent: document.querySelector(el.data('container') || '#page-container'),
  663. });
  664. });
  665. }
  666. /*
  667. * Bootstrap Notify, for more examples you can check out http://bootstrap-growl.remabledesigns.com/
  668. *
  669. * Helpers.run('jq-notify');
  670. *
  671. * Example usage:
  672. *
  673. * Please check out the Notifications page for examples
  674. *
  675. */
  676. static jqNotify(options = {}) {
  677. if (jQuery.isEmptyObject(options)) {
  678. // Init notifications (with .js-notify class)
  679. jQuery('.js-notify:not(.js-notify-enabled)').each((index, element) => {
  680. // Add .js-notify-enabled class to tag it as activated and init it
  681. jQuery(element).addClass('js-notify-enabled').on('click.pixelcave.helpers', e => {
  682. let el = jQuery(e.currentTarget);
  683. // Create notification
  684. jQuery.notify({
  685. icon: el.data('icon') || '',
  686. message: el.data('message'),
  687. url: el.data('url') || ''
  688. },
  689. {
  690. element: 'body',
  691. type: el.data('type') || 'info',
  692. placement: {
  693. from: el.data('from') || 'top',
  694. align: el.data('align') || 'right'
  695. },
  696. allow_dismiss: true,
  697. newest_on_top: true,
  698. showProgressbar: false,
  699. offset: 20,
  700. spacing: 10,
  701. z_index: 1033,
  702. delay: 5000,
  703. timer: 1000,
  704. animate: {
  705. enter: 'animated fadeIn',
  706. exit: 'animated fadeOutDown'
  707. },
  708. template: `<div data-notify="container" class="col-11 col-sm-4 alert alert-{0} alert-dismissible" role="alert">
  709. <p class="mb-0">
  710. <span data-notify="icon"></span>
  711. <span data-notify="title">{1}</span>
  712. <span data-notify="message">{2}</span>
  713. </p>
  714. <div class="progress" data-notify="progressbar">
  715. <div class="progress-bar progress-bar-{0}" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%;"></div>
  716. </div>
  717. <a href="{3}" target="{4}" data-notify="url"></a>
  718. <a class="p-2 m-1 text-dark" href="javascript:void(0)" aria-label="Close" data-notify="dismiss">
  719. <i class="fa fa-times"></i>
  720. </a>
  721. </div>`
  722. });
  723. });
  724. });
  725. } else {
  726. // Create notification
  727. jQuery.notify({
  728. icon: options.icon || '',
  729. message: options.message,
  730. url: options.url || ''
  731. },
  732. {
  733. element: options.element || 'body',
  734. type: options.type || 'info',
  735. placement: {
  736. from: options.from || 'top',
  737. align: options.align || 'right'
  738. },
  739. allow_dismiss: (options.allow_dismiss === false) ? false : true,
  740. newest_on_top: (options.newest_on_top === false) ? false : true,
  741. showProgressbar: options.show_progress_bar ? true : false,
  742. offset: options.offset || 20,
  743. spacing: options.spacing || 10,
  744. z_index: options.z_index || 1033,
  745. delay: options.delay || 5000,
  746. timer: options.timer || 1000,
  747. animate: {
  748. enter: options.animate_enter || 'animated fadeIn',
  749. exit: options.animate_exit || 'animated fadeOutDown'
  750. },
  751. template: `<div data-notify="container" class="col-11 col-sm-4 alert alert-{0} alert-dismissible" role="alert">
  752. <p class="mb-0">
  753. <span data-notify="icon"></span>
  754. <span data-notify="title">{1}</span>
  755. <span data-notify="message">{2}</span>
  756. </p>
  757. <div class="progress" data-notify="progressbar">
  758. <div class="progress-bar progress-bar-{0}" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%;"></div>
  759. </div>
  760. <a href="{3}" target="{4}" data-notify="url"></a>
  761. <a class="p-2 m-1 text-dark" href="javascript:void(0)" aria-label="Close" data-notify="dismiss">
  762. <i class="fa fa-times"></i>
  763. </a>
  764. </div>`
  765. });
  766. }
  767. }
  768. /*
  769. * Easy Pie Chart, for more examples you can check out http://rendro.github.io/easy-pie-chart/
  770. *
  771. * Helpers.run('jq-easy-pie-chart');
  772. *
  773. * Example usage:
  774. *
  775. * <div class="js-pie-chart pie-chart" data-percent="25" data-line-width="2" data-size="100">
  776. * <span>..Content..</span>
  777. * </div>
  778. *
  779. */
  780. static jqEasyPieChart() {
  781. // Init Easy Pie Charts (with .js-pie-chart class)
  782. jQuery('.js-pie-chart:not(.js-pie-chart-enabled)').each((index, element) => {
  783. let el = jQuery(element);
  784. // Add .js-pie-chart-enabled class to tag it as activated and init it
  785. el.addClass('js-pie-chart-enabled').easyPieChart({
  786. barColor: el.data('bar-color') || '#777777',
  787. trackColor: el.data('track-color') || '#eeeeee',
  788. lineWidth: el.data('line-width') || 3,
  789. size: el.data('size') || '80',
  790. animate: el.data('animate') || 750,
  791. scaleColor: el.data('scale-color') || false
  792. });
  793. });
  794. }
  795. /*
  796. * Bootstrap Maxlength, for more examples you can check out https://github.com/mimo84/bootstrap-maxlength
  797. *
  798. * Helpers.run('jq-maxlength');
  799. *
  800. * Example usage:
  801. *
  802. * <input type="text" class="js-maxlength form-control" maxlength="20">
  803. *
  804. */
  805. static jqMaxlength() {
  806. // Init Bootstrap Maxlength (with .js-maxlength class)
  807. jQuery('.js-maxlength:not(.js-maxlength-enabled)').each((index, element) => {
  808. let el = jQuery(element);
  809. // Add .js-maxlength-enabled class to tag it as activated and init it
  810. el.addClass('js-maxlength-enabled').maxlength({
  811. alwaysShow: el.data('always-show') ? true : false,
  812. threshold: el.data('threshold') || 10,
  813. warningClass: el.data('warning-class') || 'badge bg-warning',
  814. limitReachedClass: el.data('limit-reached-class') || 'badge bg-danger',
  815. placement: el.data('placement') || 'bottom',
  816. preText: el.data('pre-text') || '',
  817. separator: el.data('separator') || '/',
  818. postText: el.data('post-text') || ''
  819. });
  820. });
  821. }
  822. /*
  823. * Ion Range Slider, for more examples you can check out https://github.com/IonDen/ion.rangeSlider
  824. *
  825. * Helpers.run('jq-rangeslider');
  826. *
  827. * Example usage:
  828. *
  829. * <input type="text" class="js-rangeslider form-control" value="50">
  830. *
  831. */
  832. static jqRangeslider() {
  833. // Init Ion Range Slider (with .js-rangeslider class)
  834. jQuery('.js-rangeslider:not(.js-rangeslider-enabled)').each((index, element) => {
  835. let el = jQuery(element);
  836. // Add .js-rangeslider-enabled class to tag it as activated and init it
  837. jQuery(element).addClass('js-rangeslider-enabled').ionRangeSlider({
  838. input_values_separator: ';',
  839. skin: el.data('skin') || 'round'
  840. });
  841. });
  842. }
  843. /*
  844. * Password Strength Meter, for more examples you can check out https://github.com/ablanco/jquery.pwstrength.bootstrap
  845. *
  846. * Helpers.run('jq-pw-strength');
  847. *
  848. * Example usage:
  849. *
  850. * <div class="js-pw-strength-container mb-4">
  851. * <label class="form-label" for="example-pw-strength1">Password</label>
  852. * <input type="password" class="js-pw-strength form-control" id="example-pw-strength1" name="example-pw-strength1">
  853. * <div class="js-pw-strength-progress pw-strength-progress mt-1"></div>
  854. * <p class="js-pw-strength-feedback form-text mb-0"></p>
  855. * </div>
  856. *
  857. */
  858. static jqPwStrength() {
  859. // Init Password Strength Meter (with .js-pw-strength class)
  860. jQuery('.js-pw-strength:not(.js-pw-strength-enabled)').each((index, element) => {
  861. let el = jQuery(element);
  862. let container = el.parents('.js-pw-strength-container');
  863. let progress = jQuery('.js-pw-strength-progress', container);
  864. let verdict = jQuery('.js-pw-strength-feedback', container);
  865. // Add .js-pw-strength-enabled class to tag it as activated and init it
  866. el.addClass('js-pw-strength-enabled').pwstrength({
  867. ui: {
  868. container: container,
  869. viewports: {
  870. progress: progress,
  871. verdict: verdict
  872. }
  873. }
  874. });
  875. });
  876. }
  877. /*
  878. * jQuery Sparkline Charts, for more examples you can check out http://omnipotent.net/jquery.sparkline/#s-docs
  879. *
  880. * Helpers.run('jq-sparkline');
  881. *
  882. * Example usage:
  883. *
  884. * <span class="js-sparkline" data-type="line" data-points="[10,20,30,25,15,40,45]"></span>
  885. *
  886. */
  887. static jqSparkline() {
  888. let self = this;
  889. // Init jQuery Sparkline Charts (with .js-sparkline class)
  890. jQuery('.js-sparkline:not(.js-sparkline-enabled)').each((index, element) => {
  891. let el = jQuery(element);
  892. let type = el.data('type');
  893. let options = {};
  894. // Sparkline types
  895. let types = {
  896. line: () => {
  897. options['type'] = type;
  898. options['lineWidth'] = el.data('line-width') || 2;
  899. options['lineColor'] = el.data('line-color') || '#0665d0';
  900. options['fillColor'] = el.data('fill-color') || '#0665d0';
  901. options['spotColor'] = el.data('spot-color') || '#495057';
  902. options['minSpotColor'] = el.data('min-spot-color') || '#495057';
  903. options['maxSpotColor'] = el.data('max-spot-color') || '#495057';
  904. options['highlightSpotColor'] = el.data('highlight-spot-color') || '#495057';
  905. options['highlightLineColor'] = el.data('highlight-line-color') || '#495057';
  906. options['spotRadius'] = el.data('spot-radius') || 2;
  907. options['tooltipFormat'] = '{{prefix}}{{y}}{{suffix}}';
  908. },
  909. bar: () => {
  910. options['type'] = type;
  911. options['barWidth'] = el.data('bar-width') || 8;
  912. options['barSpacing'] = el.data('bar-spacing') || 6;
  913. options['barColor'] = el.data('bar-color') || '#0665d0';
  914. options['tooltipFormat'] = '{{prefix}}{{value}}{{suffix}}';
  915. },
  916. pie: () => {
  917. options['type'] = type;
  918. options['sliceColors'] = ['#fadb7d', '#faad7d', '#75b0eb', '#abe37d'];
  919. options['highlightLighten'] = el.data('highlight-lighten') || 1.1;
  920. options['tooltipFormat'] = '{{prefix}}{{value}}{{suffix}}';
  921. },
  922. tristate: () => {
  923. options['type'] = type;
  924. options['barWidth'] = el.data('bar-width') || 8;
  925. options['barSpacing'] = el.data('bar-spacing') || 6;
  926. options['posBarColor'] = el.data('pos-bar-color') || '#82b54b';
  927. options['negBarColor'] = el.data('neg-bar-color') || '#e04f1a';
  928. }
  929. };
  930. // If the correct type is set init the chart
  931. if (types[type]) {
  932. types[type]();
  933. // Extra options added only if specified
  934. if (type === 'line') {
  935. if (el.data('chart-range-min') >= 0 || el.data('chart-range-min')) {
  936. options['chartRangeMin'] = el.data('chart-range-min');
  937. }
  938. if (el.data('chart-range-max') >= 0 || el.data('chart-range-max')) {
  939. options['chartRangeMax'] = el.data('chart-range-max');
  940. }
  941. }
  942. // Add common options used in all types
  943. options['width'] = el.data('width') || '120px';
  944. options['height'] = el.data('height') || '80px';
  945. options['tooltipPrefix'] = el.data('tooltip-prefix') ? el.data('tooltip-prefix') + ' ' : '';
  946. options['tooltipSuffix'] = el.data('tooltip-suffix') ? ' ' + el.data('tooltip-suffix') : '';
  947. // If we need a responsive width for the chart, then don't add .js-sparkline-enabled class and re-run the helper on window resize
  948. if (options['width'] === '100%') {
  949. if (!jqSparklineResize) {
  950. // Make sure that we bind the event only once
  951. jqSparklineResize = true;
  952. // On window resize, re-run the Sparkline helper
  953. jQuery(window).on('resize.pixelcave.helpers.sparkline', function (e) {
  954. clearTimeout(jqSparklineTimeout);
  955. jqSparklineTimeout = setTimeout(() => {
  956. self.sparkline();
  957. }, 500);
  958. });
  959. }
  960. } else {
  961. // It has a specific width (it doesn't need to re-init again on resize), so add .js-sparkline-enabled class to tag it as activated
  962. jQuery(element).addClass('js-sparkline-enabled');
  963. }
  964. // Finally init it
  965. jQuery(element).sparkline(el.data('points') || [0], options);
  966. } else {
  967. console.log('[jQuery Sparkline JS Helper] Please add a correct type (line, bar, pie or tristate) in all your elements with \'js-sparkline\' class.')
  968. }
  969. });
  970. }
  971. /*
  972. * jQuery Validation, for more examples you can check out https://github.com/jzaefferer/jquery-validation
  973. *
  974. * Helpers.run('jq-validation');
  975. *
  976. * Example usage:
  977. *
  978. * By calling the helper, you set up the default options that will be used for jQuery Validation
  979. *
  980. */
  981. static jqValidation() {
  982. // Set default options for jQuery Validation plugin
  983. jQuery.validator.setDefaults({
  984. errorClass: 'invalid-feedback animated fadeIn',
  985. errorElement: 'div',
  986. errorPlacement: (error, el) => {
  987. jQuery(el).addClass('is-invalid');
  988. jQuery(el).parents('div:not(.input-group)').first().append(error);
  989. },
  990. highlight: el => {
  991. jQuery(el).parents('div:not(.input-group)').first().find('.is-invalid').removeClass('is-invalid').addClass('is-invalid');
  992. },
  993. success: el => {
  994. jQuery(el).parents('div:not(.input-group)').first().find('.is-invalid').removeClass('is-invalid');
  995. jQuery(el).remove();
  996. }
  997. });
  998. // Add custom email validation rule which requires a dot after @
  999. jQuery.validator.addMethod('emailWithDot', function(value, element) {
  1000. let reg = /^([a-z\d!#$%&'*+\-\/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+(\.[a-z\d!#$%&'*+\-\/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)*|"((([ \t]*\r\n)?[ \t]+)?([\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|\\[\x01-\x09\x0b\x0c\x0d-\x7f\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))*(([ \t]*\r\n)?[ \t]+)?")@(([a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.)+([a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.?$/i;
  1001. return this.optional(element) || reg.test(value);
  1002. }, 'Please enter a valid email address');
  1003. }
  1004. }