Start to replace jQuery with dom.

This commit is contained in:
Lars Jung 2016-06-26 01:03:24 +02:00
parent 9234e3f287
commit 7320592dc2
26 changed files with 566 additions and 301 deletions

View file

@ -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

View file

@ -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);

View file

@ -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) {

View file

@ -1,5 +1,4 @@
#sidebar {
display: none;
overflow-x: hidden;
overflow-y: auto;
flex: 0 0 auto;

View file

@ -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;
});
}

View file

@ -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 (/^<t(head|body|foot)|^<c(ap|olg)/i.test(str)) {
return CONTAINER_TABLE;
}
if (/^<col/i.test(str)) {
return CONTAINER_COLGROUP;
}
if (/^<tr/i.test(str)) {
return CONTAINER_TBODY;
}
if (/^<t[dh]/i.test(str)) {
return CONTAINER_TR;
}
return CONTAINER_DIV;
};
const parseHtml = str => {
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
};

View file

@ -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 = '<div id="crumbbar"></div>';
const crumbTpl =
`<a class="crumb">
<img class="sep" src="${resource.image('crumb')}" alt=">"/>
<span class="label"/>
<span class="label"></span>
</a>`;
const pageHintTemplate =
const pageHintTpl =
`<img class="hint" src="${resource.icon('folder-page')}" alt="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('<div id="crumbbar"/>').appendTo(base.$flowbar);
$crumbbar = dom(crumbbarTpl).appTo(base.$flowbar);
event.sub('location.changed', onLocationChanged);
};

View file

@ -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();

View file

@ -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();

View file

@ -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('<script/>').attr('src', pkBaseURL + 'piwik.js').appendTo('body');
jq(win).load(() => {
dom('<script></script>').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();

View file

@ -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();

View file

@ -161,7 +161,7 @@ function onViewChanged(added, removed) {
each(removed, item => {
if (item.$view) {
item.$view.removeClass('selected');
item.$view.rmCls('selected');
}
});

View file

@ -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 = '<img src="' + resource.image('sort') + '" class="sort" alt="sort order" />';
const template = '<img src="' + resource.image('sort') + '" class="sort" alt="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();

View file

@ -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();

View file

@ -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) {

View file

@ -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;
}

View file

@ -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}`)));

View file

@ -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,

View file

@ -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 =
'<ul id="tests">';
'<ul id="tests"></ul>';
const tplTest =
`<li class="test">
<span class="label"></span>
@ -27,9 +28,9 @@ const tplSupport =
Show your support with a donation!
<div class="paypal">
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank">
<input type="hidden" name="cmd" value="_s-xclick" />
<input type="hidden" name="hosted_button_id" value="8WSPKWT7YBTSQ" />
<input type="image" src="${resource.image('paypal')}" name="submit" alt="PayPal" />
<input type="hidden" name="cmd" value="_s-xclick"/>
<input type="hidden" name="hosted_button_id" value="8WSPKWT7YBTSQ"/>
<input type="image" src="${resource.image('paypal')}" name="submit" alt="PayPal"/>
</form>
</div>
</div>`;
@ -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();
}
};

View file

@ -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('<form method="post" action="?" style="display:none;"/>');
const $form = dom('<form method="post" action="?" style="display:none;"/>');
each(data, (val, key) => {
jq('<input type="hidden"/>')
dom('<input type="hidden"/>')
.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 = {

View file

@ -1,10 +1,10 @@
const {jq} = require('../globals');
const {dom} = require('../dom');
const rootSelector = 'body';
const tplTopbar =
`<div id="topbar">
<div id="toolbar"/>
<div id="flowbar"/>
<div id="toolbar"></div>
<div id="flowbar"></div>
<a id="backlink" href="https://larsjung.de/h5ai/" title="powered by h5ai - https://larsjung.de/h5ai/">
<div>powered</div>
<div>by h5ai</div>
@ -12,16 +12,16 @@ const tplTopbar =
</div>`;
const tplMainrow =
`<div id="mainrow">
<div id="content"/>
<div id="content"></div>
</div>`;
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,

View file

@ -1,17 +1,20 @@
const {jq} = require('../globals');
const {dom} = require('../dom');
const base = require('./base');
const $el = jq('<div id="notification"/>').hide().appendTo(base.$root);
const init = () => {
const $el = dom('<div id="notification"></div>').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();

View file

@ -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 = '<div id="sidebar"/>';
const tplSidebar = '<div id="sidebar"></div>';
const tplToggle =
`<div id="sidebar-toggle" class="tool">
<img alt="sidebar"/>
@ -17,8 +17,8 @@ const tplToggle =
const init = () => {
const $sidebar = jq(tplSidebar);
const $toggle = jq(tplToggle);
const $sidebar = dom(tplSidebar).hide();
const $toggle = dom(tplToggle);
const $img = $toggle.find('img');
const update = toggle => {
@ -30,19 +30,19 @@ const init = () => {
}
if (isVisible) {
$toggle.addClass('active');
$toggle.addCls('active');
$img.attr('src', resource.image('back'));
$sidebar.show();
} else {
$toggle.removeClass('active');
$toggle.rmCls('active');
$img.attr('src', resource.image('sidebar'));
$sidebar.hide();
}
};
if (!settings.disableSidebar) {
$sidebar.appendTo(base.$mainrow);
$toggle.appendTo(base.$toolbar).on('click', () => update(true));
$sidebar.appTo(base.$mainrow);
$toggle.appTo(base.$toolbar).on('click', () => update(true));
update();
}

View file

@ -1,5 +1,5 @@
const {each, map, includes, intersection} = require('../lo');
const {jq} = require('../globals');
const {dom} = require('../dom');
const event = require('../core/event');
const format = require('../core/format');
const location = require('../core/location');
@ -26,25 +26,25 @@ const tplView =
`<div id="view">
<ul id="items" class="clearfix">
<li class="header">
<a class="icon"/>
<a class="icon"></a>
<a class="label" href="#"><span class="l10n-name"/></a>
<a class="date" href="#"><span class="l10n-lastModified"/></a>
<a class="size" href="#"><span class="l10n-size"/></a>
</li>
</ul>
<div id="view-hint"/>
<div id="view-hint"></div>
</div>`;
const tplItem =
`<li class="item">
<a>
<span class="icon square"><img/></span>
<span class="icon landscape"><img/></span>
<span class="label"/>
<span class="date"/>
<span class="size"/>
<span class="label"></span>
<span class="date"></span>
<span class="size"></span>
</a>
</li>`;
const $view = jq(tplView);
const $view = dom(tplView);
const $items = $view.find('#items');
const $hint = $view.find('#view-hint');
@ -80,7 +80,7 @@ const createStyles = size => {
const addCssStyles = () => {
const styles = map(sortedSizes, size => createStyles(size));
styles.push(`#view .icon img {max-width: ${settings.maxIconSize}px; max-height: ${settings.maxIconSize}px;}`);
jq('<style/>').text(styles.join('\n')).appendTo('head');
dom('<style></style>').text(styles.join('\n')).appTo('head');
};
const set = (mode, size) => {
@ -94,17 +94,17 @@ const set = (mode, size) => {
each(checkedModes, m => {
if (m === mode) {
$view.addClass('view-' + m);
$view.addCls('view-' + m);
} else {
$view.removeClass('view-' + m);
$view.rmCls('view-' + m);
}
});
each(sortedSizes, s => {
if (s === size) {
$view.addClass('view-size-' + s);
$view.addCls('view-size-' + s);
} else {
$view.removeClass('view-size-' + s);
$view.rmCls('view-size-' + s);
}
});
@ -119,56 +119,58 @@ const getSizes = () => sortedSizes;
const getSize = () => store.get(storekey).size;
const setSize = size => set(null, size);
const onMouseenter = ev => {
const item = ev.target._item;
event.pub('item.mouseenter', item);
};
const onMouseleave = ev => {
const item = ev.target._item;
event.pub('item.mouseleave', item);
};
const createHtml = item => {
const $html = jq(tplItem);
const $html = dom(tplItem);
const $a = $html.find('a');
const $iconImg = $html.find('.icon img');
const $label = $html.find('.label');
const $date = $html.find('.date');
const $size = $html.find('.size');
$html.addClass(item.isFolder() ? 'folder' : 'file');
$html[0]._item = item;
$html
.addCls(item.isFolder() ? 'folder' : 'file')
.on('mouseenter', onMouseenter)
.on('mouseleave', onMouseleave);
location.setLink($a, item);
$label.text(item.label).attr('title', item.label);
$date.data('time', item.time).text(format.formatDate(item.time));
$size.data('bytes', item.size).text(format.formatSize(item.size));
$date.attr('data-time', item.time).text(format.formatDate(item.time));
$size.attr('data-bytes', item.size).text(format.formatSize(item.size));
item.icon = resource.icon(item.type);
if (item.isFolder() && !item.isManaged) {
$html.addClass('page');
$html.addCls('page');
item.icon = resource.icon('folder-page');
}
if (item.isCurrentParentFolder()) {
item.icon = resource.icon('folder-parent');
if (!settings.setParentFolderLabels) {
$label.addClass('l10n-parentDirectory');
$label.addCls('l10n-parentDirectory');
}
$html.addClass('folder-parent');
$html.addCls('folder-parent');
}
$iconImg.attr('src', item.icon).attr('alt', item.type);
item.$view = $html;
item.elView = $html[0];
$html[0]._item = item;
return $html;
};
const onMouseenter = ev => {
const item = jq(ev.currentTarget).closest('.item')[0]._item;
event.pub('item.mouseenter', item);
};
const onMouseleave = ev => {
const item = jq(ev.currentTarget).closest('.item')[0]._item;
event.pub('item.mouseleave', item);
};
const checkHint = () => {
const hasNoItems = $items.find('.item').not('.folder-parent').length === 0;
const hasNoItems = $items.find('.item').length === $items.find('.folder-parent').length;
if (hasNoItems) {
$hint.show();
@ -180,24 +182,23 @@ const checkHint = () => {
const setItems = items => {
const removed = map($items.find('.item'), el => el._item);
$items.find('.item').remove();
$items.find('.item').rm();
each(items, item => $items.append(createHtml(item)));
each(items, item => $items.app(createHtml(item)));
base.$content.scrollLeft(0).scrollTop(0);
base.$content[0].scrollLeft = 0;
base.$content[0].scrollTop = 0;
checkHint();
event.pub('view.changed', items, removed);
};
const changeItems = (add, remove) => {
each(add, item => {
createHtml(item).hide().appendTo($items).fadeIn(400);
createHtml(item).hide().appTo($items).show();
});
each(remove, item => {
item.$view.fadeOut(400, () => {
item.$view.remove();
});
item.$view.hide().rm();
});
checkHint();
@ -205,7 +206,7 @@ const changeItems = (add, remove) => {
};
const setHint = l10nKey => {
$hint.removeClass().addClass('l10n-' + l10nKey);
$hint.rmCls().addCls('l10n-' + l10nKey);
checkHint();
};
@ -247,15 +248,11 @@ const init = () => {
addCssStyles();
set();
$view.appendTo(base.$content);
$view.appTo(base.$content);
$hint.hide();
format.setDefaultMetric(settings.binaryPrefix);
$items
.on('mouseenter', '.item a', onMouseenter)
.on('mouseleave', '.item a', onMouseleave);
event.sub('location.changed', onLocationChanged);
event.sub('location.refreshed', onLocationRefreshed);
};
@ -265,7 +262,6 @@ init();
module.exports = {
$el: $view,
$items,
setItems,
changeItems,
setLocation: onLocationChanged,

View file

@ -1,5 +1,5 @@
const {each} = require('../lo');
const {jq} = require('../globals');
const {dom} = require('../dom');
const event = require('../core/event');
const resource = require('../core/resource');
const allsettings = require('../core/settings');
@ -28,14 +28,14 @@ let sizes;
const onChanged = (mode, size) => {
jq('#viewmode-settings .mode').removeClass('active');
jq('#viewmode-' + mode).addClass('active');
jq('#viewmode-size').val(sizes.indexOf(size));
dom('#viewmode-settings .mode').rmCls('active');
dom('#viewmode-' + mode).addCls('active');
dom('#viewmode-size').val(sizes.indexOf(size));
if (settings.modeToggle === 'next') {
mode = modes[(modes.indexOf(mode) + 1) % modes.length];
}
jq('#viewmode-toggle img').attr('src', resource.image('view-' + mode));
dom('#viewmode-toggle img').attr('src', resource.image('view-' + mode));
};
const addSettings = () => {
@ -43,29 +43,28 @@ const addSettings = () => {
return;
}
const $viewBlock = jq(tplSettings);
const $viewBlock = dom(tplSettings);
if (modes.length > 1) {
each(modes, mode => {
jq(tplMode.replace(/\[MODE\]/g, mode))
dom(tplMode.replace(/\[MODE\]/g, mode))
.on('click', () => {
view.setMode(mode);
})
.appendTo($viewBlock);
.appTo($viewBlock);
});
}
if (sizes.length > 1) {
const max = sizes.length - 1;
jq(tplSize)
.prop('max', max).attr('max', max)
.on('input change', ev => {
view.setSize(sizes[ev.target.valueAsNumber]);
})
.appendTo($viewBlock);
dom(tplSize)
.attr('max', max)
.on('input', ev => view.setSize(sizes[ev.target.valueAsNumber]))
.on('change', ev => view.setSize(sizes[ev.target.valueAsNumber]))
.appTo($viewBlock);
}
$viewBlock.appendTo(sidebar.$el);
$viewBlock.appTo(sidebar.$el);
};
const onToggle = () => {
@ -78,9 +77,9 @@ const onToggle = () => {
const addToggle = () => {
if (settings.modeToggle && modes.length > 1) {
jq(tplToggle)
dom(tplToggle)
.on('click', onToggle)
.appendTo(base.$toolbar);
.appTo(base.$toolbar);
}
};

View file

@ -22,6 +22,7 @@
assert('console', win.console && isFn(win.console.log));
assert('assign', win.Object && isFn(win.Object.assign));
assert('promise', isFn(win.Promise));
assert('xhr', isFn(win.XMLHttpRequest));
}(this));
/* eslint-enable */