diff --git a/src/_h5ai/private/conf/options.json b/src/_h5ai/private/conf/options.json index 8b1d88ec..9bc3e2ea 100644 --- a/src/_h5ai/private/conf/options.json +++ b/src/_h5ai/private/conf/options.json @@ -149,7 +149,7 @@ - ignorecase: boolean, ignore case */ "filter": { - "enabled": false, + "enabled": true, "advanced": true, "debounceTime": 100, "ignorecase": true @@ -319,7 +319,7 @@ - ignorecase: boolean, ignore case */ "search": { - "enabled": true, + "enabled": false, "advanced": true, "debounceTime": 300, "ignorecase": true diff --git a/src/_h5ai/private/php/class-bootstrap.php b/src/_h5ai/private/php/class-bootstrap.php index d67cf586..306b4c37 100644 --- a/src/_h5ai/private/php/class-bootstrap.php +++ b/src/_h5ai/private/php/class-bootstrap.php @@ -11,7 +11,7 @@ class Bootstrap { session_start(); $session = new Session($_SESSION); - $request = new Request($_REQUEST); + $request = new Request($_REQUEST, file_get_contents('php://input')); $setup = new Setup($request->query_boolean('refresh', false)); $context = new Context($session, $request, $setup); diff --git a/src/_h5ai/private/php/core/class-request.php b/src/_h5ai/private/php/core/class-request.php index 873a2876..95bae09b 100644 --- a/src/_h5ai/private/php/core/class-request.php +++ b/src/_h5ai/private/php/core/class-request.php @@ -3,8 +3,9 @@ class Request { private $params; - public function __construct($params) { - $this->params = $params; + public function __construct($params, $body) { + $data = json_decode($body, true); + $this->params = $data !== null ? $data : $params; } public function query($keypath = '', $default = Util::NO_DEFAULT) { diff --git a/src/_h5ai/public/css/lib/view/sidebar.less b/src/_h5ai/public/css/lib/view/sidebar.less index 4eb4a936..2a406f8a 100644 --- a/src/_h5ai/public/css/lib/view/sidebar.less +++ b/src/_h5ai/public/css/lib/view/sidebar.less @@ -1,5 +1,4 @@ #sidebar { - display: none; overflow-x: hidden; overflow-y: auto; flex: 0 0 auto; diff --git a/src/_h5ai/public/js/lib/core/location.js b/src/_h5ai/public/js/lib/core/location.js index 6ee48ac9..7c4dc6b7 100644 --- a/src/_h5ai/public/js/lib/core/location.js +++ b/src/_h5ai/public/js/lib/core/location.js @@ -150,8 +150,9 @@ const setLink = ($el, item) => { $el.attr('href', item.absHref); if (history && item.isFolder() && item.isManaged) { - $el.on('click', () => { + $el.on('click', ev => { setLocation(item.absHref); + ev.preventDefault(); return false; }); } diff --git a/src/_h5ai/public/js/lib/dom.js b/src/_h5ai/public/js/lib/dom.js new file mode 100644 index 00000000..9c44977b --- /dev/null +++ b/src/_h5ai/public/js/lib/dom.js @@ -0,0 +1,274 @@ +const {win} = require('./globals'); +const {each, filter, hasLength, is, isStr, map, isInstanceOf, toArray} = require('./lo'); + +const doc = win.document; + +const createElement = name => doc.createElement(name); +const CONTAINER_DIV = createElement('div'); +const CONTAINER_TABLE = createElement('table'); +const CONTAINER_TBODY = createElement('tbody'); +const CONTAINER_TR = createElement('tr'); +const CONTAINER_COLGROUP = createElement('colgroup'); + +const publish = (obj, arr) => { + each(arr, (el, idx) => { + obj[idx] = el; + }); + obj.length = arr.length; +}; + +const findContainer = str => { + if (/^ { + const container = findContainer(str); + container.innerHTML = str; + const res = toArray(container.childNodes); + each(res, el => container.removeChild(el)); + container.innerHTML = ''; + return res; +}; + +const queryAll = (selector, context) => { + try { + return toArray((context || doc).querySelectorAll(selector)); + } catch (err) {/* ignore */} + return []; +}; + +const isElement = x => isInstanceOf(x, win.Element); +const isDocument = x => isInstanceOf(x, win.Document); +const isWindow = x => is(x) && x.window === x && isDocument(x.document); +const isElDocWin = x => isElement(x) || isDocument(x) || isWindow(x); + +const addListener = (el, type, fn) => el.addEventListener(type, fn); +const removeListener = (el, type, fn) => el.removeEventListener(type, fn); + +const onReady = fn => { + if (/^(i|c|loade)/.test(doc.readyState)) { + fn(); + } else { + addListener(doc, 'DOMContentLoaded', fn); + } +}; + +const onLoad = fn => addListener(win, 'load', fn); + +const onResize = fn => { + addListener(win, 'resize', fn); +}; + +const onPrint = (before, after) => { + win.matchMedia('print').addListener(mql => { + if (mql.matches) { + before(); + } else { + after(); + } + }); +}; + +const dom = arg => { + if (isInstanceOf(arg, dom)) { + return arg; + } + + let els; + if (isStr(arg)) { + arg = arg.trim(); + els = arg[0] === '<' ? parseHtml(arg) : queryAll(arg); + } else if (isElDocWin(arg)) { + els = [arg]; + } else { + els = hasLength(arg) ? arg : [arg]; + } + els = filter(els, isElDocWin); + + const inst = Object.create(dom.prototype); + publish(inst, els); + return inst; +}; + +dom.prototype = { + constructor: dom, + + each(fn) { + each(this, fn); + return this; + }, + + map(fn) { + return map(this, fn); + }, + + find(selector) { + let els = []; + this.each(el => { + els = els.concat(queryAll(selector, el)); + }); + return dom(els); + }, + + on(type, fn) { + return this.each(el => addListener(el, type, fn)); + }, + + off(type, fn) { + return this.each(el => removeListener(el, type, fn)); + }, + + attr(key, value) { + if (value === undefined) { + return this.length ? this[0].getAttribute(key) : undefined; + } + return this.each(el => el.setAttribute(key, value)); + }, + + rmAttr(key) { + return this.each(el => el.removeAttribute(key)); + }, + + val(value) { + if (value === undefined) { + return this.length ? this[0].value : undefined; + } + return this.each(el => { + el.value = value; + }); + }, + + html(str) { + if (str === undefined) { + return this.map(el => el.innerHTML).join(''); + } + return this.each(el => { + el.innerHTML = str; + }); + }, + + text(str) { + if (str === undefined) { + return this.map(el => el.textContent).join(''); + } + return this.each(el => { + el.textContent = str; + }); + }, + + clr() { + return this.html(''); + }, + + rm() { + return this.each(el => { + const parent = el.parentNode; + if (parent) { + parent.removeChild(el); + } + }); + }, + + rpl(arg) { + return this.each(el => { + el.outerHTML = dom(arg).map(rplEl => rplEl.outerHTML).join(''); + }); + }, + + app(arg) { + return this.each(el => { + dom(arg).each(child => el.appendChild(child)); + }); + }, + + appTo(arg) { + dom(arg).app(this); + return this; + }, + + pre(arg) { + return this.each(el => { + dom(arg).each(child => { + const firstChild = el.firstChild; + if (!firstChild) { + el.appendChild(child); + } else { + el.insertBefore(child, firstChild); + } + }); + }); + }, + + preTo(arg) { + dom(arg).pre(this); + return this; + }, + + cls(...names) { + if (!names.length) { + return this.length ? toArray(this[0].classList) : []; + } + this.each(el => {el.className = '';}); + return this.addCls(...names); + }, + + hasCls(name) { + return toArray(this).every(el => el.classList.contains(name)); + }, + + addCls(...names) { + return this.each(el => { + for (const name of names) { + el.classList.add(name); + } + }); + }, + + rmCls(...names) { + return this.each(el => { + for (const name of names) { + el.classList.remove(name); + } + }); + }, + + parent() { + return dom(this.map(el => el.parentNode)); + }, + + children() { + return dom([].concat(...this.map(el => toArray(el.children)))); + }, + + hide() { + return this.addCls('hidden'); + }, + + show() { + return this.rmCls('hidden'); + } +}; + +module.exports = { + isElement, + isDocument, + isWindow, + isElDocWin, + onReady, + onLoad, + onResize, + onPrint, + dom +}; diff --git a/src/_h5ai/public/js/lib/ext/crumb.js b/src/_h5ai/public/js/lib/ext/crumb.js index 1b729732..f928cf1a 100644 --- a/src/_h5ai/public/js/lib/ext/crumb.js +++ b/src/_h5ai/public/js/lib/ext/crumb.js @@ -1,5 +1,5 @@ const {each} = require('../lo'); -const {jq} = require('../globals'); +const {dom} = require('../dom'); const event = require('../core/event'); const location = require('../core/location'); const resource = require('../core/resource'); @@ -10,45 +10,47 @@ const base = require('../view/base'); const settings = Object.assign({ enabled: false }, allsettings.crumb); -const crumbTemplate = +const crumbbarTpl = '
'; +const crumbTpl = ` > - + `; -const pageHintTemplate = +const pageHintTpl = `has index page`; let $crumbbar; const createHtml = item => { - const $html = jq(crumbTemplate); - $html[0]._item = item; - item.elCrumb = $html[0]; + const $html = dom(crumbTpl); location.setLink($html, item); $html.find('.label').text(item.label); if (item.isCurrentFolder()) { - $html.addClass('active'); + $html.addCls('active'); } if (!item.isManaged) { - $html.append(jq(pageHintTemplate)); + $html.app(dom(pageHintTpl)); } + item.$crumb = $html; + $html[0]._item = item; + return $html; }; const onLocationChanged = item => { - const $crumb = jq(item.elCrumb); + const $crumb = item.$crumb; if ($crumb && $crumb.parent()[0] === $crumbbar[0]) { - $crumbbar.children().removeClass('active'); - $crumb.addClass('active'); + $crumbbar.children().rmCls('active'); + $crumb.addCls('active'); } else { - $crumbbar.empty(); + $crumbbar.clr(); each(item.getCrumb(), crumbItem => { - $crumbbar.append(createHtml(crumbItem)); + $crumbbar.app(createHtml(crumbItem)); }); } }; @@ -58,7 +60,7 @@ const init = () => { return; } - $crumbbar = jq('
').appendTo(base.$flowbar); + $crumbbar = dom(crumbbarTpl).appTo(base.$flowbar); event.sub('location.changed', onLocationChanged); }; diff --git a/src/_h5ai/public/js/lib/ext/filter.js b/src/_h5ai/public/js/lib/ext/filter.js index 03dcf87e..8b78d06b 100644 --- a/src/_h5ai/public/js/lib/ext/filter.js +++ b/src/_h5ai/public/js/lib/ext/filter.js @@ -1,5 +1,5 @@ const {each, debounce} = require('../lo'); -const {jq} = require('../globals'); +const {dom} = require('../dom'); const event = require('../core/event'); const location = require('../core/location'); const resource = require('../core/resource'); @@ -25,7 +25,7 @@ let $filter; let $input; -function filter(pattern) { +const filter = pattern => { pattern = pattern || ''; if (pattern === prevPattern) { return; @@ -37,7 +37,7 @@ function filter(pattern) { return; } - $filter.addClass('pending'); + $filter.addCls('pending'); const re = new RegExp(pattern, settings.ignorecase ? 'i' : ''); const matchedItems = []; @@ -48,45 +48,45 @@ function filter(pattern) { } }); - $filter.removeClass('pending'); + $filter.rmCls('pending'); view.setHint('noMatch'); view.setItems(matchedItems); -} +}; -function update() { +const update = () => { if (inputIsVisible) { - $filter.addClass('active'); - $input.focus(); + $filter.addCls('active'); + $input[0].focus(); filter(util.parsePattern($input.val(), settings.advanced)); } else { filter(); - $filter.removeClass('active'); + $filter.rmCls('active'); } -} +}; -function toggle() { +const toggle = () => { inputIsVisible = !inputIsVisible; update(); -} +}; -function reset() { +const reset = () => { inputIsVisible = false; $input.val(''); update(); -} +}; -function init() { +const init = () => { if (!settings.enabled) { return; } - $filter = jq(template).appendTo('#toolbar'); + $filter = dom(template).appTo('#toolbar'); $input = $filter.find('input'); - $filter.on('click', 'img', toggle); - $input.on('keyup', debounce(update, settings.debounceTime, {trailing: true})); + $filter.find('img').on('click', toggle); + $input.on('keyup', debounce(update, settings.debounceTime)); event.sub('location.changed', reset); -} +}; init(); diff --git a/src/_h5ai/public/js/lib/ext/l10n.js b/src/_h5ai/public/js/lib/ext/l10n.js index ca5e6258..32fa5075 100644 --- a/src/_h5ai/public/js/lib/ext/l10n.js +++ b/src/_h5ai/public/js/lib/ext/l10n.js @@ -1,4 +1,4 @@ -const {keys, isStr} = require('../lo'); +const {each, isStr} = require('../lo'); const {win, jq} = require('../globals'); const server = require('../server'); const event = require('../core/event'); @@ -50,7 +50,7 @@ const loaded = { let currentLang = loaded.en; -function update(lang) { +const update = lang => { if (lang) { currentLang = lang; } @@ -60,7 +60,7 @@ function update(lang) { .filter('.' + currentLang.isoCode) .attr('selected', 'selected').prop('selected', 'selected'); - jq.each(currentLang, (key, value) => { + each(currentLang, (value, key) => { jq('.l10n-' + key).text(value); jq('.l10n_ph-' + key).attr('placeholder', value); }); @@ -70,9 +70,9 @@ function update(lang) { const $el = jq(el); $el.text(format.formatDate($el.data('time'))); }); -} +}; -function loadLanguage(isoCode, callback) { +const loadLanguage = (isoCode, callback) => { if (loaded[isoCode]) { callback(loaded[isoCode]); } else { @@ -82,9 +82,9 @@ function loadLanguage(isoCode, callback) { callback(loaded[isoCode]); }); } -} +}; -function localize(languages, isoCode, useBrowserLang) { +const localize = (languages, isoCode, useBrowserLang) => { const storedIsoCode = store.get(storekey); if (languages[storedIsoCode]) { @@ -105,10 +105,9 @@ function localize(languages, isoCode, useBrowserLang) { } loadLanguage(isoCode, update); -} +}; -function initLangSelector(languages) { - const isoCodes = keys(languages).sort(); +const initLangSelector = languages => { const $block = jq(blockTemplate); const $select = $block.find('select') .on('change', ev => { @@ -117,18 +116,18 @@ function initLangSelector(languages) { localize(languages, isoCode, false); }); - jq.each(isoCodes, (idx, isoCode) => { + each(languages, (language, isoCode) => { jq(optionTemplate) .attr('value', isoCode) .addClass(isoCode) - .text(isoCode + ' - ' + (isStr(languages[isoCode]) ? languages[isoCode] : languages[isoCode].lang)) + .text(isoCode + ' - ' + (isStr(language) ? language : language.lang)) .appendTo($select); }); $block.appendTo('#sidebar'); -} +}; -function init() { +const init = () => { if (settings.enabled) { initLangSelector(langs); } @@ -136,7 +135,7 @@ function init() { event.sub('view.changed', () => { localize(langs, settings.lang, settings.useBrowserLang); }); -} +}; init(); diff --git a/src/_h5ai/public/js/lib/ext/piwik-analytics.js b/src/_h5ai/public/js/lib/ext/piwik-analytics.js index 074ce7f2..9469ad7f 100644 --- a/src/_h5ai/public/js/lib/ext/piwik-analytics.js +++ b/src/_h5ai/public/js/lib/ext/piwik-analytics.js @@ -1,5 +1,5 @@ -const {map} = require('../lo'); -const {win, jq} = require('../globals'); +const {win} = require('../globals'); +const {dom, onLoad} = require('../dom'); const event = require('../core/event'); const allsettings = require('../core/settings'); @@ -10,7 +10,7 @@ const settings = Object.assign({ idSite: 0 }, allsettings['piwik-analytics']); -function init() { +const init = () => { if (!settings.enabled) { return; } @@ -20,17 +20,17 @@ function init() { const pkBaseURL = (win.location.protocol === 'https:' ? 'https://' : 'http://') + settings.baseURL + '/'; let piwikTracker = null; - jq('').attr('src', pkBaseURL + 'piwik.js').appTo('body'); + onLoad(() => { piwikTracker = win.Piwik.getTracker(pkBaseURL + 'piwik.php', settings.idSite); piwikTracker.enableLinkTracking(); }); event.sub('location.changed', item => { - const title = map(item.getCrumb(), i => i.label).join(' > '); + const title = item.getCrumb().map(i => i.label).join(' > '); piwikTracker.trackPageView(title); }); -} +}; init(); diff --git a/src/_h5ai/public/js/lib/ext/search.js b/src/_h5ai/public/js/lib/ext/search.js index f3cc6009..df47f05e 100644 --- a/src/_h5ai/public/js/lib/ext/search.js +++ b/src/_h5ai/public/js/lib/ext/search.js @@ -1,5 +1,5 @@ const {map, debounce} = require('../lo'); -const {jq} = require('../globals'); +const {dom} = require('../dom'); const server = require('../server'); const event = require('../core/event'); const location = require('../core/location'); @@ -27,7 +27,7 @@ let $search; let $input; -function search(pattern) { +const search = pattern => { pattern = pattern || ''; if (pattern === prevPattern) { return; @@ -39,7 +39,7 @@ function search(pattern) { return; } - $search.addClass('pending'); + $search.addCls('pending'); server.request({ action: 'get', @@ -49,46 +49,46 @@ function search(pattern) { ignorecase: settings.ignorecase } }).then(response => { - $search.removeClass('pending'); + $search.rmCls('pending'); view.setHint('noMatch'); view.setItems(map(response.search, item => Item.get(item))); }); -} +}; -function update() { +const update = () => { if (inputIsVisible) { - $search.addClass('active'); - $input.focus(); + $search.addCls('active'); + $input[0].focus(); search(util.parsePattern($input.val(), settings.advanced)); } else { search(); - $search.removeClass('active'); + $search.rmCls('active'); } -} +}; -function toggle() { +const toggle = () => { inputIsVisible = !inputIsVisible; update(); -} +}; -function reset() { +const reset = () => { inputIsVisible = false; $input.val(''); update(); -} +}; -function init() { +const init = () => { if (!settings.enabled) { return; } - $search = jq(template).appendTo('#toolbar'); + $search = dom(template).appTo('#toolbar'); $input = $search.find('input'); - $search.on('click', 'img', toggle); - $input.on('keyup', debounce(update, settings.debounceTime, {trailing: true})); + $search.find('img').on('click', toggle); + $input.on('keyup', debounce(update, settings.debounceTime)); event.sub('location.changed', reset); -} +}; init(); diff --git a/src/_h5ai/public/js/lib/ext/select.js b/src/_h5ai/public/js/lib/ext/select.js index fe516320..f8d2123a 100644 --- a/src/_h5ai/public/js/lib/ext/select.js +++ b/src/_h5ai/public/js/lib/ext/select.js @@ -161,7 +161,7 @@ function onViewChanged(added, removed) { each(removed, item => { if (item.$view) { - item.$view.removeClass('selected'); + item.$view.rmCls('selected'); } }); diff --git a/src/_h5ai/public/js/lib/ext/sort.js b/src/_h5ai/public/js/lib/ext/sort.js index 625cf07a..2cc549a4 100644 --- a/src/_h5ai/public/js/lib/ext/sort.js +++ b/src/_h5ai/public/js/lib/ext/sort.js @@ -1,4 +1,5 @@ -const {jq} = require('../globals'); +const {each, toArray} = require('../lo'); +const {dom} = require('../dom'); const event = require('../core/event'); const resource = require('../core/resource'); const allsettings = require('../core/settings'); @@ -14,16 +15,16 @@ const settings = Object.assign({ folders: 0 }, allsettings.sort); const storekey = 'ext/sort'; -const template = 'sort order'; +const template = 'sort order'; -function getType(item) { - const $item = jq(item); +const getType = item => { + const $item = dom(item); - if ($item.hasClass('folder-parent')) { + if ($item.hasCls('folder-parent')) { return 0; } - if ($item.hasClass('folder')) { + if ($item.hasCls('folder')) { if (settings.folders === 1) { return 2; } else if (settings.folders === 2) { @@ -32,25 +33,12 @@ function getType(item) { return 1; } return 2; -} - -function getName(item) { - return jq(item).find('.label').text(); -} - -function getTime(item) { - return jq(item).find('.date').data('time'); -} - -function getSize(item) { - return jq(item).find('.size').data('bytes'); -} - +}; const columnGetters = { - 0: getName, - 1: getTime, - 2: getSize + 0: el => el._item.label, + 1: el => el._item.time, + 2: el => el._item.size }; const columnClasses = { 0: 'label', @@ -59,7 +47,7 @@ const columnClasses = { }; -function cmpFn(getValue, reverse, ignorecase, natural) { +const cmpFn = (getValue, reverse, ignorecase, natural) => { return (item1, item2) => { let res; let val1; @@ -86,66 +74,51 @@ function cmpFn(getValue, reverse, ignorecase, natural) { res = natural ? util.naturalCmpFn(val1, val2) : util.regularCmpFn(val1, val2); return reverse ? -res : res; }; -} +}; -function sortItems(column, reverse) { - const $headers = jq('#items li.header a'); - const $header = jq('#items li.header a.' + columnClasses[column]); +const sortItems = (column, reverse) => { + const $headers = dom('#items li.header a'); + const $header = dom('#items li.header a.' + columnClasses[column]); const fn = cmpFn(columnGetters[column], reverse, settings.ignorecase, column === 0 && settings.natural); - const $current = jq('#items .item'); - const $sorted = jq('#items .item').sort(fn); store.put(storekey, {column, reverse}); - $headers.removeClass('ascending descending'); - $header.addClass(reverse ? 'descending' : 'ascending'); + $headers.rmCls('ascending').rmCls('descending'); + $header.addCls(reverse ? 'descending' : 'ascending'); - for (let i = 0, l = $current.length; i < l; i += 1) { - if ($current[i] !== $sorted[i]) { - $sorted.detach().sort(fn).appendTo('#items'); - break; - } - } -} + dom(toArray(dom('#items .item')).sort(fn)).appTo('#items'); +}; -function onContentChanged() { +const onContentChanged = () => { const order = store.get(storekey); const column = order && order.column || settings.column; const reverse = order && order.reverse || settings.reverse; sortItems(column, reverse); -} +}; -function init() { +const addToggles = () => { + const $header = dom('#items li.header'); + + each(columnClasses, (cls, idx) => { + const pos = idx === '0' ? 'app' : 'pre'; + $header + .find('a.' + cls)[pos](template) + .on('click', ev => { + sortItems(idx, dom(ev.currentTarget).hasCls('ascending')); + ev.preventDefault(); + }); + }); +}; + +const init = () => { if (!settings.enabled) { return; } - const $header = jq('#items li.header'); - - $header.find('a.label') - .append(template) - .click(ev => { - sortItems(0, jq(ev.currentTarget).hasClass('ascending')); - ev.preventDefault(); - }); - - $header.find('a.date') - .prepend(template) - .click(ev => { - sortItems(1, jq(ev.currentTarget).hasClass('ascending')); - ev.preventDefault(); - }); - - $header.find('a.size') - .prepend(template) - .click(ev => { - sortItems(2, jq(ev.currentTarget).hasClass('ascending')); - ev.preventDefault(); - }); - + addToggles(); event.sub('view.changed', onContentChanged); -} +}; init(); diff --git a/src/_h5ai/public/js/lib/ext/thumbnails.js b/src/_h5ai/public/js/lib/ext/thumbnails.js index 345a28ca..595ee2b1 100644 --- a/src/_h5ai/public/js/lib/ext/thumbnails.js +++ b/src/_h5ai/public/js/lib/ext/thumbnails.js @@ -15,7 +15,7 @@ const settings = Object.assign({ const landscapeRatio = 4 / 3; -function queueItem(queue, item) { +const queueItem = (queue, item) => { let type = null; if (includes(settings.img, item.type)) { @@ -29,7 +29,7 @@ function queueItem(queue, item) { } if (item.thumbSquare) { - item.$view.find('.icon.square img').addClass('thumb').attr('src', item.thumbSquare); + item.$view.find('.icon.square img').addCls('thumb').attr('src', item.thumbSquare); } else { queue.push({ type, @@ -38,14 +38,14 @@ function queueItem(queue, item) { callback: src => { if (src && item.$view) { item.thumbSquare = src; - item.$view.find('.icon.square img').addClass('thumb').attr('src', src); + item.$view.find('.icon.square img').addCls('thumb').attr('src', src); } } }); } if (item.thumbRational) { - item.$view.find('.icon.landscape img').addClass('thumb').attr('src', item.thumbRational); + item.$view.find('.icon.landscape img').addCls('thumb').attr('src', item.thumbRational); } else { queue.push({ type, @@ -54,14 +54,14 @@ function queueItem(queue, item) { callback: src => { if (src && item.$view) { item.thumbRational = src; - item.$view.find('.icon.landscape img').addClass('thumb').attr('src', src); + item.$view.find('.icon.landscape img').addCls('thumb').attr('src', src); } } }); } -} +}; -function requestQueue(queue) { +const requestQueue = queue => { const thumbs = map(queue, req => { return { type: req.type, @@ -79,33 +79,29 @@ function requestQueue(queue) { req.callback(json && json.thumbs ? json.thumbs[idx] : null); }); }); -} +}; -function handleItems(items) { +const handleItems = items => { const queue = []; - each(items, item => { - queueItem(queue, item); - }); + each(items, item => queueItem(queue, item)); if (queue.length) { requestQueue(queue); } -} +}; -function onViewChanged(added) { - setTimeout(() => { - handleItems(added); - }, settings.delay); -} +const onViewChanged = added => { + setTimeout(() => handleItems(added), settings.delay); +}; -function init() { +const init = () => { if (!settings.enabled) { return; } event.sub('view.changed', onViewChanged); -} +}; init(); diff --git a/src/_h5ai/public/js/lib/ext/title.js b/src/_h5ai/public/js/lib/ext/title.js index 6eddeaa7..07d2aa32 100644 --- a/src/_h5ai/public/js/lib/ext/title.js +++ b/src/_h5ai/public/js/lib/ext/title.js @@ -1,4 +1,3 @@ -const {map} = require('../lo'); const {win} = require('../globals'); const event = require('../core/event'); const allsettings = require('../core/settings'); @@ -10,7 +9,7 @@ const settings = Object.assign({ }, allsettings.title); const onLocationChanged = item => { - const labels = map(item.getCrumb(), i => i.label); + const labels = item.getCrumb().map(i => i.label); let title = labels.join(' > '); if (labels.length > 1) { diff --git a/src/_h5ai/public/js/lib/ext/tree.js b/src/_h5ai/public/js/lib/ext/tree.js index f85eccc5..47f8d97d 100644 --- a/src/_h5ai/public/js/lib/ext/tree.js +++ b/src/_h5ai/public/js/lib/ext/tree.js @@ -55,7 +55,6 @@ function update(item) { const $label = $html.find('.label'); $html.addClass(item.isFolder() ? 'folder' : 'file'); - $html[0]._item = item; location.setLink($a, item); $img.attr('src', resource.icon('folder')); @@ -108,10 +107,11 @@ function update(item) { } } - if (item.elTree) { - jq(item.elTree).replaceWith($html); + if (item.$tree) { + item.$tree.replaceWith($html); } - item.elTree = $html[0]; + item.$tree = $html; + $html[0]._item = item; return $html; } diff --git a/src/_h5ai/public/js/lib/init.js b/src/_h5ai/public/js/lib/init.js index 87d96204..2bbf6084 100644 --- a/src/_h5ai/public/js/lib/init.js +++ b/src/_h5ai/public/js/lib/init.js @@ -1,7 +1,7 @@ -const {jq} = require('./globals'); +const {dom, onReady} = require('./dom'); const config = require('./config'); -const name = jq('script[data-module]').data('module'); +const name = dom('script[data-module]').attr('data-module'); const query = { action: 'get', setup: true, @@ -18,4 +18,4 @@ if (name === 'index') { throw new Error(`no-main-module: '${name}'`); } -config._update(query).then(() => jq(() => require(`./main/${name}`))); +config._update(query).then(() => onReady(() => require(`./main/${name}`))); diff --git a/src/_h5ai/public/js/lib/lo.js b/src/_h5ai/public/js/lib/lo.js index 090ca3de..a3100f48 100644 --- a/src/_h5ai/public/js/lib/lo.js +++ b/src/_h5ai/public/js/lib/lo.js @@ -1,12 +1,14 @@ +const is = x => x !== undefined && x !== null; const tof = (x, str) => typeof x === str; const isStr = x => tof(x, 'string'); const isFn = x => tof(x, 'function'); const isNum = x => tof(x, 'number'); +const hasLength = x => x && x.hasOwnProperty('length'); const keys = obj => { if (!obj || isStr(obj)) { return []; } - if (obj.hasOwnProperty('length')) { + if (hasLength(obj)) { obj = Array.from(obj); } return Object.keys(obj); @@ -17,6 +19,10 @@ const filter = (obj, fn) => values(obj).filter(fn); const map = (obj, fn) => values(obj).map(fn); const includes = (obj, x) => values(obj).indexOf(x) >= 0; const compact = obj => filter(obj, x => !!x); + +const isInstanceOf = (x, constructor) => x ? x instanceof constructor : false; +const toArray = x => Array.from(x); + const difference = (obj1, obj2) => { obj2 = values(obj2); return filter(obj1, x => obj2.indexOf(x) < 0); @@ -40,9 +46,11 @@ const debounce = (fn, delay) => { }; module.exports = { + is, isStr, isFn, isNum, + hasLength, keys, values, each, @@ -50,6 +58,8 @@ module.exports = { map, includes, compact, + isInstanceOf, + toArray, difference, intersection, sortBy, diff --git a/src/_h5ai/public/js/lib/main/info.js b/src/_h5ai/public/js/lib/main/info.js index 22e2ce3c..79fd1fe5 100644 --- a/src/_h5ai/public/js/lib/main/info.js +++ b/src/_h5ai/public/js/lib/main/info.js @@ -1,11 +1,12 @@ -const {win, jq} = require('../globals'); +const {win} = require('../globals'); +const {dom} = require('../dom'); const config = require('../config'); const server = require('../server'); const resource = require('../core/resource'); const tplTests = - '
`; @@ -37,10 +38,10 @@ const setup = config.setup; const addTest = (label, info, passed, result) => { - const $test = jq(tplTest).appendTo('#tests'); + const $test = dom(tplTest).appTo('#tests'); $test.find('.label').text(label); $test.find('.result') - .addClass(passed ? 'passed' : 'failed') + .addCls(passed ? 'passed' : 'failed') .text(result ? result : passed ? 'yes' : 'no'); $test.find('.info').html(info); }; @@ -50,7 +51,7 @@ const addTests = () => { return; } - jq(tplTests).appendTo('#content'); + dom(tplTests).appTo('#content'); addTest( 'h5ai version', 'Only green if this is an official h5ai release', @@ -140,7 +141,7 @@ const reload = () => { const onLogin = () => { server.request({ action: 'login', - pass: jq('#pass').val() + pass: dom('#pass').val() }).then(reload); }; @@ -157,23 +158,23 @@ const onKeydown = ev => { }; const addSupport = () => { - jq(tplSupport).appendTo('#content'); + dom(tplSupport).appTo('#content'); }; const addLogin = () => { - jq(tplLogin).appendTo('#content'); + dom(tplLogin).appTo('#content'); if (setup.AS_ADMIN) { - jq('#pass').remove(); - jq('#login').remove(); - jq('#logout').on('click', onLogout); + dom('#pass').rm(); + dom('#login').rm(); + dom('#logout').on('click', onLogout); } else { - jq('#pass').on('keydown', onKeydown).focus(); - jq('#login').on('click', onLogin); - jq('#logout').remove(); + dom('#pass').on('keydown', onKeydown)[0].focus(); + dom('#login').on('click', onLogin); + dom('#logout').rm(); } if (config.options.hasCustomPasshash) { - jq('#hint').remove(); + dom('#hint').rm(); } }; diff --git a/src/_h5ai/public/js/lib/server.js b/src/_h5ai/public/js/lib/server.js index 13148c65..824ee444 100644 --- a/src/_h5ai/public/js/lib/server.js +++ b/src/_h5ai/public/js/lib/server.js @@ -1,30 +1,41 @@ -const {jq} = require('./globals'); +const {win} = require('./globals'); +const {dom} = require('./dom'); const {each} = require('./lo'); +const XHR = win.XMLHttpRequest; const request = data => { return new Promise(resolve => { - jq.ajax({ - url: '?', - data, - type: 'post', - dataType: 'json' - }) - .done(json => resolve(json)) - .fail(() => resolve()); + const xhr = new XHR(); + const callback = () => { + if (xhr.readyState === XHR.DONE) { + try { + resolve(JSON.parse(xhr.responseText)); + } catch (err) { + resolve({err, txt: xhr.responseText}); + } + } + }; + + xhr.open('POST', '?', true); + xhr.onreadystatechange = callback; + xhr.setRequestHeader('Content-Type', 'application/json;charset=utf-8'); + xhr.send(JSON.stringify(data)); }); }; const formRequest = data => { - const $form = jq('
'); + const $form = dom(''); each(data, (val, key) => { - jq('') + dom('') .attr('name', key) .attr('value', val) - .appendTo($form); + .appTo($form); }); - $form.appendTo('body').submit().remove(); + $form.appTo('body'); + $form[0].submit(); + $form.rm(); }; module.exports = { diff --git a/src/_h5ai/public/js/lib/view/base.js b/src/_h5ai/public/js/lib/view/base.js index 171b31b8..cd6909e5 100644 --- a/src/_h5ai/public/js/lib/view/base.js +++ b/src/_h5ai/public/js/lib/view/base.js @@ -1,10 +1,10 @@ -const {jq} = require('../globals'); +const {dom} = require('../dom'); const rootSelector = 'body'; const tplTopbar = `
-
- `; const tplMainrow = `
-
+
`; const init = () => { - jq('#fallback, #fallback-hints').remove(); + dom('#fallback, #fallback-hints').rm(); - const $root = jq(rootSelector) + const $root = dom(rootSelector) .attr('id', 'root') - .append(tplTopbar) - .append(tplMainrow); + .app(tplTopbar) + .app(tplMainrow); return { $root, diff --git a/src/_h5ai/public/js/lib/view/notification.js b/src/_h5ai/public/js/lib/view/notification.js index 8159aefb..0247e673 100644 --- a/src/_h5ai/public/js/lib/view/notification.js +++ b/src/_h5ai/public/js/lib/view/notification.js @@ -1,17 +1,20 @@ -const {jq} = require('../globals'); +const {dom} = require('../dom'); const base = require('./base'); -const $el = jq('
').hide().appendTo(base.$root); +const init = () => { + const $el = dom('
').hide().appTo(base.$root); -const set = content => { - if (content) { - $el.stop(true, true).html(content).fadeIn(400); - } else { - $el.stop(true, true).fadeOut(400); - } + const set = content => { + if (content) { + $el.html(content).show(); + } else { + $el.hide(); + } + }; + + return { + set + }; }; -module.exports = { - $el, - set -}; +module.exports = init(); diff --git a/src/_h5ai/public/js/lib/view/sidebar.js b/src/_h5ai/public/js/lib/view/sidebar.js index d56260fd..f008fd35 100644 --- a/src/_h5ai/public/js/lib/view/sidebar.js +++ b/src/_h5ai/public/js/lib/view/sidebar.js @@ -1,4 +1,4 @@ -const {jq} = require('../globals'); +const {dom} = require('../dom'); const resource = require('../core/resource'); const allsettings = require('../core/settings'); const store = require('../core/store'); @@ -9,7 +9,7 @@ const settings = Object.assign({ disableSidebar: false }, allsettings.view); const storekey = 'sidebarIsVisible'; -const tplSidebar = '