cloudcmd/client/modules/view.js
2018-10-19 20:20:27 +03:00

347 lines
7 KiB
JavaScript

'use strict';
/* global CloudCmd, DOM */
require('../../css/view.css');
const itype = require('itype/legacy');
const rendy = require('rendy/legacy');
const exec = require('execon');
const currify = require('currify/legacy');
const {promisify} = require('es6-promisify');
const modal = require('@cloudcmd/modal');
const createElement = require('@cloudcmd/create-element');
const {time} = require('../../common/util');
const {FS} = require('../../common/cloudfunc');
const Files = require('../dom/files');
const Events = require('../dom/events');
const load = require('load.js');
const Images = require('../dom/images');
const {encode} = require('../../common/entity');
const testRegExp = currify((name, reg) => reg.test(name));
const lifo = currify((fn, el, cb, name) => fn(name, el, cb));
const addEvent = lifo(Events.add);
const getRegExp = (ext) => RegExp(`\\.${ext}$`, 'i');
const loadCSS = promisify(load.css);
module.exports.show = show;
module.exports.hide = hide;
let Loading = false;
const Name = 'View';
CloudCmd[Name] = module.exports;
const Info = DOM.CurrentInfo;
const Key = CloudCmd.Key;
const basename = (a) => a.split('/').pop();
let El, TemplateAudio, Overlay;
const Config = {
beforeShow: (callback) => {
Images.hide();
Key.unsetBind();
exec(callback);
},
beforeClose: (callback) => {
Events.rmKey(listener);
Key.setBind();
exec(callback);
},
afterShow: (callback) => {
El.focus();
exec(callback);
},
onOverlayClick,
afterClose : exec,
autoSize : false,
helpers: {
title: {},
},
};
module.exports.init = async () => {
await loadAll();
const events = [
'click',
'contextmenu',
];
events.forEach(addEvent(Overlay, onOverlayClick));
};
function show(data, options) {
const prefixUrl = CloudCmd.PREFIX_URL + FS;
if (Loading)
return;
if (!options || options.bindKeys !== false)
Events.addKey(listener);
El = createElement('div', {
className: 'view',
notAppend: true,
});
El.tabIndex = 0;
if (data) {
El.append(data);
modal.open(El, initConfig(Config, options));
return;
}
Images.show.load();
const path = prefixUrl + Info.path;
const type = getType(path);
switch(type) {
default:
return viewFile();
case 'image':
return viewImage(path, prefixUrl);
case 'media':
return viewMedia(path);
}
}
function viewMedia(path) {
getMediaElement(path, (element) => {
const allConfig = {
...Config,
...{
autoSize: true,
afterShow: () => {
element
.querySelector('audio, video')
.focus();
}
}
};
modal.open(element, allConfig);
});
}
function viewFile() {
Info.getData((error, data) => {
if (error)
return Images.hide();
const element = document.createTextNode(data);
const options = {
...Config,
};
if (CloudCmd.config('showFileName'))
options.title = Info.name;
El.append(element);
modal.open(El, options);
});
}
function initConfig(Config, options) {
const config = {
...Config
};
if (!options)
return config;
Object.keys(options).forEach((name) => {
const isConfig = !!config[name];
const item = options[name];
const isFunc = itype.function(item);
if (!isFunc || !isConfig) {
config[name] = options[name];
return;
}
const func = config[name];
config[name] = () => {
exec.series([func, item]);
};
});
return config;
}
function hide() {
modal.close();
}
function viewImage(href, prefixUrl) {
const makeTitle = (path) => {
return {
href: prefixUrl + path,
title: encode(basename(path)),
};
};
const names = Info.files
.map(DOM.getCurrentPath)
.filter(isImage);
const titles = names
.map(makeTitle);
const index = names.indexOf(Info.path);
const imageConfig = {
index,
autoSize : true,
arrows : true,
keys : true,
helpers : {
title : {}
}
};
const config = {
...Config,
...imageConfig,
};
modal.open(titles, config);
}
function isImage(name) {
const images = [
'jp(e|g|eg)',
'gif',
'png',
'bmp',
'webp',
'svg',
'ico'
];
return images
.map(getRegExp)
.some(testRegExp(name));
}
function isMedia(name) {
return isAudio(name) || isVideo(name);
}
function isAudio(name) {
return /\.(mp3|ogg|m4a)$/i.test(name);
}
function isVideo(name) {
return /\.(mp4|avi)$/i.test(name);
}
function getType(name) {
if (isImage(name))
return 'image';
if (isMedia(name))
return 'media';
}
function getMediaElement(src, callback) {
check(src, callback);
Files.get('view/media-tmpl', (error, template) => {
const {name} = Info;
if (error)
return alert(error);
if (!TemplateAudio)
TemplateAudio = template;
const is = isAudio(name);
const type = is ? 'audio' : 'video';
const innerHTML = rendy(TemplateAudio, {
src,
type,
name,
});
const element = createElement('div', {
innerHTML,
});
callback(element);
});
}
function check(src, callback) {
if (typeof src !== 'string')
throw Error('src should be a string!');
if (typeof callback !== 'function')
throw Error('callback should be a function');
}
/**
* function loads css and js of FancyBox
* @callback - executes, when everything loaded
*/
async function loadAll() {
const {PREFIX} = CloudCmd;
time(Name + ' load');
Loading = true;
await loadCSS(PREFIX + '/dist/view.css');
Loading = false;
}
function onOverlayClick(event) {
const position = {
x: event.clientX,
y: event.clientY
};
setCurrentByPosition(position);
}
function setCurrentByPosition(position) {
const element = DOM.getCurrentByPosition(position);
if (!element)
return;
const {
files,
filesPassive,
} = Info;
const isFiles = ~files.indexOf(element);
const isFilesPassive = ~filesPassive.indexOf(element);
if (!isFiles && !isFilesPassive)
return;
const isCurrent = DOM.isCurrentFile(element);
if (isCurrent)
return;
DOM.setCurrentFile(element);
}
function listener({keyCode}) {
if (keyCode === Key.ESC)
hide();
}