diff --git a/client/dom.js b/client/dom.js index c1842d09..58d0e834 100644 --- a/client/dom.js +++ b/client/dom.js @@ -7,1638 +7,11 @@ var CloudCmd, Util, DOM, CloudFunc; /* global itype */ /* global exec */ - var DOMFunc = function() {}, - DOMProto, - - ImagesProto = function() { - var Images, - ImageElementProto = function() { - var LOADING = 'loading', - LoadingImage, - HIDDEN = 'hidden', - ERROR = 'error'; - - function getLoadingType() { - return DOM.isSVG() ? '-svg' : '-gif'; - } - - function init() { - if (LoadingImage) - return; - - LoadingImage = LOADING + getLoadingType(); - } - - function getElement() { - init(); - - return DOM.load({ - name : 'span', - id : 'js-status-image', - className : 'icon', - attribute : 'data-progress', - notAppend : true - }); - } - - this.get = getElement; - - /* Функция создаёт картинку загрузки */ - this.loading = function() { - var element = getElement(), - classList = element.classList; - - classList.add(LOADING, LoadingImage); - classList.remove(ERROR, HIDDEN); - - return element; - }; - - /* Функция создаёт картинку ошибки загрузки */ - this.error = function() { - var element = getElement(), - classList = element.classList; - - classList.add(ERROR); - classList.remove(HIDDEN, LOADING, LoadingImage); - - return element; - }; - }; - - Images = new ImageElementProto(); - - this.show = load; - this.show.load = load; - this.show.error = error; - - /** - * Function shows loading spinner - * position = {top: true}; - */ - function load(position, panel) { - var current, - image = Images.loading(), - parent = image.parentElement, - refreshButton = DOM.getRefreshButton(panel); - - if (position === 'top') { - current = refreshButton.parentElement; - } else { - current = DOM.getCurrentFile(); - - if (current) - current = DOM.getByDataName('js-name', current); - else - current = refreshButton.parentElement; - } - - if (!parent || (parent && parent !== current)) - current.appendChild(image); - - DOM.show(image); - - return image; - } - - function error(text) { - var image = Images.error(); - - DOM.show(image); - image.title = text; - - CloudCmd.log(text); - - return image; - } - - /** - * hide load image - */ - this.hide = function() { - var element = Images.get(); - DOM.hide(element); - - return this; - }; - - this.setProgress = function(value, title) { - var DATA = 'data-progress', - element = Images.get(); - - if (element) { - element.setAttribute(DATA, value + '%'); - - if (title) - element.title = title; - } - - return this; - }; - - this.clearProgress = function() { - var DATA = 'data-progress', - element = Images.get(); - - if (element) { - element.setAttribute(DATA, ''); - element.title = ''; - } - - return this; - }; - }, - - DOMTreeProto = function() { - var DOM = this; - - /** - * check class of element - * - * @param element - * @param pClass - */ - this.isContainClass = function(element, className) { - var ret, classList; - - if (!element) - throw Error('element could not be empty!'); - - if (!className) - throw Error('className could not be empty!'); - - classList = element.classList; - ret = classList.contains(className); - - return ret; - }; - - /** - * Function search element by tag - * @param pTag - className - * @param element - element - */ - this.getByTag = function(pTag, element) { - return (element || document).getElementsByTagName(pTag); - }; - - /** - * Function search element by id - * @param Id - id - */ - this.getById = function(id, element) { - return (element || document).querySelector('#' + id); - }; - - /** - * Function search first element by class name - * @param pClass - className - * @param element - element - */ - this.getByClass = function(pClass, elementParam) { - var element = elementParam || document, - ret = this.getByClassAll(pClass, element)[0]; - - return ret; - }; - - this.getByDataName = function(attribute, element) { - var ret, - selector = '[' + 'data-name="' + attribute + '"]'; - - if (!element) - element = document; - - ret = element.querySelector(selector); - - return ret; - }; - - /** - * Function search element by class name - * @param pClass - className - * @param element - element - */ - this.getByClassAll = function(pClass, element) { - return (element || document).getElementsByClassName(pClass); - }; - - /** - * check SVG SMIL animation support - */ - this.isSVG = function() { - var ret, svgNode, name, - create = document.createElementNS, - SVG_URL = 'http://www.w3.org/2000/svg'; - - if (create) { - create = create.bind(document); - svgNode = create(SVG_URL, 'animate'); - name = svgNode.toString(); - ret = /SVGAnimate/.test(name); - } - - return ret; - }; - - /** - * add class=hidden to element - * - * @param element - */ - this.hide = function(element) { - element.classList.add('hidden'); - return DOM; - }; - - this.show = function(element) { - element.classList.remove('hidden'); - return DOM; - }; - }, - - CmdProto = function() { - var Cmd = this, - CurrentInfo = {}, - CURRENT_FILE = 'current-file', - SELECTED_FILE = 'selected-file', - SelectType = '*.*', - TITLE = 'Cloud Commander', - Title, - TabPanel = { - 'js-left' : null, - 'js-right' : null - }; - - /** - * private function thet unset currentfile - * - * @currentFile - */ - function unsetCurrentFile(currentFile) { - var ret = DOM.isCurrentFile(currentFile); - - if (ret) - currentFile.classList.remove(CURRENT_FILE); - - return ret; - } - - this.loadRemote = function(name, options, callback) { - var o = options, - Files = DOM.Files; - - if (!callback) - callback = options; - - if (o.name && window[o.name]) - callback(); - else - Files.get('modules', function(error, modules) { - var remoteTmpls, local, remote, - load = DOM.load, - prefix = CloudCmd.PREFIX, - online = CloudCmd.config('online') && navigator.onLine, - - remoteObj = Util.findObjByNameInArr(modules, 'remote'), - module = Util.findObjByNameInArr(remoteObj, name), - - isArray = itype.array(module.local), - version = module.version, - - funcON = function() { - load.parallel(remote, function(error) { - if (error) - funcOFF(); - else - callback(); - }); - }, - - funcOFF = function() { - load.parallel(local, callback); - }; - - if (isArray) { - remoteTmpls = module.remote; - local = module.local; - } else { - remoteTmpls = [module.remote]; - local = [module.local]; - } - - local = local.map(function(url) { - return prefix + url; - }); - - remote = remoteTmpls.map(function(tmpl) { - return rendy(tmpl, { - version: version - }); - }); - - Util.exec.if(online, funcON, funcOFF); - }); - - return DOM; - }; - - /** - * load jquery from google cdn or local copy - * @param callback - */ - this.loadJquery = function(callback) { - DOM.loadRemote('jquery', { - name : '$' - }, callback); - - return DOM; - }; - - this.loadSocket = function(callback) { - DOM.loadRemote('socket', { - name : 'io' - }, callback); - - return DOM; - }; - - /** function loads css and js of Menu - * @param callback - */ - this.loadMenu = function(callback) { - return DOM.loadRemote('menu', callback); - }; - - this.uploadFiles = function(dir, files) { - var array, - slice = [].slice, - i = 0, - n = 0, - func = function(name) { - return function() { - CloudCmd.refresh(null, function() { - DOM.setCurrentByName(name); - }); - }; - }, - - percent = function(i, n, per) { - var value; - - if (!per) - per = 100; - - value = Math.round(i * per / n); - - return value; - }, - - step = function(n) { - return 100 / n; - }, - - load = function(file, callback) { - var uploader, - Images = DOM.Images, - name = file.name, - path = dir + name, - prefixURL = CloudCmd.PREFIX_URL, - FS = CloudFunc.FS, - api = prefixURL + FS; - - ++i; - - uploader = DOM.load.put(api + path, file); - uploader.on('progress', function(count) { - var max = step(n), - value = (i - 1) * max + percent(count, 100, max); - - Images.show.load('top'); - Images.setProgress(Math.round(value)); - }); - - uploader.on('end', callback); - }; - - if (!files) { - files = dir; - dir = CurrentInfo.dirPath; - } - - n = files.length; - array = slice.call(files); - - if (n) { - Util.exec.eachSeries(array, load, func(files[0].name)); - } - }; - - /** - * create new folder - * - */ - this.promptNewDir = function() { - promptNew('directory', '?dir'); - }; - - /** - * create new file - * - * @typeName - * @type - */ - this.promptNewFile = function() { - promptNew('file'); - }; - - function promptNew(typeName, type) { - var RESTful = DOM.RESTful, - Dialog = DOM.Dialog, - path = '', - name = Cmd.getCurrentName(), - dir = Cmd.getCurrentDirPath(), - msg = 'New ' + typeName || 'File'; - - if (name === '..') - name = ''; - - Dialog.prompt(TITLE, msg, name, {cancel: false}).then(function(name) { - path = dir + name; - - if (type) - path += type; - - if (name) - RESTful.write(path, function(error) { - !error && CloudCmd.refresh(null, function() { - DOM.setCurrentByName(name); - }); - }); - }); - } - - /** - * get current direcotory name - */ - this.getCurrentDirName = function() { - var ret, - substr, - /* получаем имя каталога в котором находимся */ - href = this.getCurrentDirPath(); - - href = href.replace(/\/$/, ''); - substr = href.substr(href, href.lastIndexOf('/')); - ret = href.replace(substr + '/', '') || '/'; - - return ret; - }; - - /** - * get current direcotory path - */ - this.getCurrentDirPath = function(panel) { - var ret, path; - - if (!panel) - panel = this.getPanel(); - - path = this.getByDataName('js-path', panel); - ret = path && path.textContent; - - return ret; - }; - - /** - * get current direcotory path - */ - this.getParentDirPath = function(panel) { - var path = DOM.getCurrentDirPath(panel), - dirName = DOM.getCurrentDirName() + '/'; - - if (path !== '/') - path = path.replace(RegExp(dirName + '$'), ''); - - return path; - }; - - /** - * get not current direcotory path - */ - this.getNotCurrentDirPath = function() { - var panel = this.getPanel({active: false}), - path = this.getCurrentDirPath(panel); - - return path; - }; - - /** - * unified way to get current file - * - * @currentFile - */ - this.getCurrentFile = function() { - var ret = this.getByClass(CURRENT_FILE); - - return ret; - }; - - /** - * get current file by name - */ - this.getCurrentByName = function(name, panelParam) { - var element, - panel = panelParam || CurrentInfo.panel; - - name = 'js-file-' + name; - element = DOM.getByDataName(name, panel); - - return element; - }; - - /** - * unified way to get current file - * - * @currentFile - */ - this.getSelectedFiles = function() { - var panel = DOM.getPanel(), - selected = this.getByClassAll(SELECTED_FILE, panel), - ret = [].slice.call(selected); - - return ret; - }; - - - /* - * unselect all files - */ - this.unselectFiles = function(files) { - var array, - isArray = Array.isArray(files); - - if (!files) { - array = DOM.getSelectedFiles(); - } else if (!isArray) - array = [].slice.call(files); - - array.forEach(DOM.toggleSelectedFile); - }; - - /** - * get all selected files or current when none selected - * - * @currentFile - */ - this.getActiveFiles = function() { - var current = DOM.getCurrentFile(); - var files = DOM.getSelectedFiles(); - var name = DOM.getCurrentName(current); - - if (!files.length && name !== '..') - return [current]; - - return files; - }; - - /** - * get size - * @currentFile - */ - this.getCurrentSize = function(currentFile) { - var current = currentFile || Cmd.getCurrentFile(), - size = this.getByDataName('js-size', current); - - /* если это папка - возвращаем слово dir вместо размера*/ - size = size - .textContent - .replace(/^<|>$/g, ''); - - return size; - }; - - /** - * get size - * @currentFile - */ - this.loadCurrentSize = function(callback, currentFile) { - var RESTful = DOM.RESTful, - Images = DOM.Images, - current = currentFile || this.getCurrentFile(), - query = '?size', - link = this.getCurrentPath(current); - - Images.show.load(); - - if (name !== '..') - RESTful.read(link + query, function(error, size) { - if (!error) { - DOM.setCurrentSize(size, current); - Util.exec(callback, current); - Images.hide(); - } - }); - }; - - /** - * load hash - * @callback - * @currentFile - */ - this.loadCurrentHash = function(callback, currentFile) { - var RESTful = DOM.RESTful, - current = currentFile || DOM.getCurrentFile(), - query = '?hash', - link = DOM.getCurrentPath(current); - - RESTful.read(link + query, callback); - }; - - /** - * load current modification time of file - * @callback - * @currentFile - */ - this.loadCurrentTime = function(callback, currentFile) { - var RESTful = DOM.RESTful, - current = currentFile || this.getCurrentFile(), - query = '?time', - link = this.getCurrentPath(current); - - RESTful.read(link + query, callback); - }; - - /** - * set size - * @currentFile - */ - this.setCurrentSize = function(size, currentFile) { - var current = currentFile || this.getCurrentFile(), - sizeElement = this.getByDataName('js-size', current); - - sizeElement.textContent = size; - - }; - - /** - * @currentFile - */ - this.getCurrentMode = function(currentFile) { - var ret, - current = currentFile || this.getCurrentFile(), - lMode = this.getByDataName('js-mode', current); - ret = lMode.textContent; - - return ret; - }; - - /** - * @currentFile - */ - this.getCurrentOwner = function(currentFile) { - var ret, - current = currentFile || this.getCurrentFile(), - owner = this.getByDataName('js-owner', current); - - ret = owner.textContent; - - return ret; - }; - - /** - * unified way to get current file content - * - * @param callback - * @param currentFile - */ - this.getCurrentData = function(callback, currentFile) { - var hash, - RESTful = DOM.RESTful, - Dialog = DOM.Dialog, - Info = DOM.CurrentInfo, - current = currentFile || DOM.getCurrentFile(), - path = DOM.getCurrentPath(current), - isDir = DOM.isCurrentIsDir(current), - - func = function(error, data) { - var length, - ONE_MEGABYTE = 1024 * 1024 * 1024; - - if (!error) { - if (itype.object(data)) - data = Util.json.stringify(data); - - length = data.length; - - if (hash && length < ONE_MEGABYTE) - DOM.saveDataToStorage(path, data, hash); - } - - callback(error, data); - }; - - if (Info.name === '..') { - Dialog.alert.noFiles(TITLE); - return callback(Error('No files selected!')); - } - - if (isDir) - return RESTful.read(path, func); - - DOM.checkStorageHash(path, function(error, equal, hashNew) { - if (error) { - callback(error); - } else if (equal) { - DOM.getDataFromStorage(path, callback); - } else { - hash = hashNew; - RESTful.read(path, func); - } - }); - }; - - /** - * unified way to save current file content - * - * @callback - function({data, name}) {} - * @currentFile - */ - this.saveCurrentData = function(url, data, callback, query) { - if (!query) - query = ''; - - DOM.RESTful.write(url + query, data, function(error) { - !error && DOM.saveDataToStorage(url, data); - }); - }; - - /** - * unified way to get RefreshButton - */ - this.getRefreshButton = function(panel) { - var currentPanel = panel || this.getPanel(), - refresh = this.getByDataName('js-refresh', currentPanel); - - return refresh; - }; - - this.setCurrentByName = function(name) { - var current = this.getCurrentByName(name); - return this.setCurrentFile(current); - }; - - /** - * unified way to set current file - */ - this.setCurrentFile = function(currentFile, options) { - var path, pathWas, title, - o = options, - FS = CloudFunc.FS, - CENTER = true, - currentFileWas = this.getCurrentFile(); - - if (currentFile) { - if (currentFileWas) { - pathWas = DOM.getCurrentDirPath(); - unsetCurrentFile(currentFileWas); - } - - currentFile.classList.add(CURRENT_FILE); - - path = DOM.getCurrentDirPath(); - - if (path !== pathWas) { - title = CloudFunc.getTitle(path); - this.setTitle(title); - - /* history could be present - * but it should be false - * to prevent default behavior - */ - if (!o || o.history !== false) { - if (path !== '/') - path = FS + path; - - DOM.setHistory(path, null, path); - } - } - - /* scrolling to current file */ - this.scrollIntoViewIfNeeded(currentFile, CENTER); - - Cmd.updateCurrentInfo(); - } - - return this; - }; - - /* - * set current file by position - * - * @param layer - element - * @param - position {x, y} - */ - this.getCurrentByPosition = function(position) { - var element, tag, isChild, - x = position.x, - y = position.y; - - element = document.elementFromPoint(x, y), - tag = element.tagName; - - isChild = /A|SPAN|LI/.test(tag); - - if (!isChild) { - element = null; - } else { - switch (tag) { - case 'A': - element = element.parentElement.parentElement; - break; - - case 'SPAN': - element = element.parentElement; - break; - } - } - - if (element && element.tagName !== 'LI') - element = null; - - return element; - }; - - /** - * select current file - * @param currentFile - */ - this.selectFile = function(currentFile) { - var current = currentFile || DOM.getCurrentFile(); - - current.classList.add(SELECTED_FILE); - - return Cmd; - }; - - this.toggleSelectedFile = function(currentFile) { - var current = currentFile || this.getCurrentFile(); - - current.classList.toggle(SELECTED_FILE); - - return Cmd; - }; - - this.toggleAllSelectedFiles = function() { - DOM.getAllFiles().map(DOM.toggleSelectedFile); - - return Cmd; - }; - - this.selectAllFiles = function() { - DOM.getAllFiles().map(DOM.selectFile); - - return Cmd; - }; - - this.getAllFiles = function() { - var i, - panel = DOM.getPanel(), - files = DOM.getFiles(panel), - name = DOM.getCurrentName(files[0]); - - if (name === '..') - i = 1; - else - i = 0; - - return [].slice.call(files, i); - }; - - function selectByPattern(msg, files) { - var n, regExp, - Dialog = DOM.Dialog, - allMsg = 'Specify file type for ' + msg + ' selection', - i = 0, - matches = 0, - name, - shouldSel = msg === 'expand', - isSelected, isMatch, - current; - - Dialog.prompt(TITLE, allMsg, SelectType, {cancel: false}).then(function(type) { - SelectType = type; - - regExp = Util.getRegExp(type); - - n = files && files.length; - for (i = 0; i < n; i++) { - current = files[i]; - name = DOM.getCurrentName(current); - - if (name !== '..') { - isMatch = regExp.test(name); - - if (isMatch) { - ++matches; - - isSelected = DOM.isSelected(current); - - if (shouldSel) - isSelected = !isSelected; - - if (isSelected) - DOM.toggleSelectedFile(current); - } - } - } - - if (!matches) - Dialog.alert('Select Files', 'No matches found!'); - }); - } - - /** - * open dialog with expand selection - */ - this.expandSelection = function() { - var msg = 'expand', - files = CurrentInfo.files; - - selectByPattern(msg, files); - }; - - /** - * open dialog with shrink selection - */ - this.shrinkSelection = function() { - var msg = 'shrink', - files = DOM.getSelectedFiles(); - - selectByPattern(msg, files); - }; - - /** - * setting history wrapper - */ - this.setHistory = function(data, title, url) { - var ret = window.history; - - url = CloudCmd.PREFIX + url; - - if (ret) - history.pushState(data, title, url); - - return ret; - }; - - /** - * set title or create title element - * - * @param name - */ - - this.setTitle = function(name) { - if (!Title) - Title = DOM.getByTag('title')[0] || - DOM.load({ - name : 'title', - innerHTML : name, - parentElement : document.head - }); - - Title.textContent = name; - - return this; - }; - - /** - * current file check - * - * @param currentFile - */ - this.isCurrentFile = function(currentFile) { - var ret; - - if (currentFile) - ret = this.isContainClass(currentFile, CURRENT_FILE); - - return ret; - }; - - /** - * selected file check - * - * @param currentFile - */ - this.isSelected = function(pSelected) { - var ret; - - if (pSelected) - ret = this.isContainClass(pSelected, SELECTED_FILE); - - return ret; - }; - - /** - * check is current file is a directory - * - * @param currentFile - */ - this.isCurrentIsDir = function(currentFile) { - var current = currentFile || this.getCurrentFile(), - fileType = this.getByDataName('js-type', current), - ret = this.isContainClass(fileType, 'directory'); - - return ret; - }; - - /** - * get link from current (or param) file - * - * @param currentFile - current file by default - */ - this.getCurrentLink = function(currentFile) { - var current = currentFile || this.getCurrentFile(), - link = this.getByTag('a', current); - - return link[0]; - }; - - /** - * get link from current (or param) file - * - * @param currentFile - current file by default - */ - this.getCurrentPath = function(currentFile) { - var current = currentFile || DOM.getCurrentFile(), - element = DOM.getByTag('a', current)[0], - path = element.getAttribute('href'), - prefix = CloudCmd.PREFIX, - fs = CloudFunc.FS; - - path = path.replace(RegExp('^' + prefix + fs), ''); - - return path; - }; - - /** - * get name from current (or param) file - * - * @param currentFile - */ - this.getCurrentName = function(currentFile) { - var link, - name = '', - current = currentFile || this.getCurrentFile(); - - if (current) - link = this.getCurrentLink(current); - - if (link) { - name = link.title; - } - - return name; - }; - - this.getFilenames = function(allFiles) { - var first, name, - files, - slice = Array.prototype.slice, - names = []; - - if (!allFiles) - throw Error('AllFiles could not be empty'); - - files = slice.call(allFiles); - first = files[0]; - - if (first) { - name = this.getCurrentName(first); - } else { - first = this.getCurrentFile(); - name = this.getCurrentName(first); - } - - if (name === '..') - files.shift(); - - names = files.map(function(current) { - return DOM.getCurrentName(current); - }); - - return names; - }; - - /** - * set name from current (or param) file - * - * @param name - * @param current - */ - this.setCurrentName = function(name, current) { - var Info = CurrentInfo, - link = Info.link, - FS = CloudFunc.FS, - PREFIX = CloudCmd.PREFIX, - dir = PREFIX + FS + Info.dirPath; - - link.title = name; - link.innerHTML = CloudFunc.Entity.encode(name); - link.href = dir + name; - - current.setAttribute('data-name', 'js-file-' + name); - - return link; - }; - - /** - * check storage hash - */ - this.checkStorageHash = function(name, callback) { - var Storage = DOM.Storage; - var parallel = Util.exec.parallel; - var loadHash = DOM.loadCurrentHash; - var nameHash = name + '-hash'; - var getStoreHash = Util.exec.with(Storage.get, nameHash); - - if (typeof name !== 'string') - throw Error('name should be a string!'); - - if (typeof callback !== 'function') - throw Error('callback should be a function!'); - - parallel([loadHash, getStoreHash], function(error, loadHash, storeHash) { - var equal; - var isContain = /error/.test(loadHash); - - if (isContain) - error = loadHash; - else if (loadHash === storeHash) - equal = true; - - callback(error, equal, loadHash); - }); - }; - - /** - * save data to storage - * - * @param name - * @param data - * @param hash - * @param callback - */ - this.saveDataToStorage = function(name, data, hash, callback) { - var allowed = CloudCmd.config('localStorage'); - var isDir = DOM.isCurrentIsDir(); - var nameHash = name + '-hash'; - var nameData = name + '-data'; - - if (!allowed || isDir) - return Util.exec(callback); - - Util.exec.if(hash, function() { - var Storage = DOM.Storage; - - Storage.set(nameHash, hash); - Storage.set(nameData, data); - - Util.exec(callback, hash); - }, function(callback) { - DOM.loadCurrentHash(function(error, loadHash) { - hash = loadHash; - callback(); - }); - }); - }; - - /** - * save data to storage - * - * @param name - * @param data - * @param callback - */ - this.getDataFromStorage = function(name, callback) { - var Storage = DOM.Storage; - var nameHash = name + '-hash'; - var nameData = name + '-data'; - var allowed = CloudCmd.config('localStorage'); - var isDir = DOM.isCurrentIsDir(); - - if (!allowed || isDir) - return Util.exec(callback); - - Util.exec.parallel([ - exec.with(Storage.get, nameData), - exec.with(Storage.get, nameHash), - ], callback); - }; - - this.getFM = function() { - return this.getPanel().parentElement; - }; - - this.getPanelPosition = function(panel) { - panel = panel || DOM.getPanel(); - - return panel.dataset.name.replace('js-', ''); - }; - - /** function getting panel active, or passive - * @param options = {active: true} - */ - this.getPanel = function(options) { - var files, panel, isLeft, - dataName = 'js-', - current = this.getCurrentFile(); - - if (!current) { - panel = this.getByDataName('js-left'); - } else { - files = current.parentElement, - panel = files.parentElement, - isLeft = panel.getAttribute('data-name') === 'js-left'; - } - - /* if {active : false} getting passive panel */ - if (options && !options.active) { - dataName += isLeft ? 'right' : 'left'; - panel = this.getByDataName(dataName); - } - - /* if two panels showed - * then always work with passive - * panel - */ - if (window.innerWidth < CloudCmd.MIN_ONE_PANEL_WIDTH) - panel = this.getByDataName('js-left'); - - - if (!panel) - throw Error('can not find Active Panel!'); - - return panel; - }; - - this.getFiles = function(element) { - var files = DOM.getByDataName('js-files', element), - ret = files.children || []; - - return ret; - }; - - /** - * shows panel right or left (or active) - */ - this.showPanel = function(active) { - var ret = true, - panel = this.getPanel({active: active}); - - if (panel) - this.show(panel); - else - ret = false; - - return ret; - }; - - /** - * hides panel right or left (or active) - */ - this.hidePanel = function(active) { - var ret = false, - panel = this.getPanel({active: active}); - - if (panel) - ret = this.hide(panel); - - return ret; - }; - - /** - * open window with URL - * @param url - */ - this.openWindow = function(url) { - var left = 140, - top = 187, - width = 1000, - height = 650, - - options = 'left=' + left + - ',top=' + top + - ',width=' + width + - ',height=' + height + - ',personalbar=0,toolbar=0' + - ',scrollbars=1,resizable=1', - - wnd = window.open(url, 'Cloud Commander Auth', options); - - if (!wnd) - DOM.Dialog.alert(TITLE, 'Please disable your popup blocker and try again.'); - - return wnd; - }; - - /** - * remove child of element - * @param pChild - * @param element - */ - this.remove = function(child, element) { - var parent = element || document.body; - - parent.removeChild(child); - - return DOM; - }; - - /** - * remove current file from file table - * @param current - * - */ - this.deleteCurrent = function(current) { - var next, prev, currentNew; - - if (!current) - Cmd.getCurrentFile(); - - var parent = current && current.parentElement; - var name = Cmd.getCurrentName(current); - - if (current && name !== '..') { - next = current.nextSibling, - prev = current.previousSibling; - - if (next) - currentNew = next; - else if (prev) - currentNew = prev; - - DOM.setCurrentFile(currentNew); - - parent.removeChild(current); - } - - return currentNew; - }; - - /** - * remove selected files from file table - * @Selected - */ - this.deleteSelected = function(selected) { - var i, n, current; - - if (!selected) - selected = this.getSelectedFiles(); - - if (selected) { - n = selected.length; - - for (i = 0; i < n; i++) { - current = selected[i]; - DOM.deleteCurrent(current); - } - } - - return selected; - }; - - /** - * rename current file - * - * @currentFile - */ - this.renameCurrent = function(current) { - var from, dirPath, files, isExist, - RESTful = DOM.RESTful, - Dialog = DOM.Dialog; - - if (!Cmd.isCurrentFile(current)) - current = Cmd.getCurrentFile(); - - from = Cmd.getCurrentName(current); - - if (from === '..') - Dialog.alert.noFiles(TITLE); - else - Dialog.prompt(TITLE, 'Rename', from, {cancel: false}).then(function(to) { - isExist = !!DOM.getCurrentByName(to); - dirPath = Cmd.getCurrentDirPath(); - - if (from !== to) { - files = { - from : dirPath + from, - to : dirPath + to - }; - - RESTful.mv(files, function(error) { - var Storage = DOM.Storage; - - if (!error) { - DOM.setCurrentName(to, current); - Cmd.updateCurrentInfo(); - Storage.remove(dirPath); - - if (isExist) - CloudCmd.refresh(); - } - }); - } - }); - }; - - /** - * unified way to scrollIntoViewIfNeeded - * (native suporte by webkit only) - * @param element - * @param pCenter - */ - this.scrollIntoViewIfNeeded = function(element, center) { - var ret = element && element.scrollIntoViewIfNeeded; - - /* for scroll as small as possible - * param should be false - */ - if (arguments.length === 1) - center = false; - - if (ret) - element.scrollIntoViewIfNeeded(center); - - return ret; - }; - - /* scroll on one page*/ - this.scrollByPages = function(element, pPages) { - var ret = element && element.scrollByPages && pPages; - - if (ret) - element.scrollByPages(pPages); - - return ret; - }; - - this.changePanel = function() { - var dataName, files, current, - - panel = DOM.getPanel(), - panelPassive = DOM.getPanel({ - active: false - }), - - name = DOM.getCurrentName(), - - filesPassive = DOM.getFiles(panelPassive); - - dataName = panel.getAttribute('data-name'); - TabPanel[dataName] = name; - - panel = panelPassive; - dataName = panel.getAttribute('data-name'); - - name = TabPanel[dataName]; - - if (name) { - current = DOM.getCurrentByName(name, panel); - - if (current) - files = current.parentElement; - } - - if (!files || !files.parentElement) { - current = DOM.getCurrentByName(name, panel); - - if (!current) - current = filesPassive[0]; - } - - DOM.setCurrentFile(current, { - history: true - }); - - return this; - }; - - this.getPackerExt = function(type) { - if (type === 'zip') - return '.zip'; - - return '.tar.gz'; - }; - - this.goToDirectory = function() { - var msg = 'Go to directory:', - path = CurrentInfo.dirPath, - Dialog = DOM.Dialog; - - Dialog.prompt(TITLE, msg, path, {cancel: false}).then(function(path) { - CloudCmd.loadDir({ - path: path - }); - }); - }, - - this.duplicatePanel = function() { - var isDir = CurrentInfo.isDir; - var path = CurrentInfo.dirPath; - var panel = CurrentInfo.panelPassive; - var noCurrent = !CurrentInfo.isOnePanel; - - if (isDir) - path = CurrentInfo.path; - - CloudCmd.loadDir({ - path: path, - panel: panel, - noCurrent: noCurrent, - }); - }; - - this.swapPanels = function() { - var Info = CurrentInfo, - panel = Info.panel, - panelPassive = Info.panelPassive, - - currentIndex = [].indexOf.call(Info.files, Info.element), - - dirPath = DOM.getCurrentDirPath(), - dirPathPassive = DOM.getNotCurrentDirPath(); - - CloudCmd.loadDir({ - path: dirPath, - panel: panelPassive, - noCurrent: true - }); - - CloudCmd.loadDir({ - path: dirPathPassive, - panel: panel - }, function() { - var el, - files = Info.files, - length = files.length - 1; - - if (currentIndex > length) - currentIndex = length; - - el = files[currentIndex]; - - DOM.setCurrentFile(el); - }); - }; - - this.CurrentInfo = CurrentInfo, - - this.updateCurrentInfo = function(currentFile) { - var info = Cmd.CurrentInfo, - current = currentFile || Cmd.getCurrentFile(), - files = current.parentElement, - panel = files.parentElement, - - panelPassive = Cmd.getPanel({ - active: false - }), - - filesPassive = DOM.getFiles(panelPassive), - - name = Cmd.getCurrentName(current); - - info.dir = Cmd.getCurrentDirName(); - info.dirPath = Cmd.getCurrentDirPath(); - info.parentDirPath = Cmd.getParentDirPath(); - info.element = current; - info.ext = Util.getExt(name); - info.files = [].slice.call(files.children), - info.filesPassive = [].slice.call(filesPassive), - info.first = files.firstChild; - info.getData = Cmd.getCurrentData; - info.last = files.lastChild; - info.link = Cmd.getCurrentLink(current); - info.mode = Cmd.getCurrentMode(current); - info.name = name; - info.path = Cmd.getCurrentPath(current); - info.panel = panel; - info.panelPassive = panelPassive; - info.size = Cmd.getCurrentSize(current); - info.isDir = Cmd.isCurrentIsDir(); - info.isSelected = Cmd.isSelected(current); - info.panelPosition = Cmd.getPanel().dataset.name.replace('js-', ''); - info.isOnePanel = - info.panel.getAttribute('data-name') === - info.panelPassive.getAttribute('data-name'); - }; - - }, - - DOMTree = Util.extendProto(DOMTreeProto), - Images = Util.extendProto(ImagesProto); + var DOMFunc = function() {}; + var DOMTree = Util.extendProto(DOMTreeProto); + var Images = Util.extendProto(ImagesProto); - DOMProto = DOMFunc.prototype = new CmdProto(); + var DOMProto = DOMFunc.prototype = new CmdProto(); Util.extend(DOMProto, [ DOMTree, { @@ -1646,6 +19,1629 @@ var CloudCmd, Util, DOM, CloudFunc; } ]); - DOM = new DOMFunc(); + window.DOM = new DOMFunc(); + function ImagesProto() { + var Images = new ImageElementProto(); + + function ImageElementProto () { + var LOADING = 'loading'; + var LoadingImage; + var HIDDEN = 'hidden'; + var ERROR = 'error'; + + function getLoadingType() { + return DOM.isSVG() ? '-svg' : '-gif'; + } + + function init() { + if (LoadingImage) + return; + + LoadingImage = LOADING + getLoadingType(); + } + + function getElement() { + init(); + + return DOM.load({ + name : 'span', + id : 'js-status-image', + className : 'icon', + attribute : 'data-progress', + notAppend : true + }); + } + + this.get = getElement; + + /* Функция создаёт картинку загрузки */ + this.loading = function() { + var element = getElement(); + var classList = element.classList; + + classList.add(LOADING, LoadingImage); + classList.remove(ERROR, HIDDEN); + + return element; + }; + + /* Функция создаёт картинку ошибки загрузки */ + this.error = function() { + var element = getElement(); + var classList = element.classList; + + classList.add(ERROR); + classList.remove(HIDDEN, LOADING, LoadingImage); + + return element; + }; + } + + this.show = load; + this.show.load = load; + this.show.error = error; + + /** + * Function shows loading spinner + * position = {top: true}; + */ + function load(position, panel) { + var current, + image = Images.loading(), + parent = image.parentElement, + refreshButton = DOM.getRefreshButton(panel); + + if (position === 'top') { + current = refreshButton.parentElement; + } else { + current = DOM.getCurrentFile(); + + if (current) + current = DOM.getByDataName('js-name', current); + else + current = refreshButton.parentElement; + } + + if (!parent || (parent && parent !== current)) + current.appendChild(image); + + DOM.show(image); + + return image; + } + + function error(text) { + var image = Images.error(); + + DOM.show(image); + image.title = text; + + CloudCmd.log(text); + + return image; + } + + /** + * hide load image + */ + this.hide = function() { + var element = Images.get(); + DOM.hide(element); + + return this; + }; + + this.setProgress = function(value, title) { + var DATA = 'data-progress', + element = Images.get(); + + if (element) { + element.setAttribute(DATA, value + '%'); + + if (title) + element.title = title; + } + + return this; + }; + + this.clearProgress = function() { + var DATA = 'data-progress', + element = Images.get(); + + if (element) { + element.setAttribute(DATA, ''); + element.title = ''; + } + + return this; + }; + } + + function DOMTreeProto() { + var DOM = this; + + /** + * check class of element + * + * @param element + * @param pClass + */ + this.isContainClass = function(element, className) { + var ret, classList; + + if (!element) + throw Error('element could not be empty!'); + + if (!className) + throw Error('className could not be empty!'); + + classList = element.classList; + ret = classList.contains(className); + + return ret; + }; + + /** + * Function search element by tag + * @param pTag - className + * @param element - element + */ + this.getByTag = function(pTag, element) { + return (element || document).getElementsByTagName(pTag); + }; + + /** + * Function search element by id + * @param Id - id + */ + this.getById = function(id, element) { + return (element || document).querySelector('#' + id); + }; + + /** + * Function search first element by class name + * @param pClass - className + * @param element - element + */ + this.getByClass = function(pClass, elementParam) { + var element = elementParam || document, + ret = this.getByClassAll(pClass, element)[0]; + + return ret; + }; + + this.getByDataName = function(attribute, element) { + var ret, + selector = '[' + 'data-name="' + attribute + '"]'; + + if (!element) + element = document; + + ret = element.querySelector(selector); + + return ret; + }; + + /** + * Function search element by class name + * @param pClass - className + * @param element - element + */ + this.getByClassAll = function(pClass, element) { + return (element || document).getElementsByClassName(pClass); + }; + + /** + * check SVG SMIL animation support + */ + this.isSVG = function() { + var ret, svgNode, name, + create = document.createElementNS, + SVG_URL = 'http://www.w3.org/2000/svg'; + + if (create) { + create = create.bind(document); + svgNode = create(SVG_URL, 'animate'); + name = svgNode.toString(); + ret = /SVGAnimate/.test(name); + } + + return ret; + }; + + /** + * add class=hidden to element + * + * @param element + */ + this.hide = function(element) { + element.classList.add('hidden'); + return DOM; + }; + + this.show = function(element) { + element.classList.remove('hidden'); + return DOM; + }; + } + + function CmdProto() { + var Cmd = this; + var CurrentInfo = {}; + var CURRENT_FILE = 'current-file'; + var SELECTED_FILE = 'selected-file'; + var SelectType = '*.*'; + var TITLE = 'Cloud Commander'; + var Title; + var TabPanel = { + 'js-left' : null, + 'js-right' : null + }; + + /** + * private function thet unset currentfile + * + * @currentFile + */ + function unsetCurrentFile(currentFile) { + var ret = DOM.isCurrentFile(currentFile); + + if (ret) + currentFile.classList.remove(CURRENT_FILE); + + return ret; + } + + this.loadRemote = function(name, options, callback) { + var o = options; + var Files = DOM.Files; + + if (!callback) + callback = options; + + if (o.name && window[o.name]) + callback(); + else + Files.get('modules', function(error, modules) { + var remoteTmpls, local, remote, + load = DOM.load, + prefix = CloudCmd.PREFIX, + online = CloudCmd.config('online') && navigator.onLine, + + remoteObj = Util.findObjByNameInArr(modules, 'remote'), + module = Util.findObjByNameInArr(remoteObj, name), + + isArray = itype.array(module.local), + version = module.version, + + funcON = function() { + load.parallel(remote, function(error) { + if (error) + funcOFF(); + else + callback(); + }); + }, + + funcOFF = function() { + load.parallel(local, callback); + }; + + if (isArray) { + remoteTmpls = module.remote; + local = module.local; + } else { + remoteTmpls = [module.remote]; + local = [module.local]; + } + + local = local.map(function(url) { + return prefix + url; + }); + + remote = remoteTmpls.map(function(tmpl) { + return rendy(tmpl, { + version: version + }); + }); + + Util.exec.if(online, funcON, funcOFF); + }); + + return DOM; + }; + + /** + * load jquery from google cdn or local copy + * @param callback + */ + this.loadJquery = function(callback) { + DOM.loadRemote('jquery', { + name : '$' + }, callback); + + return DOM; + }; + + this.loadSocket = function(callback) { + DOM.loadRemote('socket', { + name : 'io' + }, callback); + + return DOM; + }; + + /** function loads css and js of Menu + * @param callback + */ + this.loadMenu = function(callback) { + return DOM.loadRemote('menu', callback); + }; + + this.uploadFiles = function(dir, files) { + var array, + slice = [].slice, + i = 0, + n = 0, + func = function(name) { + return function() { + CloudCmd.refresh(null, function() { + DOM.setCurrentByName(name); + }); + }; + }, + + percent = function(i, n, per) { + var value; + + if (!per) + per = 100; + + value = Math.round(i * per / n); + + return value; + }, + + step = function(n) { + return 100 / n; + }, + + load = function(file, callback) { + var uploader, + Images = DOM.Images, + name = file.name, + path = dir + name, + prefixURL = CloudCmd.PREFIX_URL, + FS = CloudFunc.FS, + api = prefixURL + FS; + + ++i; + + uploader = DOM.load.put(api + path, file); + uploader.on('progress', function(count) { + var max = step(n), + value = (i - 1) * max + percent(count, 100, max); + + Images.show.load('top'); + Images.setProgress(Math.round(value)); + }); + + uploader.on('end', callback); + }; + + if (!files) { + files = dir; + dir = CurrentInfo.dirPath; + } + + n = files.length; + array = slice.call(files); + + if (n) { + Util.exec.eachSeries(array, load, func(files[0].name)); + } + }; + + /** + * create new folder + * + */ + this.promptNewDir = function() { + promptNew('directory', '?dir'); + }; + + /** + * create new file + * + * @typeName + * @type + */ + this.promptNewFile = function() { + promptNew('file'); + }; + + function promptNew(typeName, type) { + var RESTful = DOM.RESTful, + Dialog = DOM.Dialog, + path = '', + name = Cmd.getCurrentName(), + dir = Cmd.getCurrentDirPath(), + msg = 'New ' + typeName || 'File'; + + if (name === '..') + name = ''; + + Dialog.prompt(TITLE, msg, name, {cancel: false}).then(function(name) { + path = dir + name; + + if (type) + path += type; + + if (name) + RESTful.write(path, function(error) { + !error && CloudCmd.refresh(null, function() { + DOM.setCurrentByName(name); + }); + }); + }); + } + + /** + * get current direcotory name + */ + this.getCurrentDirName = function() { + var ret, + substr, + /* получаем имя каталога в котором находимся */ + href = this.getCurrentDirPath(); + + href = href.replace(/\/$/, ''); + substr = href.substr(href, href.lastIndexOf('/')); + ret = href.replace(substr + '/', '') || '/'; + + return ret; + }; + + /** + * get current direcotory path + */ + this.getCurrentDirPath = function(panel) { + var ret, path; + + if (!panel) + panel = this.getPanel(); + + path = this.getByDataName('js-path', panel); + ret = path && path.textContent; + + return ret; + }; + + /** + * get current direcotory path + */ + this.getParentDirPath = function(panel) { + var path = DOM.getCurrentDirPath(panel), + dirName = DOM.getCurrentDirName() + '/'; + + if (path !== '/') + path = path.replace(RegExp(dirName + '$'), ''); + + return path; + }; + + /** + * get not current direcotory path + */ + this.getNotCurrentDirPath = function() { + var panel = this.getPanel({active: false}), + path = this.getCurrentDirPath(panel); + + return path; + }; + + /** + * unified way to get current file + * + * @currentFile + */ + this.getCurrentFile = function() { + var ret = this.getByClass(CURRENT_FILE); + + return ret; + }; + + /** + * get current file by name + */ + this.getCurrentByName = function(name, panelParam) { + var element, + panel = panelParam || CurrentInfo.panel; + + name = 'js-file-' + name; + element = DOM.getByDataName(name, panel); + + return element; + }; + + /** + * unified way to get current file + * + * @currentFile + */ + this.getSelectedFiles = function() { + var panel = DOM.getPanel(), + selected = this.getByClassAll(SELECTED_FILE, panel), + ret = [].slice.call(selected); + + return ret; + }; + + + /* + * unselect all files + */ + this.unselectFiles = function(files) { + var array, + isArray = Array.isArray(files); + + if (!files) { + array = DOM.getSelectedFiles(); + } else if (!isArray) + array = [].slice.call(files); + + array.forEach(DOM.toggleSelectedFile); + }; + + /** + * get all selected files or current when none selected + * + * @currentFile + */ + this.getActiveFiles = function() { + var current = DOM.getCurrentFile(); + var files = DOM.getSelectedFiles(); + var name = DOM.getCurrentName(current); + + if (!files.length && name !== '..') + return [current]; + + return files; + }; + + /** + * get size + * @currentFile + */ + this.getCurrentSize = function(currentFile) { + var current = currentFile || Cmd.getCurrentFile(), + size = this.getByDataName('js-size', current); + + /* если это папка - возвращаем слово dir вместо размера*/ + size = size + .textContent + .replace(/^<|>$/g, ''); + + return size; + }; + + /** + * get size + * @currentFile + */ + this.loadCurrentSize = function(callback, currentFile) { + var RESTful = DOM.RESTful, + Images = DOM.Images, + current = currentFile || this.getCurrentFile(), + query = '?size', + link = this.getCurrentPath(current); + + Images.show.load(); + + if (name !== '..') + RESTful.read(link + query, function(error, size) { + if (!error) { + DOM.setCurrentSize(size, current); + Util.exec(callback, current); + Images.hide(); + } + }); + }; + + /** + * load hash + * @callback + * @currentFile + */ + this.loadCurrentHash = function(callback, currentFile) { + var RESTful = DOM.RESTful, + current = currentFile || DOM.getCurrentFile(), + query = '?hash', + link = DOM.getCurrentPath(current); + + RESTful.read(link + query, callback); + }; + + /** + * load current modification time of file + * @callback + * @currentFile + */ + this.loadCurrentTime = function(callback, currentFile) { + var RESTful = DOM.RESTful, + current = currentFile || this.getCurrentFile(), + query = '?time', + link = this.getCurrentPath(current); + + RESTful.read(link + query, callback); + }; + + /** + * set size + * @currentFile + */ + this.setCurrentSize = function(size, currentFile) { + var current = currentFile || this.getCurrentFile(), + sizeElement = this.getByDataName('js-size', current); + + sizeElement.textContent = size; + + }; + + /** + * @currentFile + */ + this.getCurrentMode = function(currentFile) { + var ret, + current = currentFile || this.getCurrentFile(), + lMode = this.getByDataName('js-mode', current); + ret = lMode.textContent; + + return ret; + }; + + /** + * @currentFile + */ + this.getCurrentOwner = function(currentFile) { + var ret, + current = currentFile || this.getCurrentFile(), + owner = this.getByDataName('js-owner', current); + + ret = owner.textContent; + + return ret; + }; + + /** + * unified way to get current file content + * + * @param callback + * @param currentFile + */ + this.getCurrentData = function(callback, currentFile) { + var hash, + RESTful = DOM.RESTful, + Dialog = DOM.Dialog, + Info = DOM.CurrentInfo, + current = currentFile || DOM.getCurrentFile(), + path = DOM.getCurrentPath(current), + isDir = DOM.isCurrentIsDir(current), + + func = function(error, data) { + var length, + ONE_MEGABYTE = 1024 * 1024 * 1024; + + if (!error) { + if (itype.object(data)) + data = Util.json.stringify(data); + + length = data.length; + + if (hash && length < ONE_MEGABYTE) + DOM.saveDataToStorage(path, data, hash); + } + + callback(error, data); + }; + + if (Info.name === '..') { + Dialog.alert.noFiles(TITLE); + return callback(Error('No files selected!')); + } + + if (isDir) + return RESTful.read(path, func); + + DOM.checkStorageHash(path, function(error, equal, hashNew) { + if (error) { + callback(error); + } else if (equal) { + DOM.getDataFromStorage(path, callback); + } else { + hash = hashNew; + RESTful.read(path, func); + } + }); + }; + + /** + * unified way to save current file content + * + * @callback - function({data, name}) {} + * @currentFile + */ + this.saveCurrentData = function(url, data, callback, query) { + if (!query) + query = ''; + + DOM.RESTful.write(url + query, data, function(error) { + !error && DOM.saveDataToStorage(url, data); + }); + }; + + /** + * unified way to get RefreshButton + */ + this.getRefreshButton = function(panel) { + var currentPanel = panel || this.getPanel(), + refresh = this.getByDataName('js-refresh', currentPanel); + + return refresh; + }; + + this.setCurrentByName = function(name) { + var current = this.getCurrentByName(name); + return this.setCurrentFile(current); + }; + + /** + * unified way to set current file + */ + this.setCurrentFile = function(currentFile, options) { + var path, pathWas, title, + o = options, + FS = CloudFunc.FS, + CENTER = true, + currentFileWas = this.getCurrentFile(); + + if (currentFile) { + if (currentFileWas) { + pathWas = DOM.getCurrentDirPath(); + unsetCurrentFile(currentFileWas); + } + + currentFile.classList.add(CURRENT_FILE); + + path = DOM.getCurrentDirPath(); + + if (path !== pathWas) { + title = CloudFunc.getTitle(path); + this.setTitle(title); + + /* history could be present + * but it should be false + * to prevent default behavior + */ + if (!o || o.history !== false) { + if (path !== '/') + path = FS + path; + + DOM.setHistory(path, null, path); + } + } + + /* scrolling to current file */ + this.scrollIntoViewIfNeeded(currentFile, CENTER); + + Cmd.updateCurrentInfo(); + } + + return this; + }; + + /* + * set current file by position + * + * @param layer - element + * @param - position {x, y} + */ + this.getCurrentByPosition = function(position) { + var element, tag, isChild, + x = position.x, + y = position.y; + + element = document.elementFromPoint(x, y), + tag = element.tagName; + + isChild = /A|SPAN|LI/.test(tag); + + if (!isChild) { + element = null; + } else { + switch (tag) { + case 'A': + element = element.parentElement.parentElement; + break; + + case 'SPAN': + element = element.parentElement; + break; + } + } + + if (element && element.tagName !== 'LI') + element = null; + + return element; + }; + + /** + * select current file + * @param currentFile + */ + this.selectFile = function(currentFile) { + var current = currentFile || DOM.getCurrentFile(); + + current.classList.add(SELECTED_FILE); + + return Cmd; + }; + + this.toggleSelectedFile = function(currentFile) { + var current = currentFile || this.getCurrentFile(); + + current.classList.toggle(SELECTED_FILE); + + return Cmd; + }; + + this.toggleAllSelectedFiles = function() { + DOM.getAllFiles().map(DOM.toggleSelectedFile); + + return Cmd; + }; + + this.selectAllFiles = function() { + DOM.getAllFiles().map(DOM.selectFile); + + return Cmd; + }; + + this.getAllFiles = function() { + var i, + panel = DOM.getPanel(), + files = DOM.getFiles(panel), + name = DOM.getCurrentName(files[0]); + + if (name === '..') + i = 1; + else + i = 0; + + return [].slice.call(files, i); + }; + + function selectByPattern(msg, files) { + var n, regExp, + Dialog = DOM.Dialog, + allMsg = 'Specify file type for ' + msg + ' selection', + i = 0, + matches = 0, + name, + shouldSel = msg === 'expand', + isSelected, isMatch, + current; + + Dialog.prompt(TITLE, allMsg, SelectType, {cancel: false}).then(function(type) { + SelectType = type; + + regExp = Util.getRegExp(type); + + n = files && files.length; + for (i = 0; i < n; i++) { + current = files[i]; + name = DOM.getCurrentName(current); + + if (name !== '..') { + isMatch = regExp.test(name); + + if (isMatch) { + ++matches; + + isSelected = DOM.isSelected(current); + + if (shouldSel) + isSelected = !isSelected; + + if (isSelected) + DOM.toggleSelectedFile(current); + } + } + } + + if (!matches) + Dialog.alert('Select Files', 'No matches found!'); + }); + } + + /** + * open dialog with expand selection + */ + this.expandSelection = function() { + var msg = 'expand', + files = CurrentInfo.files; + + selectByPattern(msg, files); + }; + + /** + * open dialog with shrink selection + */ + this.shrinkSelection = function() { + var msg = 'shrink', + files = DOM.getSelectedFiles(); + + selectByPattern(msg, files); + }; + + /** + * setting history wrapper + */ + this.setHistory = function(data, title, url) { + var ret = window.history; + + url = CloudCmd.PREFIX + url; + + if (ret) + history.pushState(data, title, url); + + return ret; + }; + + /** + * set title or create title element + * + * @param name + */ + + this.setTitle = function(name) { + if (!Title) + Title = DOM.getByTag('title')[0] || + DOM.load({ + name : 'title', + innerHTML : name, + parentElement : document.head + }); + + Title.textContent = name; + + return this; + }; + + /** + * current file check + * + * @param currentFile + */ + this.isCurrentFile = function(currentFile) { + var ret; + + if (currentFile) + ret = this.isContainClass(currentFile, CURRENT_FILE); + + return ret; + }; + + /** + * selected file check + * + * @param currentFile + */ + this.isSelected = function(pSelected) { + var ret; + + if (pSelected) + ret = this.isContainClass(pSelected, SELECTED_FILE); + + return ret; + }; + + /** + * check is current file is a directory + * + * @param currentFile + */ + this.isCurrentIsDir = function(currentFile) { + var current = currentFile || this.getCurrentFile(), + fileType = this.getByDataName('js-type', current), + ret = this.isContainClass(fileType, 'directory'); + + return ret; + }; + + /** + * get link from current (or param) file + * + * @param currentFile - current file by default + */ + this.getCurrentLink = function(currentFile) { + var current = currentFile || this.getCurrentFile(), + link = this.getByTag('a', current); + + return link[0]; + }; + + /** + * get link from current (or param) file + * + * @param currentFile - current file by default + */ + this.getCurrentPath = function(currentFile) { + var current = currentFile || DOM.getCurrentFile(), + element = DOM.getByTag('a', current)[0], + path = element.getAttribute('href'), + prefix = CloudCmd.PREFIX, + fs = CloudFunc.FS; + + path = path.replace(RegExp('^' + prefix + fs), ''); + + return path; + }; + + /** + * get name from current (or param) file + * + * @param currentFile + */ + this.getCurrentName = function(currentFile) { + var link, + name = '', + current = currentFile || this.getCurrentFile(); + + if (current) + link = this.getCurrentLink(current); + + if (link) { + name = link.title; + } + + return name; + }; + + this.getFilenames = function(allFiles) { + var first, name, + files, + slice = Array.prototype.slice, + names = []; + + if (!allFiles) + throw Error('AllFiles could not be empty'); + + files = slice.call(allFiles); + first = files[0]; + + if (first) { + name = this.getCurrentName(first); + } else { + first = this.getCurrentFile(); + name = this.getCurrentName(first); + } + + if (name === '..') + files.shift(); + + names = files.map(function(current) { + return DOM.getCurrentName(current); + }); + + return names; + }; + + /** + * set name from current (or param) file + * + * @param name + * @param current + */ + this.setCurrentName = function(name, current) { + var Info = CurrentInfo, + link = Info.link, + FS = CloudFunc.FS, + PREFIX = CloudCmd.PREFIX, + dir = PREFIX + FS + Info.dirPath; + + link.title = name; + link.innerHTML = CloudFunc.Entity.encode(name); + link.href = dir + name; + + current.setAttribute('data-name', 'js-file-' + name); + + return link; + }; + + /** + * check storage hash + */ + this.checkStorageHash = function(name, callback) { + var Storage = DOM.Storage; + var parallel = Util.exec.parallel; + var loadHash = DOM.loadCurrentHash; + var nameHash = name + '-hash'; + var getStoreHash = Util.exec.with(Storage.get, nameHash); + + if (typeof name !== 'string') + throw Error('name should be a string!'); + + if (typeof callback !== 'function') + throw Error('callback should be a function!'); + + parallel([loadHash, getStoreHash], function(error, loadHash, storeHash) { + var equal; + var isContain = /error/.test(loadHash); + + if (isContain) + error = loadHash; + else if (loadHash === storeHash) + equal = true; + + callback(error, equal, loadHash); + }); + }; + + /** + * save data to storage + * + * @param name + * @param data + * @param hash + * @param callback + */ + this.saveDataToStorage = function(name, data, hash, callback) { + var allowed = CloudCmd.config('localStorage'); + var isDir = DOM.isCurrentIsDir(); + var nameHash = name + '-hash'; + var nameData = name + '-data'; + + if (!allowed || isDir) + return Util.exec(callback); + + Util.exec.if(hash, function() { + var Storage = DOM.Storage; + + Storage.set(nameHash, hash); + Storage.set(nameData, data); + + Util.exec(callback, hash); + }, function(callback) { + DOM.loadCurrentHash(function(error, loadHash) { + hash = loadHash; + callback(); + }); + }); + }; + + /** + * save data to storage + * + * @param name + * @param data + * @param callback + */ + this.getDataFromStorage = function(name, callback) { + var Storage = DOM.Storage; + var nameHash = name + '-hash'; + var nameData = name + '-data'; + var allowed = CloudCmd.config('localStorage'); + var isDir = DOM.isCurrentIsDir(); + + if (!allowed || isDir) + return Util.exec(callback); + + Util.exec.parallel([ + exec.with(Storage.get, nameData), + exec.with(Storage.get, nameHash), + ], callback); + }; + + this.getFM = function() { + return this.getPanel().parentElement; + }; + + this.getPanelPosition = function(panel) { + panel = panel || DOM.getPanel(); + + return panel.dataset.name.replace('js-', ''); + }; + + /** function getting panel active, or passive + * @param options = {active: true} + */ + this.getPanel = function(options) { + var files, panel, isLeft, + dataName = 'js-', + current = this.getCurrentFile(); + + if (!current) { + panel = this.getByDataName('js-left'); + } else { + files = current.parentElement, + panel = files.parentElement, + isLeft = panel.getAttribute('data-name') === 'js-left'; + } + + /* if {active : false} getting passive panel */ + if (options && !options.active) { + dataName += isLeft ? 'right' : 'left'; + panel = this.getByDataName(dataName); + } + + /* if two panels showed + * then always work with passive + * panel + */ + if (window.innerWidth < CloudCmd.MIN_ONE_PANEL_WIDTH) + panel = this.getByDataName('js-left'); + + + if (!panel) + throw Error('can not find Active Panel!'); + + return panel; + }; + + this.getFiles = function(element) { + var files = DOM.getByDataName('js-files', element), + ret = files.children || []; + + return ret; + }; + + /** + * shows panel right or left (or active) + */ + this.showPanel = function(active) { + var ret = true, + panel = this.getPanel({active: active}); + + if (panel) + this.show(panel); + else + ret = false; + + return ret; + }; + + /** + * hides panel right or left (or active) + */ + this.hidePanel = function(active) { + var ret = false, + panel = this.getPanel({active: active}); + + if (panel) + ret = this.hide(panel); + + return ret; + }; + + /** + * open window with URL + * @param url + */ + this.openWindow = function(url) { + var left = 140, + top = 187, + width = 1000, + height = 650, + + options = 'left=' + left + + ',top=' + top + + ',width=' + width + + ',height=' + height + + ',personalbar=0,toolbar=0' + + ',scrollbars=1,resizable=1', + + wnd = window.open(url, 'Cloud Commander Auth', options); + + if (!wnd) + DOM.Dialog.alert(TITLE, 'Please disable your popup blocker and try again.'); + + return wnd; + }; + + /** + * remove child of element + * @param pChild + * @param element + */ + this.remove = function(child, element) { + var parent = element || document.body; + + parent.removeChild(child); + + return DOM; + }; + + /** + * remove current file from file table + * @param current + * + */ + this.deleteCurrent = function(current) { + var next, prev, currentNew; + + if (!current) + Cmd.getCurrentFile(); + + var parent = current && current.parentElement; + var name = Cmd.getCurrentName(current); + + if (current && name !== '..') { + next = current.nextSibling, + prev = current.previousSibling; + + if (next) + currentNew = next; + else if (prev) + currentNew = prev; + + DOM.setCurrentFile(currentNew); + + parent.removeChild(current); + } + + return currentNew; + }; + + /** + * remove selected files from file table + * @Selected + */ + this.deleteSelected = function(selected) { + var i, n, current; + + if (!selected) + selected = this.getSelectedFiles(); + + if (selected) { + n = selected.length; + + for (i = 0; i < n; i++) { + current = selected[i]; + DOM.deleteCurrent(current); + } + } + + return selected; + }; + + /** + * rename current file + * + * @currentFile + */ + this.renameCurrent = function(current) { + var from, dirPath, files, isExist, + RESTful = DOM.RESTful, + Dialog = DOM.Dialog; + + if (!Cmd.isCurrentFile(current)) + current = Cmd.getCurrentFile(); + + from = Cmd.getCurrentName(current); + + if (from === '..') + Dialog.alert.noFiles(TITLE); + else + Dialog.prompt(TITLE, 'Rename', from, {cancel: false}).then(function(to) { + isExist = !!DOM.getCurrentByName(to); + dirPath = Cmd.getCurrentDirPath(); + + if (from !== to) { + files = { + from : dirPath + from, + to : dirPath + to + }; + + RESTful.mv(files, function(error) { + var Storage = DOM.Storage; + + if (!error) { + DOM.setCurrentName(to, current); + Cmd.updateCurrentInfo(); + Storage.remove(dirPath); + + if (isExist) + CloudCmd.refresh(); + } + }); + } + }); + }; + + /** + * unified way to scrollIntoViewIfNeeded + * (native suporte by webkit only) + * @param element + * @param pCenter + */ + this.scrollIntoViewIfNeeded = function(element, center) { + var ret = element && element.scrollIntoViewIfNeeded; + + /* for scroll as small as possible + * param should be false + */ + if (arguments.length === 1) + center = false; + + if (ret) + element.scrollIntoViewIfNeeded(center); + + return ret; + }; + + /* scroll on one page*/ + this.scrollByPages = function(element, pPages) { + var ret = element && element.scrollByPages && pPages; + + if (ret) + element.scrollByPages(pPages); + + return ret; + }; + + this.changePanel = function() { + var dataName, files, current, + + panel = DOM.getPanel(), + panelPassive = DOM.getPanel({ + active: false + }), + + name = DOM.getCurrentName(), + + filesPassive = DOM.getFiles(panelPassive); + + dataName = panel.getAttribute('data-name'); + TabPanel[dataName] = name; + + panel = panelPassive; + dataName = panel.getAttribute('data-name'); + + name = TabPanel[dataName]; + + if (name) { + current = DOM.getCurrentByName(name, panel); + + if (current) + files = current.parentElement; + } + + if (!files || !files.parentElement) { + current = DOM.getCurrentByName(name, panel); + + if (!current) + current = filesPassive[0]; + } + + DOM.setCurrentFile(current, { + history: true + }); + + return this; + }; + + this.getPackerExt = function(type) { + if (type === 'zip') + return '.zip'; + + return '.tar.gz'; + }; + + this.goToDirectory = function() { + var msg = 'Go to directory:', + path = CurrentInfo.dirPath, + Dialog = DOM.Dialog; + + Dialog.prompt(TITLE, msg, path, {cancel: false}).then(function(path) { + CloudCmd.loadDir({ + path: path + }); + }); + }, + + this.duplicatePanel = function() { + var isDir = CurrentInfo.isDir; + var path = CurrentInfo.dirPath; + var panel = CurrentInfo.panelPassive; + var noCurrent = !CurrentInfo.isOnePanel; + + if (isDir) + path = CurrentInfo.path; + + CloudCmd.loadDir({ + path: path, + panel: panel, + noCurrent: noCurrent, + }); + }; + + this.swapPanels = function() { + var Info = CurrentInfo, + panel = Info.panel, + panelPassive = Info.panelPassive, + + currentIndex = [].indexOf.call(Info.files, Info.element), + + dirPath = DOM.getCurrentDirPath(), + dirPathPassive = DOM.getNotCurrentDirPath(); + + CloudCmd.loadDir({ + path: dirPath, + panel: panelPassive, + noCurrent: true + }); + + CloudCmd.loadDir({ + path: dirPathPassive, + panel: panel + }, function() { + var el, + files = Info.files, + length = files.length - 1; + + if (currentIndex > length) + currentIndex = length; + + el = files[currentIndex]; + + DOM.setCurrentFile(el); + }); + }; + + this.CurrentInfo = CurrentInfo, + + this.updateCurrentInfo = function(currentFile) { + var info = Cmd.CurrentInfo, + current = currentFile || Cmd.getCurrentFile(), + files = current.parentElement, + panel = files.parentElement, + + panelPassive = Cmd.getPanel({ + active: false + }), + + filesPassive = DOM.getFiles(panelPassive), + + name = Cmd.getCurrentName(current); + + info.dir = Cmd.getCurrentDirName(); + info.dirPath = Cmd.getCurrentDirPath(); + info.parentDirPath = Cmd.getParentDirPath(); + info.element = current; + info.ext = Util.getExt(name); + info.files = [].slice.call(files.children), + info.filesPassive = [].slice.call(filesPassive), + info.first = files.firstChild; + info.getData = Cmd.getCurrentData; + info.last = files.lastChild; + info.link = Cmd.getCurrentLink(current); + info.mode = Cmd.getCurrentMode(current); + info.name = name; + info.path = Cmd.getCurrentPath(current); + info.panel = panel; + info.panelPassive = panelPassive; + info.size = Cmd.getCurrentSize(current); + info.isDir = Cmd.isCurrentIsDir(); + info.isSelected = Cmd.isSelected(current); + info.panelPosition = Cmd.getPanel().dataset.name.replace('js-', ''); + info.isOnePanel = + info.panel.getAttribute('data-name') === + info.panelPassive.getAttribute('data-name'); + }; + } })(Util, window); +