import Templates from '../templates/Views' import { createNode, destroyNode, findDates, formatDate, parseURL, replaceURLPathname, ucWords } from '../helpers' import NotesAPI from './NotesAPI' import ValidationController from './ValidationController' import Alert from './AlertController' export default class DOMController { constructor (root, routes) { this.routes = routes this.root = root try { this.root.innerHTML = Templates.grid this.renderRoutes() } catch (e) { Alert.displayDialog(e.toString(), 0, 3) } this.btnCreateNote = this.root.querySelector(window.env.querySelectors.btnCreateNote) this.btnPrepopulate = this.root.querySelector(window.env.querySelectors.btnPrepopulate) this.createNotesGrid(window.env.prePopulateAmount || 0) this.listener([ [this.btnCreateNote, 'click', (this.btnCreateNote.dataset.action || 'showError'), { legend: 'Create Note' }], [this.btnPrepopulate, 'click', (this.btnPrepopulate.dataset.action || 'showError')], ]) } /** * */ renderRoutes () { for (const [route, name] of Object.entries(this.routes.routes)) { const tab = createNode('li') tab.classList.add('nav-tab') if (route === '/') { tab.classList.add('initial') tab.classList.add('uk-active') } tab.innerHTML = `${name}` const anchor = tab.querySelector('a') this.routes.navbar.appendChild(tab) this.listener([anchor, 'click', 'createNotesGridByRoute', anchor]) } } createNotesGridByRoute (navTab) { const pathname = parseURL(navTab.href).pathname const route = pathname.slice(1).split('\/')[0] window.history.pushState('', navTab.innerText, pathname) //change url without reload //history.pushState({}, null, pathname) //change url this.routes.navbar.querySelector('.uk-active').classList.remove('uk-active') navTab.closest('.nav-tab').classList.add('uk-active') document.getElementById(window.env.querySelectors.analyticsGrid.slice(1)).classList.add('uk-hidden') document.getElementById(window.env.querySelectors.notesGrid.slice(1)).classList.remove('uk-hidden') switch (route) { case 'all': this.createNotesGrid(0, null) break case '': this.createNotesGrid(window.env.prePopulateAmount || 0) break case 'archive': this.createNotesGrid(window.env.prePopulateAmount || 0, true) break case 'analytics': document.getElementById(window.env.querySelectors.notesGrid.slice(1)).classList.add('uk-hidden') const analyticsGrid = document.getElementById(window.env.querySelectors.analyticsGrid.slice(1)) this.createAnalyticsGrid(analyticsGrid) break } } prePopulate () { const fakerAmount = document.querySelector(env.querySelectors.selectPrepopulate) if (+fakerAmount.value <= 0) { return } env.fakerAmount = (+fakerAmount.value > 44) ? 44 : +fakerAmount.value localStorage.removeItem(window.env.localStorageKey) fakerAmount.selectedIndex = 0 document.querySelector('.nav-tab.uk-active > a').click() } /** * The method is being called for typical DOM-event task inside the table row * to manipulate with records: deleting, editing, archiving. * * @param node * @param method */ noteToAction (node, method) { const row = node.closest('tr') NotesAPI[method](parseInt(row.dataset.id, 10)) row.classList.add('as-removing') setTimeout(() => { row.remove() document.querySelector('.nav-tab.uk-active > a').click() }, 1050) } /** * * @param node */ noteToArchive (node) { this.noteToAction(node, 'archiveNote') } /** * * @param node */ noteFromArchive (node) { this.noteToAction(node, 'unArchiveNote') } /** * * @param node */ noteToTrash (node) { this.noteToAction(node, 'deleteNote') } createAnalyticsGrid (grid) { grid.classList.remove('uk-hidden') const aGrid = grid.querySelector(' tbody') aGrid.innerHTML = '' for (const [key, value] of Object.entries(NotesAPI.getAnalytics())) { const row = createNode('tr') const td = createNode('td', {}, ucWords(key)) row.appendChild(td) for (const [key, val] of Object.entries( value ) ) { const td = createNode('td', {}, val.toString()) row.appendChild(td) } aGrid.appendChild(row) } } /** * * @param len * @param archived */ createNotesGrid (len = window.env.prePopulateAmount || 0, archived = false) { const notesGrid = document.querySelector(window.env.querySelectors.notesGrid + ' tbody') notesGrid.innerHTML = '' const notes = NotesAPI.getNotesSanitized(len, archived) if (notes.length === 0) { return } notes.forEach(el => { const row = createNode('tr') row.dataset.id = el.id window.env.gridOrder.notes.forEach(key => { let attrs = {} let value = el[key] || '' switch (key) { case 'createdAt': value = formatDate(value) break case 'category': value = ucWords(value) break case 'title': attrs.class = 'uk-text-bold' break case 'dates': attrs.style = 'font-size: 12px' value = findDates(el.content).join(', ') break case 'edit' : attrs.class = 'grid-control icon icon-edit' attrs['data-action'] = 'getEditForm' break case 'archive' : attrs.class = 'grid-control icon ' + (value === 'on' ? 'icon-unarchive' : 'icon-archive') attrs['data-action'] = value === 'on' ? 'noteFromArchive' : 'noteToArchive' value = '' break case 'delete' : attrs.class = 'icon icon-delete grid-control' attrs['data-action'] = 'noteToTrash' break } const td = createNode('td', attrs, value) if (key.match(/^(edit|archive|delete)$/)) { this.listener([td, 'click', td.dataset.action, td]) } row.appendChild(td) }) notesGrid.appendChild(row) }) } /** * * @param selector * @returns {{}} */ static getFormData (selector = window.env.querySelectors.noteEditForm) { const formData = new FormData(document.querySelector(selector)) const data = {} for (let key of formData.keys()) { data[key] = formData.get(key) } return data } /** * * @param args */ getEditForm (args = {}) { const node = createNode('div', { class: window.env.querySelectors.modal.slice(1) }, Templates.templates.editForm) if (args.legend) { node.querySelector('legend').innerHTML = args.legend } else { DOMController.fillTheForm(node, args) } this.root.appendChild(node) node.style.display = 'block' const contentInput = node.querySelector('textarea[maxlength]') const titleInput = node.querySelector('input[maxlength]') this.listener([ [node.querySelector(window.env.querySelectors.btnDestroyModal), 'click', destroyNode, node], //Remove modal dialog [node.querySelector('form'), 'submit', NotesAPI.saveNote], [contentInput, 'keydown', ValidationController.lengthOnKeyUp, contentInput], [titleInput, 'keydown', ValidationController.lengthOnKeyUp, titleInput], ]) } /** * * @param $form * @param $el */ static fillTheForm ($form, $el) { if ($el instanceof Element) { const note = NotesAPI.getNoteByID(+$el.closest('tr').dataset.id) if (note.length === 1) { for (const [key, value] of Object.entries(note[0])) { const $input = $form.querySelector(`[name='${key}']`) if ($input instanceof Element) { if ($input.type === 'checkbox') { if (value !== '') { $input.click() } } else { $input.value = value } } } } } } /** * The function takes single DOM element or list of them * * @param els * @returns {void|*} */ listener (els) { return els[0] instanceof Element ? this.on(els) : els.forEach(el => this.on(el)) } /** * TODO: guessed this should be rethought * @param args */ on (args) { const [selector, event, method] = args if (!(selector instanceof Element)) { Alert.displayDialog(`Internal Error: Try reload a page`, 3000, 3) return false } try { selector.addEventListener(event, evt => { const params = args[3] || {} if (selector.tagName === 'A') { evt.preventDefault() } if (evt.type === 'submit') { evt.preventDefault() setTimeout(() => { document.querySelector('.nav-tab.uk-active > a').click() }, 300) } if (typeof this[method] === 'function') { this[method](params) } else if (typeof method === 'function') { method(params) } }) return true } catch (err) { Alert.displayDialog(`Error: ${err.toString()}`, 3000, 3) return false } } }