'use strict'; require('../util/polyfill.js'); const api = require('../api.js'); const templates = require('../templates.js'); const tags = require('../tags.js'); const domParser = new DOMParser(); const misc = require('./misc.js'); function _imbueId(options) { if (!options.id) { options.id = 'gen-' + Math.random().toString(36).substring(7); } } function _makeLabel(options, attrs) { if (!options.text) { return ''; } if (!attrs) { attrs = {}; } attrs.for = options.id; return makeElement('label', attrs, options.text); } function makeFileSize(fileSize) { return misc.formatFileSize(fileSize); } function makeMarkdown(text) { return misc.formatMarkdown(text); } function makeRelativeTime(time) { return makeElement( 'time', {datetime: time, title: time}, misc.formatRelativeTime(time)); } function makeThumbnail(url) { return makeElement( 'span', url ? {class: 'thumbnail', style: `background-image: url(\'${url}\')`} : {class: 'thumbnail empty'}, makeElement('img', {alt: 'thumbnail', src: url})); } function makeRadio(options) { _imbueId(options); return makeElement( 'label', {for: options.id}, makeElement( 'input', { id: options.id, name: options.name, value: options.value, type: 'radio', checked: options.selectedValue === options.value, disabled: options.readonly, required: options.required, }), makeElement('span', {class: 'radio'}, options.text)); } function makeCheckbox(options) { _imbueId(options); return makeElement( 'label', {for: options.id}, makeElement( 'input', { id: options.id, name: options.name, value: options.value, type: 'checkbox', checked: options.checked !== undefined ? options.checked : false, disabled: options.readonly, required: options.required, }), makeElement('span', {class: 'checkbox'}, options.text)); } function makeSelect(options) { return _makeLabel(options) + makeElement( 'select', { id: options.id, name: options.name, disabled: options.readonly, }, ...Object.keys(options.keyValues).map(key => makeElement( 'option', {value: key, selected: key === options.selectedKey}, options.keyValues[key]))); } function makeInput(options) { options.value = options.value || ''; return _makeLabel(options) + makeElement('input', options); } function makeButton(options) { options.type = 'button'; return makeInput(options); } function makeTextInput(options) { options.type = 'text'; return makeInput(options); } function makeTextarea(options) { const value = options.value || ''; delete options.value; return _makeLabel(options) + makeElement('textarea', options, value); } function makePasswordInput(options) { options.type = 'password'; return makeInput(options); } function makeEmailInput(options) { options.type = 'email'; return makeInput(options); } function makeColorInput(options) { const textInput = makeElement( 'input', { type: 'text', value: options.value || '', required: options.required, style: 'color: ' + options.value, disabled: true, }); const colorInput = makeElement( 'input', {type: 'color', value: options.value || ''}); return makeElement('label', {class: 'color'}, colorInput, textInput); } function makeNumericInput(options) { options.type = 'number'; return makeInput(options); } function getPostUrl(id, parameters) { let url = '/post/' + encodeURIComponent(id); if (parameters && parameters.query) { url += '/query=' + encodeURIComponent(parameters.query); } return url; } function getPostEditUrl(id, parameters) { let url = '/post/' + encodeURIComponent(id) + '/edit'; if (parameters && parameters.query) { url += '/query=' + encodeURIComponent(parameters.query); } return url; } function makePostLink(id, includeHash) { let text = id; if (includeHash) { text = '@' + id; } return api.hasPrivilege('posts:view') ? makeElement( 'a', {'href': '/post/' + encodeURIComponent(id)}, misc.escapeHtml(text)) : misc.escapeHtml(text); } function makeTagLink(name, includeHash) { const tag = tags.getTagByName(name); const category = tag ? tag.category : 'unknown'; let text = name; if (includeHash === true) { text = '#' + text; } return api.hasPrivilege('tags:view') ? makeElement( 'a', { 'href': '/tag/' + encodeURIComponent(name), 'class': misc.makeCssName(category, 'tag'), }, misc.escapeHtml(text)) : makeElement( 'span', {'class': misc.makeCssName(category, 'tag')}, misc.escapeHtml(text)); } function makeUserLink(user) { let text = makeThumbnail(user ? user.avatarUrl : null); text += user && user.name ? misc.escapeHtml(user.name) : 'Anonymous'; const link = user && api.hasPrivilege('users:view') ? makeElement( 'a', {'href': '/user/' + encodeURIComponent(user.name)}, text) : text; return makeElement('span', {class: 'user'}, link); } function makeFlexboxAlign(options) { return [...misc.range(20)] .map(() => '
').join(''); } function makeAccessKey(html, key) { const regex = new RegExp('(' + key + ')', 'i'); html = html.replace( regex, '$1'); return html; } function _serializeElement(name, attributes) { return [name] .concat(Object.keys(attributes).map(key => { if (attributes[key] === true) { return key; } else if (attributes[key] === false || attributes[key] === undefined) { return ''; } const attribute = misc.escapeHtml(attributes[key] || ''); return `${key}="${attribute}"`; })) .join(' '); } function makeElement(name, attrs, ...content) { return content.length !== undefined ? `<${_serializeElement(name, attrs)}>${content.join('')}${name}>` : `<${_serializeElement(name, attrs)}/>`; } function emptyContent(target) { while (target.lastChild) { target.removeChild(target.lastChild); } } function replaceContent(target, source) { emptyContent(target); if (source instanceof NodeList) { for (let child of [...source]) { target.appendChild(child); } } else if (source instanceof Node) { target.appendChild(source); } else if (source !== null) { throw `Invalid view source: ${source}`; } } function showMessage(target, message, className) { if (!message) { message = 'Unknown message'; } const messagesHolder = target.querySelector('.messages'); if (!messagesHolder) { return false; } /* TODO: animate this */ const node = document.createElement('div'); node.innerHTML = message.replace(/\n/g, '