Clean preview code.

This commit is contained in:
Lars Jung 2016-07-22 03:59:50 +02:00
parent 9e0b1698fc
commit 8a4ccd098c
10 changed files with 266 additions and 481 deletions

View file

@ -1,4 +1,4 @@
#pv-aud-audio {
#pv-content-aud {
.raised;
position: absolute;

View file

@ -1,4 +1,4 @@
#pv-img-image {
#pv-content-img {
.raised;
@check-white: #f8f8f8;

View file

@ -1,4 +1,4 @@
#pv-txt-text {
#pv-content-txt {
.raised;
max-width: 960px;
@ -8,101 +8,27 @@
padding: 8px;
overflow: auto;
a, a:active, a:visited {
color: #2080FF;
text-decoration: none;
cursor: pointer;
&:hover {
color: #68A9FF;
}
}
&.highlighted {
code {
line-height: 1.2em;
}
a, a:active, a:visited {
color: #2080FF;
text-decoration: none;
cursor: pointer;
&:hover {
color: #68A9FF;
}
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #aaa;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol {
color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.builtin {
color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string,
.token.variable {
color: #a67f59;
background: hsla(0,0%,100%,.5);
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #07a;
}
.token.function {
color: #DD4A68;
}
.token.regex,
.token.important {
color: #e90;
}
.token.important {
font-weight: bold;
}
.token.entity {
cursor: help;
}
}
&.markdown {
font-size: 1.1em;
padding: 8px 24px;
a, a:active, a:visited {
color: #2080FF;
text-decoration: none;
cursor: pointer;
&:hover {
color: #68A9FF;
}
}
code {
color: #008200;
}

View file

@ -1,4 +1,4 @@
#pv-vid-video {
#pv-content-vid {
.raised;
position: absolute;
@ -7,7 +7,7 @@
max-height: 100%;
}
#pv-vid-video:-webkit-full-screen {
#pv-content-vid:-webkit-full-screen {
top: auto !important;
left: auto !important;
}

View file

@ -1,104 +1,57 @@
const {each, includes, compact, dom} = require('../../util');
const {win} = require('../../globals');
const {each, dom} = require('../../util');
const event = require('../../core/event');
const format = require('../../core/format');
const allsettings = require('../../core/settings');
const preview = require('./preview');
const previewX = require('./preview-x');
const settings = Object.assign({
enabled: false,
types: []
}, allsettings['preview-aud']);
const tpl = '<audio id="pv-content-aud"/>';
const preloadAudio = (src, callback) => {
const $audio = dom('<audio/>')
.on('loadedmetadata', () => callback($audio))
.attr('autoplay', 'autoplay')
.attr('controls', 'controls')
.attr('src', src);
};
let state;
const onAdjustSize = () => {
const $audio = dom('#pv-aud-audio');
if (!$audio.length) {
const el = dom('#pv-content-aud')[0];
if (!el) {
return;
}
const elContent = dom('#pv-content')[0];
const contentW = elContent.offsetWidth;
const contentH = elContent.offsetHeight;
const audioW = $audio[0].offsetWidth;
const audioH = $audio[0].offsetHeight;
const elW = el.offsetWidth;
const elH = el.offsetHeight;
$audio.css({
left: (contentW - audioW) * 0.5 + 'px',
top: (contentH - audioH) * 0.5 + 'px'
dom(el).css({
left: (contentW - elW) * 0.5 + 'px',
top: (contentH - elH) * 0.5 + 'px'
});
preview.setLabels([
state.item.label,
format.formatDate(dom('#pv-content-aud')[0].duration * 1000, 'm:ss')
]);
};
const loadAudio = item => {
return new Promise(resolve => {
const $el = dom(tpl)
.on('loadedmetadata', () => resolve($el))
// .attr('autoplay', 'autoplay')
.attr('controls', 'controls')
.attr('src', item.absHref);
});
// .then(x => new Promise(resolve => setTimeout(() => resolve(x), 1000)));
};
const onEnter = (items, idx) => {
const currentItems = items;
let currentIdx = idx;
let currentItem = items[idx];
let spinnerTimeout;
const updateMeta = () => {
preview.setLabels([
currentItem.label,
format.formatDate(dom('#pv-aud-audio')[0].duration * 1000, 'm:ss')
]);
preview.setIndex(currentIdx + 1, currentItems.length);
preview.setRawLink(currentItem.absHref);
};
const onReady = $preloadedContent => {
win.clearTimeout(spinnerTimeout);
preview.showSpinner(false);
dom('#pv-content')
.hide()
.clr()
.app($preloadedContent.attr('id', 'pv-aud-audio'))
.show();
updateMeta();
onAdjustSize();
};
const onIdxChange = rel => {
currentIdx = (currentIdx + rel + currentItems.length) % currentItems.length;
currentItem = currentItems[currentIdx];
spinnerTimeout = win.setTimeout(() => preview.showSpinner(true), 200);
if (dom('#pv-aud-audio').length) {
dom('#pv-aud-audio')[0].pause();
}
preloadAudio(currentItem.absHref, onReady);
};
onIdxChange(0);
preview.setOnIndexChange(onIdxChange);
preview.setOnAdjustSize(onAdjustSize);
preview.enter();
};
const initItem = item => {
if (item.$view && includes(settings.types, item.type)) {
item.$view.find('a').on('click', ev => {
ev.preventDefault();
const matchedItems = compact(dom('#items .item').map(el => {
const matchedItem = el._item;
return includes(settings.types, matchedItem.type) ? matchedItem : null;
}));
onEnter(matchedItems, matchedItems.indexOf(item));
});
}
state = previewX.pvState(items, idx, loadAudio, onAdjustSize);
};
const initItem = previewX.initItemFn(settings.types, onEnter);
const onViewChanged = added => each(added, initItem);
const init = () => {

View file

@ -1,27 +1,47 @@
const {each, includes, compact, dom} = require('../../util');
const {win} = require('../../globals');
const {each, dom} = require('../../util');
const server = require('../../server');
const event = require('../../core/event');
const allsettings = require('../../core/settings');
const preview = require('./preview');
const previewX = require('./preview-x');
const settings = Object.assign({
enabled: false,
size: null,
types: []
}, allsettings['preview-img']);
const spinnerThreshold = 200;
let spinnerTimeoutId;
let currentItems;
let currentIdx;
let currentItem;
const tpl = '<img id="pv-content-img"/>';
let state;
const requestSample = href => {
if (!settings.size) {
return Promise.resolve(href);
const onAdjustSize = () => {
const el = dom('#pv-content-img')[0];
if (!el) {
return;
}
const elContent = dom('#pv-content')[0];
const contentW = elContent.offsetWidth;
const contentH = elContent.offsetHeight;
const elW = el.offsetWidth;
const elH = el.offsetHeight;
dom(el).css({
left: (contentW - elW) * 0.5 + 'px',
top: (contentH - elH) * 0.5 + 'px'
});
const labels = [state.item.label];
if (!settings.size) {
const elNW = el.naturalWidth;
const elNH = el.naturalHeight;
labels.push(String(elNW) + 'x' + String(elNH));
labels.push(String((100 * elW / elNW).toFixed(0)) + '%');
}
preview.setLabels(labels);
};
const requestSample = href => {
return server.request({
action: 'get',
thumbs: [{
@ -35,107 +55,25 @@ const requestSample = href => {
});
};
const preloadImage = (item, callback) => {
return requestSample(item.absHref).then(src => {
const $img = dom('<img/>')
.on('load', () => {
callback(item, $img);
// for testing
// win.setTimeout(() => callback(item, $img), 1000);
})
.attr('src', src);
});
};
const onAdjustSize = () => {
const $content = dom('#pv-content');
const $img = dom('#pv-img-image');
const contentW = $content[0].offsetWidth;
const contentH = $content[0].offsetHeight;
const imgW = ($img[0] || {}).offsetWidth;
const imgH = ($img[0] || {}).offsetHeight;
if ($img.length === 0) {
return;
}
$img.css({
left: (contentW - imgW) * 0.5 + 'px',
top: (contentH - imgH) * 0.5 + 'px'
});
const labels = [currentItem.label];
if (!settings.size) {
const imgNW = $img[0].naturalWidth;
const imgNH = $img[0].naturalHeight;
labels.push(String(imgNW) + 'x' + String(imgNH));
labels.push(String((100 * imgW / imgNW).toFixed(0)) + '%');
}
preview.setLabels(labels);
};
const onIdxChange = rel => {
currentIdx = (currentIdx + rel + currentItems.length) % currentItems.length;
currentItem = currentItems[currentIdx];
preview.setLabels([currentItem.label]);
preview.setIndex(currentIdx + 1, currentItems.length);
preview.setRawLink(currentItem.absHref);
dom('#pv-content').hide();
if (preview.isSpinnerVisible()) {
preview.showSpinner(true, currentItem.thumbSquare);
} else {
win.clearTimeout(spinnerTimeoutId);
spinnerTimeoutId = win.setTimeout(() => {
preview.showSpinner(true, currentItem.thumbSquare);
}, spinnerThreshold);
}
preloadImage(currentItem, (item, preloadedImage) => {
if (item !== currentItem) {
return;
}
win.clearTimeout(spinnerTimeoutId);
preview.showSpinner(false);
dom('#pv-content')
.clr()
.app(dom(preloadedImage).attr('id', 'pv-img-image'))
.show();
onAdjustSize();
});
const loadImage = item => {
return Promise.resolve(item.absHref)
.then(href => {
return settings.size ? requestSample(href) : href;
})
.then(href => new Promise(resolve => {
const $img = dom(tpl)
.on('load', () => resolve($img))
.attr('src', href);
}));
// .then(x => new Promise(resolve => setTimeout(() => resolve(x), 1000)));
};
const onEnter = (items, idx) => {
currentItems = items;
currentIdx = idx;
preview.setOnIndexChange(onIdxChange);
preview.setOnAdjustSize(onAdjustSize);
preview.enter();
onIdxChange(0);
state = previewX.pvState(items, idx, loadImage, onAdjustSize);
};
const initItem = item => {
if (item.$view && includes(settings.types, item.type)) {
item.$view.find('a').on('click', ev => {
ev.preventDefault();
const matchedItems = compact(dom('#items .item').map(el => {
const matchedItem = el._item;
return includes(settings.types, matchedItem.type) ? matchedItem : null;
}));
onEnter(matchedItems, matchedItems.indexOf(item));
});
}
};
const onViewChanged = added => {
each(added, initItem);
};
const initItem = previewX.initItemFn(settings.types, onEnter);
const onViewChanged = added => each(added, initItem);
const init = () => {
if (!settings.enabled) {
@ -145,5 +83,4 @@ const init = () => {
event.sub('view.changed', onViewChanged);
};
init();

View file

@ -1,26 +1,39 @@
const lolight = require('lolight');
const marked = require('marked');
const {each, keys, includes, compact, dom} = require('../../util');
const {each, keys, dom} = require('../../util');
const {win} = require('../../globals');
const event = require('../../core/event');
const allsettings = require('../../core/settings');
const preview = require('./preview');
const previewX = require('./preview-x');
const XHR = win.XMLHttpRequest;
const settings = Object.assign({
enabled: false,
styles: {}
}, allsettings['preview-txt']);
const tplText = '<pre id="pv-txt-text" class="highlighted"/>';
const tplMarkdown = '<div id="pv-txt-text" class="markdown"/>';
const spinnerThreshold = 200;
const tplText = '<pre id="pv-content-txt" class="highlighted"></pre>';
const tplMarkdown = '<div id="pv-content-txt" class="markdown"></div>';
let spinnerTimeoutId;
let currentItems;
let currentIdx;
let currentItem;
let state;
const onAdjustSize = () => {
const el = dom('#pv-content-txt')[0];
if (!el) {
return;
}
const elContent = dom('#pv-content')[0];
dom(el).css({
height: elContent.offsetHeight - 16 + 'px'
});
preview.setLabels([
state.item.label,
state.item.size + ' bytes'
]);
};
const requestTextContent = href => {
return new Promise((resolve, reject) => {
@ -28,7 +41,7 @@ const requestTextContent = href => {
const callback = () => {
if (xhr.readyState === XHR.DONE) {
try {
resolve(xhr.responseText);
resolve(xhr.responseText || '');
} catch (err) {
reject(String(err));
}
@ -41,104 +54,37 @@ const requestTextContent = href => {
});
};
const preloadText = (item, callback) => {
requestTextContent(item.absHref)
const loadText = item => {
return requestTextContent(item.absHref)
.catch(err => '[ajax error] ' + err)
.then(content => {
callback(item, content);
const style = settings.styles[state.item.type];
let $text;
// for testing
// win.setTimeout(() => callback(item, content), 1000);
})
.catch(err => callback(item, '[ajax error] ' + err));
};
if (style === 1) {
$text = dom(tplText).text(content);
} else if (style === 2) {
$text = dom(tplMarkdown).html(marked(content));
} else if (style === 3) {
$text = dom(tplText);
const $code = dom('<code/>').text(content).appTo($text);
win.setTimeout(() => {
lolight.el($code[0]);
}, content.length < 20000 ? 0 : 500);
} else {
$text = dom(tplMarkdown).text(content);
}
const onAdjustSize = () => {
const $content = dom('#pv-content');
const $text = dom('#pv-txt-text');
if ($text.length) {
$text[0].style.height = $content[0].offsetHeight - 16 + 'px';
}
};
const onIdxChange = rel => {
currentIdx = (currentIdx + rel + currentItems.length) % currentItems.length;
currentItem = currentItems[currentIdx];
preview.setLabels([
currentItem.label,
String(currentItem.size) + ' bytes'
]);
preview.setIndex(currentIdx + 1, currentItems.length);
preview.setRawLink(currentItem.absHref);
dom('#pv-content').hide();
if (preview.isSpinnerVisible()) {
preview.showSpinner(true, currentItem.icon);
} else {
win.clearTimeout(spinnerTimeoutId);
spinnerTimeoutId = win.setTimeout(() => {
preview.showSpinner(true, currentItem.icon);
}, spinnerThreshold);
}
preloadText(currentItem, (item, textContent) => {
if (item !== currentItem) {
return;
}
const style = settings.styles[currentItem.type];
let $text;
if (style === 1) {
$text = dom(tplText).text(textContent);
} else if (style === 2) {
$text = dom(tplMarkdown).html(marked(textContent));
} else if (style === 3) {
$text = dom(tplText);
const $code = dom('<code/>').text(textContent).appTo($text);
win.setTimeout(() => {
lolight.el($code[0]);
}, textContent.length < 20000 ? 0 : 500);
} else {
$text = dom(tplMarkdown).text(textContent);
}
win.clearTimeout(spinnerTimeoutId);
preview.showSpinner(false);
dom('#pv-content')
.clr()
.app($text)
.show();
onAdjustSize();
});
return $text;
});
// .then(x => new Promise(resolve => setTimeout(() => resolve(x), 1000)));
};
const onEnter = (items, idx) => {
currentItems = items;
currentIdx = idx;
currentItem = items[idx];
preview.setOnIndexChange(onIdxChange);
preview.setOnAdjustSize(onAdjustSize);
preview.enter();
onIdxChange(0);
};
const initItem = item => {
if (item.$view && includes(keys(settings.styles), item.type)) {
item.$view.find('a').on('click', ev => {
ev.preventDefault();
const matchedItems = compact(dom('#items .item').map(el => {
const matchedItem = el._item;
return includes(keys(settings.styles), matchedItem.type) ? matchedItem : null;
}));
onEnter(matchedItems, matchedItems.indexOf(item));
});
}
state = previewX.pvState(items, idx, loadText, onAdjustSize);
};
const initItem = previewX.initItemFn(keys(settings.styles), onEnter);
const onViewChanged = added => each(added, initItem);
const init = () => {
@ -149,5 +95,4 @@ const init = () => {
event.sub('view.changed', onViewChanged);
};
init();

View file

@ -1,112 +1,61 @@
const {each, includes, compact, dom} = require('../../util');
const {win} = require('../../globals');
const {each, dom} = require('../../util');
const event = require('../../core/event');
const allsettings = require('../../core/settings');
const preview = require('./preview');
const previewX = require('./preview-x');
const settings = Object.assign({
enabled: false,
types: []
}, allsettings['preview-vid']);
const tpl = '<video id="pv-content-vid"/>';
const preloadVideo = (src, callback) => {
const $video = dom('<video/>')
.on('loadedmetadata', () => {
callback($video);
// win.setTimeout(() => {callback($video);}, 1000); // for testing
})
.attr('autoplay', 'autoplay')
.attr('controls', 'controls')
.attr('src', src);
let state;
const onAdjustSize = () => {
const el = dom('#pv-content-vid')[0];
if (!el) {
return;
}
const elContent = dom('#pv-content')[0];
const contentW = elContent.offsetWidth;
const contentH = elContent.offsetHeight;
const elW = el.offsetWidth;
const elH = el.offsetHeight;
dom(el).css({
left: (contentW - elW) * 0.5 + 'px',
top: (contentH - elH) * 0.5 + 'px'
});
const elVW = el.videoWidth;
const elVH = el.videoHeight;
preview.setLabels([
state.item.label,
String(elVW) + 'x' + String(elVH),
String((100 * elW / elVW).toFixed(0)) + '%'
]);
};
const loadVideo = item => {
return new Promise(resolve => {
const $el = dom(tpl)
.on('loadedmetadata', () => resolve($el))
// .attr('autoplay', 'autoplay')
.attr('controls', 'controls')
.attr('src', item.absHref);
});
// .then(x => new Promise(resolve => setTimeout(() => resolve(x), 1000)));
};
const onEnter = (items, idx) => {
const currentItems = items;
let currentIdx = idx;
let currentItem = items[idx];
const onAdjustSize = () => {
const $content = dom('#pv-content');
const $vid = dom('#pv-vid-video');
const contentW = $content[0].offsetWidth;
const contentH = $content[0].offsetHeight;
const vidW = ($vid[0] || {}).offsetWidth;
const vidH = ($vid[0] || {}).offsetHeight;
if ($vid.length === 0) {
return;
}
$vid.css({
left: (contentW - vidW) * 0.5 + 'px',
top: (contentH - vidH) * 0.5 + 'px'
});
const vidVW = $vid[0].videoWidth;
const vidVH = $vid[0].videoHeight;
preview.setLabels([
currentItem.label,
String(vidVW) + 'x' + String(vidVH),
String((100 * vidW / vidVW).toFixed(0)) + '%'
]);
};
const onIdxChange = rel => {
currentIdx = (currentIdx + rel + currentItems.length) % currentItems.length;
currentItem = currentItems[currentIdx];
const spinnerTimeout = win.setTimeout(() => preview.showSpinner(true), 200);
if (dom('#pv-vid-video').length) {
dom('#pv-vid-video')[0].pause();
}
const updateMeta = () => {
onAdjustSize();
preview.setIndex(currentIdx + 1, currentItems.length);
preview.setRawLink(currentItem.absHref);
};
const onReady = $preloadedContent => {
win.clearTimeout(spinnerTimeout);
preview.showSpinner(false);
dom('#pv-content')
.clr()
.app($preloadedContent.attr('id', 'pv-vid-video'))
.show();
updateMeta();
};
preloadVideo(currentItem.absHref, onReady);
};
onIdxChange(0);
preview.setOnIndexChange(onIdxChange);
preview.setOnAdjustSize(onAdjustSize);
preview.enter();
state = previewX.pvState(items, idx, loadVideo, onAdjustSize);
};
const initItem = item => {
if (item.$view && includes(settings.types, item.type)) {
item.$view.find('a').on('click', ev => {
ev.preventDefault();
const matchedItems = compact(dom('#items .item').map(el => {
const matchedItem = el._item;
return includes(settings.types, matchedItem.type) ? matchedItem : null;
}));
onEnter(matchedItems, matchedItems.indexOf(item));
});
}
};
const onViewChanged = added => {
each(added, initItem);
};
const initItem = previewX.initItemFn(settings.types, onEnter);
const onViewChanged = added => each(added, initItem);
const init = () => {
if (!settings.enabled) {

View file

@ -0,0 +1,68 @@
const {includes, compact, dom} = require('../../util');
const preview = require('./preview');
const initItemFn = (types, onEnter) => {
return item => {
if (item.$view && includes(types, item.type)) {
item.$view.find('a').on('click', ev => {
ev.preventDefault();
const matchedItems = compact(dom('#items .item').map(el => {
const matchedItem = el._item;
return includes(types, matchedItem.type) ? matchedItem : null;
}));
onEnter(matchedItems, matchedItems.indexOf(item));
});
}
};
};
const pvState = (items, idx = 0, load, adjust) => {
const inst = Object.assign(Object.create(pvState.prototype), {items, load, adjust});
preview.setOnAdjustSize(adjust);
preview.setOnIndexChange(delta => inst.moveIdx(delta));
preview.enter();
inst.setIdx(idx);
return inst;
};
pvState.prototype = {
constructor: pvState,
setIdx(idx) {
this.idx = (idx + this.items.length) % this.items.length;
this.item = this.items[this.idx];
preview.setLabels([this.item.label]);
preview.setIndex(this.idx + 1, this.items.length);
preview.setRawLink(this.item.absHref);
this.loadContent(this.item);
},
moveIdx(delta) {
this.setIdx(this.idx + delta);
},
loadContent(item) {
Promise.resolve()
.then(() => {
dom('#pv-content').hide().clr();
preview.showSpinner(true, item.thumbSquare || item.icon, 200);
})
.then(() => this.load(item))
.then($content => {
if (item !== this.item) {
return;
}
preview.showSpinner(false);
dom('#pv-content').clr().app($content).show();
this.adjust();
});
}
};
module.exports = {
initItemFn,
pvState
};

View file

@ -31,7 +31,7 @@ let userAliveTimeoutId = null;
let onIndexChange = null;
let onAdjustSize = null;
let spinnerVisible = false;
let spinnerTimeoutId;
const adjustSize = () => {
const docEl = win.document.documentElement;
@ -61,7 +61,7 @@ const adjustSize = () => {
}
if (isFn(onAdjustSize)) {
onAdjustSize(1);
onAdjustSize();
}
};
@ -167,22 +167,29 @@ const setOnAdjustSize = fn => {
onAdjustSize = fn;
};
const showSpinner = (show, src) => {
const showSpinner = (show, src, delay) => {
win.clearTimeout(spinnerTimeoutId);
const $spinner = dom('#pv-spinner');
if (show) {
const $back = $spinner.find('.back');
if (src) {
$back.attr('src', src).show();
} else {
$back.hide();
}
spinnerVisible = true;
$spinner.show();
} else {
if (!show) {
spinnerVisible = false;
$spinner.hide();
return;
}
if (!spinnerVisible && delay) {
spinnerTimeoutId = win.setTimeout(() => showSpinner(true, src), delay);
return;
}
const $back = $spinner.find('.back');
if (src) {
$back.attr('src', src).show();
} else {
$back.hide();
}
spinnerVisible = true;
$spinner.show();
};
const isSpinnerVisible = () => {