Update preview code.

This commit is contained in:
Lars Jung 2016-07-22 19:43:44 +02:00
parent 8a4ccd098c
commit 20e080751d
11 changed files with 166 additions and 194 deletions

View file

@ -10,6 +10,7 @@
* fixes shell command detection on Windows
* fixes `.htaccess` auth issues
* adds `rust` type and icon
* adds `autoplay` option to audio and video preview
* removes *Install* section from `README.md`, caused to much trouble
* updates build process to use `node 6.0+`, no need for babel now
* replaces `jquery-qrcode` with `kjua`

2
ghu.js
View file

@ -68,7 +68,7 @@ ghu.task('build:scripts', runtime => {
.then(webpack(webpackCfg([SRC]), {showStats: false}))
.then(wrap('\n\n// @include "pre.js"\n\n'))
.then(includeit())
.then(ife(() => runtime.args.production, uglify()))
.then(ife(() => runtime.args.production, uglify({compressor: {warnings: false}})))
.then(wrap(runtime.commentJs))
.then(write(mapper, {overwrite: true}));
});

View file

@ -220,6 +220,7 @@
*/
"preview-aud": {
"enabled": true,
"autoplay": true,
"types": ["aud"]
},
@ -283,6 +284,7 @@
*/
"preview-vid": {
"enabled": true,
"autoplay": true,
"types": ["vid-avi", "vid-flv", "vid-mkv", "vid-mov", "vid-mp4", "vid-mpg", "vid-webm"]
},

View file

@ -1,6 +1,7 @@
#pv-content-txt {
.raised;
box-sizing: border-box;
max-width: 960px;
text-align: left;
background: @col-back-paper;
@ -17,20 +18,19 @@
color: #68A9FF;
}
}
}
&.highlighted {
code {
line-height: 1.2em;
}
}
&.markdown {
font-size: 1.1em;
padding: 8px 24px;
code {
color: #008200;
}
pre#pv-content-txt {
code {
line-height: 1.2em;
}
}
div#pv-content-txt {
font-size: 1.1em;
padding: 8px 24px;
code {
color: #008200;
}
}

View file

@ -16,7 +16,7 @@
background: @col-grey-900;
}
#pv-content {
#pv-container {
position: absolute;
}

View file

@ -1,38 +1,27 @@
const {each, dom} = require('../../util');
const event = require('../../core/event');
const {dom} = require('../../util');
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,
autoplay: true,
types: []
}, allsettings['preview-aud']);
const tpl = '<audio id="pv-content-aud"/>';
let state;
const onAdjustSize = () => {
const updateGui = () => {
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 elW = el.offsetWidth;
const elH = el.offsetHeight;
dom(el).css({
left: (contentW - elW) * 0.5 + 'px',
top: (contentH - elH) * 0.5 + 'px'
});
preview.centerContent();
preview.setLabels([
state.item.label,
format.formatDate(dom('#pv-content-aud')[0].duration * 1000, 'm:ss')
format.formatDate(el.duration * 1000, 'm:ss')
]);
};
@ -40,26 +29,25 @@ 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);
if (settings.autoplay) {
$el.attr('autoplay', 'autoplay');
}
});
// .then(x => new Promise(resolve => setTimeout(() => resolve(x), 1000)));
};
const onEnter = (items, idx) => {
state = previewX.pvState(items, idx, loadAudio, onAdjustSize);
state = preview.state(items, idx, loadAudio, updateGui);
};
const initItem = previewX.initItemFn(settings.types, onEnter);
const onViewChanged = added => each(added, initItem);
const init = () => {
if (!settings.enabled) {
return;
}
event.sub('view.changed', onViewChanged);
preview.register(settings.types, onEnter);
};
init();

View file

@ -1,9 +1,7 @@
const {each, dom} = require('../../util');
const {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,
@ -14,22 +12,15 @@ const tpl = '<img id="pv-content-img"/>';
let state;
const onAdjustSize = () => {
const updateGui = () => {
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;
preview.centerContent();
dom(el).css({
left: (contentW - elW) * 0.5 + 'px',
top: (contentH - elH) * 0.5 + 'px'
});
const elW = el.offsetWidth;
const labels = [state.item.label];
if (!settings.size) {
@ -61,26 +52,22 @@ const loadImage = item => {
return settings.size ? requestSample(href) : href;
})
.then(href => new Promise(resolve => {
const $img = dom(tpl)
.on('load', () => resolve($img))
const $el = dom(tpl)
.on('load', () => resolve($el))
.attr('src', href);
}));
// .then(x => new Promise(resolve => setTimeout(() => resolve(x), 1000)));
};
const onEnter = (items, idx) => {
state = previewX.pvState(items, idx, loadImage, onAdjustSize);
state = preview.state(items, idx, loadImage, updateGui);
};
const initItem = previewX.initItemFn(settings.types, onEnter);
const onViewChanged = added => each(added, initItem);
const init = () => {
if (!settings.enabled) {
return;
}
event.sub('view.changed', onViewChanged);
preview.register(settings.types, onEnter);
};
init();

View file

@ -1,33 +1,28 @@
const lolight = require('lolight');
const marked = require('marked');
const {each, keys, dom} = require('../../util');
const {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-content-txt" class="highlighted"></pre>';
const tplMarkdown = '<div id="pv-content-txt" class="markdown"></div>';
const tplPre = '<pre id="pv-content-txt"></pre>';
const tplDiv = '<div id="pv-content-txt"></div>';
let state;
const onAdjustSize = () => {
const updateGui = () => {
const el = dom('#pv-content-txt')[0];
if (!el) {
return;
}
const elContent = dom('#pv-content')[0];
dom(el).css({
height: elContent.offsetHeight - 16 + 'px'
});
const container = dom('#pv-container')[0];
el.style.height = container.offsetHeight - 16 + 'px';
preview.setLabels([
state.item.label,
@ -56,43 +51,36 @@ const requestTextContent = href => {
const loadText = item => {
return requestTextContent(item.absHref)
.catch(err => '[ajax error] ' + err)
.catch(err => '[request failed] ' + err)
.then(content => {
const style = settings.styles[state.item.type];
let $text;
const style = settings.styles[item.type];
if (style === 1) {
$text = dom(tplText).text(content);
return dom(tplPre).text(content);
} else if (style === 2) {
$text = dom(tplMarkdown).html(marked(content));
return dom(tplDiv).html(marked(content));
} else if (style === 3) {
$text = dom(tplText);
const $code = dom('<code/>').text(content).appTo($text);
const $code = dom('<code></code>').text(content);
win.setTimeout(() => {
lolight.el($code[0]);
}, content.length < 20000 ? 0 : 500);
} else {
$text = dom(tplMarkdown).text(content);
return dom(tplPre).app($code);
}
return $text;
return dom(tplDiv).text(content);
});
// .then(x => new Promise(resolve => setTimeout(() => resolve(x), 1000)));
};
const onEnter = (items, idx) => {
state = previewX.pvState(items, idx, loadText, onAdjustSize);
state = preview.state(items, idx, loadText, updateGui);
};
const initItem = previewX.initItemFn(keys(settings.styles), onEnter);
const onViewChanged = added => each(added, initItem);
const init = () => {
if (!settings.enabled) {
return;
}
event.sub('view.changed', onViewChanged);
preview.register(keys(settings.styles), onEnter);
};
init();

View file

@ -1,34 +1,25 @@
const {each, dom} = require('../../util');
const event = require('../../core/event');
const {dom} = require('../../util');
const allsettings = require('../../core/settings');
const preview = require('./preview');
const previewX = require('./preview-x');
const settings = Object.assign({
enabled: false,
autoplay: true,
types: []
}, allsettings['preview-vid']);
const tpl = '<video id="pv-content-vid"/>';
let state;
const onAdjustSize = () => {
const updateGui = () => {
const el = dom('#pv-content-vid')[0];
if (!el) {
return;
}
const elContent = dom('#pv-content')[0];
const contentW = elContent.offsetWidth;
const contentH = elContent.offsetHeight;
preview.centerContent();
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;
@ -43,26 +34,25 @@ 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);
if (settings.autoplay) {
$el.attr('autoplay', 'autoplay');
}
});
// .then(x => new Promise(resolve => setTimeout(() => resolve(x), 1000)));
};
const onEnter = (items, idx) => {
state = previewX.pvState(items, idx, loadVideo, onAdjustSize);
state = preview.state(items, idx, loadVideo, updateGui);
};
const initItem = previewX.initItemFn(settings.types, onEnter);
const onViewChanged = added => each(added, initItem);
const init = () => {
if (!settings.enabled) {
return;
}
event.sub('view.changed', onViewChanged);
preview.register(settings.types, onEnter);
};
init();

View file

@ -1,68 +0,0 @@
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

@ -1,16 +1,16 @@
const {each, isFn, isNum, dom} = require('../../util');
const {each, isFn, isNum, dom, includes, compact} = require('../../util');
const {win} = require('../../globals');
const event = require('../../core/event');
const resource = require('../../core/resource');
const allsettings = require('../../core/settings');
const store = require('../../core/store');
const settings = Object.assign({
enabled: true
}, allsettings.preview);
const tplOverlay =
`<div id="pv-overlay">
<div id="pv-content"></div>
<div id="pv-container"></div>
<div id="pv-spinner"><img class="back"/><img class="spinner" src="${resource.image('spinner')}"/></div>
<div id="pv-prev-area" class="hof"><img src="${resource.image('preview-prev')}"/></div>
<div id="pv-next-area" class="hof"><img src="${resource.image('preview-next')}"/></div>
@ -40,7 +40,7 @@ const adjustSize = () => {
const margin = isFullscreen ? 0 : 20;
const barHeight = isFullscreen ? 0 : 48;
dom('#pv-content').css({
dom('#pv-container').css({
width: winWidth - 2 * margin + 'px',
height: winHeight - 2 * margin - barHeight + 'px',
left: margin + 'px',
@ -130,7 +130,7 @@ const onKeydown = ev => {
const onEnter = () => {
setLabels([]);
dom('#pv-content').clr();
dom('#pv-container').clr();
dom('#pv-overlay').show();
dom(win).on('keydown', onKeydown);
adjustSize();
@ -138,7 +138,7 @@ const onEnter = () => {
const onExit = () => {
setLabels([]);
dom('#pv-content').clr();
dom('#pv-container').clr();
dom('#pv-overlay').hide();
dom(win).off('keydown', onKeydown);
};
@ -192,8 +192,98 @@ const showSpinner = (show, src, delay) => {
$spinner.show();
};
const isSpinnerVisible = () => {
return spinnerVisible;
const centerContent = () => {
const $container = dom('#pv-container');
const container = $container[0];
const content = $container.children()[0];
if (!container || !content) {
return;
}
const containerW = container.offsetWidth;
const containerH = container.offsetHeight;
const contentW = content.offsetWidth;
const contentH = content.offsetHeight;
dom(content).css({
left: (containerW - contentW) * 0.5 + 'px',
top: (containerH - contentH) * 0.5 + 'px'
});
};
const state = (items, idx, load, adjust) => {
const inst = Object.assign(Object.create(state.prototype), {items, load, adjust});
inst.setIdx(idx);
setOnAdjustSize(adjust);
setOnIndexChange(delta => inst.moveIdx(delta));
onEnter();
return {
get item() {
return inst.item;
}
};
};
state.prototype = {
setIdx(idx) {
this.idx = (idx + this.items.length) % this.items.length;
this.item = this.items[this.idx];
this.updateGui();
this.updateContent();
},
moveIdx(delta) {
this.setIdx(this.idx + delta);
},
updateGui() {
setLabels([this.item.label]);
setIndex(this.idx + 1, this.items.length);
setRawLink(this.item.absHref);
},
updateContent() {
const item = this.item;
Promise.resolve()
.then(() => {
dom('#pv-container').hide().clr();
showSpinner(true, item.thumbSquare || item.icon, 200);
})
.then(() => this.load(item))
// delay for testing
// .then(x => new Promise(resolve => setTimeout(() => resolve(x), 1000)))
.then($content => {
if (item !== this.item) {
return;
}
showSpinner(false);
dom('#pv-container').clr().app($content).show();
this.adjust();
});
}
};
const register = (types, enter) => {
const initItem = 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;
}));
enter(matchedItems, matchedItems.indexOf(item));
});
}
};
event.sub('view.changed', added => each(added, initItem));
};
const init = () => {
@ -208,7 +298,7 @@ const init = () => {
.on('mousemove', userAlive)
.on('mousedown', userAlive)
.on('click', ev => {
if (ev.target.id === 'pv-overlay' || ev.target.id === 'pv-content') {
if (ev.target.id === 'pv-overlay' || ev.target.id === 'pv-container') {
onExit();
}
})
@ -228,17 +318,11 @@ const init = () => {
.on('load', adjustSize);
};
init();
module.exports = {
enter: onEnter,
exit: onExit,
setIndex,
setRawLink,
setLabels,
setOnIndexChange,
setOnAdjustSize,
showSpinner,
isSpinnerVisible
centerContent,
state,
register
};