diff --git a/client.js b/client.js index 0045c2e6..91fac3d8 100644 --- a/client.js +++ b/client.js @@ -1,921 +1,942 @@ -/* Функция которая возвратит обьект CloudCommander - * @window - обьект window - * @document - обьект document - * @CloudFunc - обьект содержащий общий функционал - * клиентский и серверный - */ - -var CloudCommander=(function(){ -"use strict"; - -/* Клиентский обьект, содержащий функциональную часть*/ -var CloudClient={ - /* Конструктор CloudClient, который - * выполняет весь функционал по - * инициализации - */ - init :function(){}, - - keyBinding :function(){},/* функция нажатий обработки клавишь */ - Editor :function(){},/* function loads and shows editor */ - Viewer :function(){},/* function loads and shows viewer */ - Terminal :function(){},/* function loads and shows terminal */ - keyBinded :false,/* оброботка нажатий клавишь установлена*/ - _loadDir :function(){}, - /* - * Функция привязываеться ко всем ссылкам и - * загружает содержимое каталогов - */ - /* Обьект для работы с кэшем */ - Cashe :{}, - - /* ПРИВАТНЫЕ ФУНКЦИИ */ - /* функция загружает json-данные о файловой системе */ - _ajaxLoad :function(){}, - /* Функция генерирует JSON из html-таблицы файлов */ - _getJSONfromFileTable :function(){}, - /* функция меняет ссыки на ajax-овые */ - _changeLinks :function(){}, - /* ОБЬЕКТЫ */ - /* обьект, который содержит функции для отображения картинок*/ - _images :{}, - /* КОНСТАНТЫ*/ - /* название css-класа текущего файла*/ - CURRENT_FILE :'current-file', - LIBDIR :'/lib/', - LIBDIRCLIENT :'/lib/client/', - /* height of Cloud Commander - * seting up in init() - */ - HEIGHT :0 -}; - -/* short names used all the time functions */ -var getByClass = function(pClass){ - return document.getElementsByClassName(pClass); -}; - -var getById = function(pId){ - return document.getElementById(pId); -}; - -/* - * Обьект для работы с кэшем - * в него будут включены функции для - * работы с LocalStorage, webdb, - * indexed db etc. - */ -CloudClient.Cache={ - _allowed :true, /* приватный переключатель возможности работы с кэшем */ - /* функция проверяет возможно ли работать с кэшем каким-либо образом */ - isAllowed :function(){}, - /* Тип кэша, который доступен*/ - type :{}, - /* Функция устанавливает кэш, если выбранный вид поддерживаеться браузером*/ - set :function(){}, - /* Функция достаёт кэш, если выбранный вид поддерживаеться браузером*/ - get :function(){}, - /* функция чистит весь кэш для всех каталогов*/ - clear :function(){} -}; - -/* Обьект, который содержит - * функции для отображения - * картинок - */ -CloudClient._images={ - /* Функция создаёт картинку загрузки*/ - loading :function(){ - var e=document.createElement('span'); - e.className='icon loading'; - e.id='loading-image'; - return e; -}, - - /* Функция создаёт картинку ошибки загрузки*/ - error :function(){ - var e=document.createElement('span'); - e.className='icon error'; - e.id='error-image'; - return e; - } -}; - -/* функция проверяет поддерживаеться ли localStorage */ -CloudClient.Cache.isAllowed=(function(){ - if(window.localStorage && - localStorage.setItem && - localStorage.getItem){ - CloudClient.Cache._allowed=true; - }else - { - CloudClient.Cache._allowed=false; - /* загружаем PolyFill для localStorage, - * если он не поддерживаеться браузером - * https://gist.github.com/350433 - */ - /* - CloudClient.jsload('https://raw.github.com/gist/350433/c9d3834ace63e5f5d7c8e1f6e3e2874d477cb9c1/gistfile1.js', - function(){CloudClient.Cache._allowed=true; - }); - */ - } -}); - /* если доступен localStorage и - * в нём есть нужная нам директория - - * записываем данные в него - */ -CloudClient.Cache.set=(function(pName, pData){ - if(CloudClient.Cache._allowed && pName && pData){ - localStorage.setItem(pName,pData); - } -}); -/* Если доступен Cache принимаем из него данные*/ -CloudClient.Cache.get=(function(pName){ - if(CloudClient.Cache._allowed && pName){ - return localStorage.getItem(pName); - } - else return null; -}); -/* Функция очищает кэш*/ -CloudClient.Cache.clear=(function(){ - if(CloudClient.Cache._allowed){ - localStorage.clear(); - } -}); - - -/* функция обработки нажатий клавишь */ -CloudClient.keyBinding=(function(){ - /* loading keyBinding module and start it */ - CloudClient.jsload(CloudClient.LIBDIRCLIENT+'keyBinding.js',function(){ - CloudCommander.keyBinding(); - }); -}); - -/* function loads and shows editor */ -CloudClient.Editor = (function(){ - /* loading CloudMirror plagin */ - CloudClient.jsload(CloudClient.LIBDIRCLIENT + - 'editor.js',{ - onload:(function(){ - CloudCommander.Editor.Keys(); - }) - }); -}); - -/* function loads and shows viewer */ -CloudClient.Viewer = (function(){ - CloudCommander.jsload(CloudClient.LIBDIRCLIENT + - 'viewer.js',{ - onload: (function(){ - CloudCommander.Viewer.Keys(); - }) - }); -}); - -/* function loads and shows terminal */ -CloudClient.Terminal = (function(){ - CloudCommander.jsload(CloudClient.LIBDIRCLIENT + - 'terminal.js',{ - onload: (function(){ - CloudCommander.Terminal.Keys(); - }) - }); -}); - -/* - * Функция привязываеться ко всем ссылкам и - * загружает содержимое каталогов - */ -CloudClient._loadDir=(function(pLink,pNeedRefresh){ - /* @pElem - элемент, - * для которого нужно - * выполнить загрузку - */ - return function(){ - /* показываем гиф загрузки возле пути папки сверху*/ - LoadingImage.className='icon loading';/* показываем загрузку*/ - ErrorImage.className='icon error hidden';/* прячем ошибку */ - /* если элемент задан - - * работаем с ним - */ - /* если мы попали сюда с таблицы файлов*/ - try{ - this.firstChild.nextSibling.appendChild(LoadingImage); - }catch(error){ - /* если + - * кнопка обновления - */ - try{this.firstChild.parentElement.appendChild(LoadingImage);} - catch(error){console.log(error);} - } - - var lCurrentFile=getByClass(CloudClient.CURRENT_FILE); - /* получаем имя каталога в котором находимся*/ - var lHref; - try{ - lHref=lCurrentFile[0].parentElement.getElementsByClassName('path')[0].textContent; - }catch(error){console.log('error');} - - lHref=CloudFunc.removeLastSlash(lHref); - var lSubstr=lHref.substr(lHref,lHref.lastIndexOf('/')); - lHref=lHref.replace(lSubstr+'/',''); - - /* загружаем содержимое каталога*/ - CloudClient._ajaxLoad(pLink, pNeedRefresh); - - /* получаем все элементы выделенной папки*/ - /* при этом, если мы нажали обновить - * или +R - ссылок мы ненайдём - * и заходить не будем - */ - var lA=this.getElementsByTagName('a'); - /* если нажали на ссылку на верхний каталог*/ - if(lA.length>0 && lA[0].textContent==='..' && - lHref!=='/'){ - /* функция устанавливает курсор на каталог - * с которого мы пришли, если мы поднялись - * в верх по файловой структуре - */ - CloudClient._currentToParent(lHref); - } - - /* что бы не переходить по ссылкам - * а грузить всё ajax'ом, - * возвращаем false на событие - * onclick - */ - return false; - }; - }); - - -/* - * Function edits file name - * - * @pParent - parent element - * @pEvent - */ -CloudClient._editFileName = (function(pParent){ -var lA = pParent.getElementsByTagName('a'); - if (lA.length && lA.textContent !== '..'){ - - lA[0].contentEditable = true; - CloudCommander.keyBinded = false; - - var lDocumentOnclick = document.onclick; - - /* setting event handler onclick - * if user clicks somewhere keyBinded - * backs - */ - document.onclick = (function(){ - var lA = pParent.getElementsByTagName('a'); - if (lA.length && lA.textContent !== '..') - lA[0].contentEditable = false; - - CloudCommander.keyBinded = true; - - /* backs old document.onclick - * and call it if it was - * setted up earlier - */ - document.onclick = lDocumentOnclick; - if(typeof lDocumentOnclick === 'function') - lDocumentOnclick(); - - }); - } -}); - -/* Функция устанавливает текущим файлом, тот - * на который кликнули единожды - */ -CloudClient._setCurrent=(function(){ - /* - * @pFromEnter - если мы сюда попали - * из события нажатия на энтер - - * вызоветься _loadDir - */ - return function(pFromEnter){ - var lCurrentFile=getByClass(CloudClient.CURRENT_FILE); - if(lCurrentFile && lCurrentFile.length > 0){ - /* если мы находимся не на - * пути и не на заголовках - */ - if(this.className!=='path' && - this.className!=='fm_header'){ - - if (this.className === CloudClient.CURRENT_FILE && - typeof pFromEnter !== 'boolean'){ - var lParent = this; - - setTimeout(function(){ - /* waiting a few seconds - * and if classes still equal - * make file name editable - * in other case - * double click event happend - */ - if(lParent.className === CloudClient.CURRENT_FILE) - CloudClient._editFileName(lParent); - },400); - } - else{ - lCurrentFile[0].className=''; - /* устанавливаем курсор на файл, - * на который нажали */ - this.className = CloudClient.CURRENT_FILE; - } - } - } - /* если мы попали сюда с энтера*/ - if(pFromEnter===true){ - this.ondblclick(this); - }/* если мы попали сюда от клика мышки */ - else{pFromEnter.returnValue=false;} - - /* что бы не переходить по ссылкам - * а грузить всё ajax'ом, - * возвращаем false на событие - * onclick - */ - return false; - }; - }); - -/* функция устанавливает курсор на каталог - * с которого мы пришли, если мы поднялись - * в верх по файловой структуре - * @pDirName - имя каталога с которого мы пришли - */ -CloudClient._currentToParent = (function(pDirName){ - /* опредиляем в какой мы панели: - * правой или левой - */ - var lCurrentFile = getByClass(CloudClient.CURRENT_FILE); - var lPanel = lCurrentFile[0].parentElement; - - /* убираем слэш с имени каталога*/ - pDirName=pDirName.replace('/',''); - - var lRootDir = getById(pDirName + '(' + lPanel.id + ')'); - - /* if found li element with ID directory name - * set it to current file - */ - lRootDir && - !(lCurrentFile[0].className = '') && - (lRootDir.className = CloudClient.CURRENT_FILE); -}); - -/* глобальные переменные */ -var LoadingImage; -var ErrorImage; - -var CloudFunc, $; -/* Конструктор CloudClient, который - * выполняет весь функционал по - * инициализации - */ -CloudClient.init=(function() -{ - /* меняем title - * если js включен - имена папок отображать необязательно... - * а может и обязательно при переходе, можно будет это сделать - */ - var lTitle=document.getElementsByTagName('title'); - if(lTitle.length>0)lTitle[0].textContent='Cloud Commander'; - - /* загружаем jquery: */ - CloudClient.jsload('//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js',{ - onload: function(){ - $ = window.jQuery; - }, - - onerror: function(){ - CloudClient.jsload('lib/client/jquery.js'); - - /* - * if could not load jquery from google server - * maybe we offline, load font from local - * directory - */ - CloudClient.cssSet({id:'local-droids-font', - element : document.head, - inner : '@font-face {font-family: "Droid Sans Mono";' + - 'font-style: normal;font-weight: normal;' + - 'src: local("Droid Sans Mono"), local("DroidSansMono"),'+ - ' url("font/DroidSansMono.woff") format("woff");}' - }); - } - }); - - /* загружаем общие функции для клиента и сервера*/ - CloudClient.jsload(CloudClient.LIBDIR+'cloudfunc.js',function(){ - /* берём из обьекта window общий с сервером функционал */ - CloudFunc=window.CloudFunc; - - /* меняем ссылки на ajax'овые*/ - CloudClient._changeLinks(CloudFunc.LEFTPANEL); - CloudClient._changeLinks(CloudFunc.RIGHTPANEL); - - /* устанавливаем переменную доступности кэша*/ - CloudClient.Cache.isAllowed(); - /* Устанавливаем кэш корневого каталога */ - if(!CloudClient.Cache.get('/'))CloudClient.Cache.set('/',CloudClient._getJSONfromFileTable()); - } - ); - - LoadingImage=CloudClient._images.loading(); - /* загружаем иконку загрузки возле кнопки обновления дерева каталогов*/ - try{ - getByClass('path')[0] - .getElementsByTagName('a')[0] - .appendChild(LoadingImage); - - LoadingImage.className+=' hidden'; /* прячем её */ - }catch(error){console.log(error);} - ErrorImage=CloudClient._images.error(); - - /* устанавливаем размер высоты таблицы файлов - * исходя из размеров разрешения экрана - */ - - /* выделяем строку с первым файлом */ - var lFmHeader=getByClass('fm_header'); - if(lFmHeader && lFmHeader[0].nextSibling) - lFmHeader[0].nextSibling.className=CloudClient.CURRENT_FILE; - - /* показываем элементы, которые будут работать только, если есть js */ - var lFM = getById('fm'); - if(lFM) - lFM.className='localstorage'; - - /* если есть js - показываем правую панель*/ - var lRight=getById('right'); - if(lRight)lRight.className=lRight.className.replace('hidden',''); - - /* формируем и округляем высоту экрана - * при разрешениии 1024x1280: - * 658 -> 700 - */ - - var lHeight = - window.screen.height - - (window.screen.height/3).toFixed(); - - lHeight=(lHeight/100).toFixed()*100; - - CloudClient.HEIGHT = lHeight; - - CloudClient.cssSet({id:'show_2panels', - element:document.head, - inner:'#left{width:46%;}' + - '.panel{height:' + lHeight +'px' - }); -}); - -/* функция меняет ссыки на ajax-овые */ -CloudClient._changeLinks = function(pPanelID) -{ - /* назначаем кнопку очистить кэш и показываем её*/ - var lClearcache=getById('clear-cache'); - if(lClearcache)lClearcache.onclick=CloudClient.Cache.clear; - - /* меняем ссылки на ajax-запросы */ - var lPanel=getById(pPanelID); - var a=lPanel.getElementsByTagName('a'); - - /* Если нажмут на кнопку перезагрузить страниц - её нужно будет обязательно - * перезагрузить - */ - /* номер ссылки очистки кэша*/ - /* номер ссылки иконки обновления страницы */ - var lREFRESHICON=0; - - /* путь в ссылке, который говорит - * что js отключен - */ - var lNoJS_s = CloudFunc.NOJS; - var lFS_s = CloudFunc.FS; - - for(var i=0;i+ + * кнопка обновления + */ + try{this.firstChild.parentElement.appendChild(LoadingImage);} + catch(error){console.log(error);} + } + + var lCurrentFile=getByClass(CloudClient.CURRENT_FILE); + /* получаем имя каталога в котором находимся*/ + var lHref; + try{ + lHref=lCurrentFile[0].parentElement.getElementsByClassName('path')[0].textContent; + }catch(error){console.log('error');} + + lHref=CloudFunc.removeLastSlash(lHref); + var lSubstr=lHref.substr(lHref,lHref.lastIndexOf('/')); + lHref=lHref.replace(lSubstr+'/',''); + + /* загружаем содержимое каталога*/ + CloudClient._ajaxLoad(pLink, pNeedRefresh); + + /* получаем все элементы выделенной папки*/ + /* при этом, если мы нажали обновить + * или +R - ссылок мы ненайдём + * и заходить не будем + */ + var lA=this.getElementsByTagName('a'); + /* если нажали на ссылку на верхний каталог*/ + if(lA.length>0 && lA[0].textContent==='..' && + lHref!=='/'){ + /* функция устанавливает курсор на каталог + * с которого мы пришли, если мы поднялись + * в верх по файловой структуре + */ + CloudClient._currentToParent(lHref); + } + + /* что бы не переходить по ссылкам + * а грузить всё ajax'ом, + * возвращаем false на событие + * onclick + */ + return false; + }; + }); + + +/* + * Function edits file name + * + * @pParent - parent element + * @pEvent + */ +CloudClient._editFileName = (function(pParent){ +var lA = pParent.getElementsByTagName('a'); + if (lA.length && lA.textContent !== '..'){ + + lA[0].contentEditable = true; + CloudCommander.keyBinded = false; + + var lDocumentOnclick = document.onclick; + + /* setting event handler onclick + * if user clicks somewhere keyBinded + * backs + */ + document.onclick = (function(){ + var lA = pParent.getElementsByTagName('a'); + if (lA.length && lA.textContent !== '..') + lA[0].contentEditable = false; + + CloudCommander.keyBinded = true; + + /* backs old document.onclick + * and call it if it was + * setted up earlier + */ + document.onclick = lDocumentOnclick; + if(typeof lDocumentOnclick === 'function') + lDocumentOnclick(); + + }); + } +}); + +/* Функция устанавливает текущим файлом, тот + * на который кликнули единожды + */ +CloudClient._setCurrent=(function(){ + /* + * @pFromEnter - если мы сюда попали + * из события нажатия на энтер - + * вызоветься _loadDir + */ + return function(pFromEnter){ + var lCurrentFile=getByClass(CloudClient.CURRENT_FILE); + if(lCurrentFile && lCurrentFile.length > 0){ + /* если мы находимся не на + * пути и не на заголовках + */ + if(this.className!=='path' && + this.className!=='fm_header'){ + + if (this.className === CloudClient.CURRENT_FILE && + typeof pFromEnter !== 'boolean'){ + var lParent = this; + + setTimeout(function(){ + /* waiting a few seconds + * and if classes still equal + * make file name editable + * in other case + * double click event happend + */ + if(lParent.className === CloudClient.CURRENT_FILE) + CloudClient._editFileName(lParent); + },400); + } + else{ + lCurrentFile[0].className=''; + /* устанавливаем курсор на файл, + * на который нажали */ + this.className = CloudClient.CURRENT_FILE; + } + } + } + /* если мы попали сюда с энтера*/ + if(pFromEnter===true){ + this.ondblclick(this); + }/* если мы попали сюда от клика мышки */ + else{pFromEnter.returnValue=false;} + + /* что бы не переходить по ссылкам + * а грузить всё ajax'ом, + * возвращаем false на событие + * onclick + */ + return false; + }; + }); + +/* функция устанавливает курсор на каталог + * с которого мы пришли, если мы поднялись + * в верх по файловой структуре + * @pDirName - имя каталога с которого мы пришли + */ +CloudClient._currentToParent = (function(pDirName){ + /* опредиляем в какой мы панели: + * правой или левой + */ + var lCurrentFile = getByClass(CloudClient.CURRENT_FILE); + var lPanel = lCurrentFile[0].parentElement; + + /* убираем слэш с имени каталога*/ + pDirName=pDirName.replace('/',''); + + var lRootDir = getById(pDirName + '(' + lPanel.id + ')'); + + /* if found li element with ID directory name + * set it to current file + */ + lRootDir && + !(lCurrentFile[0].className = '') && + (lRootDir.className = CloudClient.CURRENT_FILE); +}); + +/* глобальные переменные */ +var LoadingImage; +var ErrorImage; + +var CloudFunc, $, Util; +/* Конструктор CloudClient, который + * выполняет весь функционал по + * инициализации + */ +CloudClient.init=(function() +{ + Util = new CloudClient.Util(); + + /* меняем title + * если js включен - имена папок отображать необязательно... + * а может и обязательно при переходе, можно будет это сделать + */ + var lTitle=document.getElementsByTagName('title'); + if(lTitle.length>0)lTitle[0].textContent='Cloud Commander'; + + /* загружаем jquery: */ + Util.jsload('//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js',{ + onload: function(){ + $ = window.jQuery; + }, + + onerror: function(){ + Util.jsload('lib/client/jquery.js'); + + /* + * if could not load jquery from google server + * maybe we offline, load font from local + * directory + */ + Util.cssSet({id:'local-droids-font', + element : document.head, + inner : '@font-face {font-family: "Droid Sans Mono";' + + 'font-style: normal;font-weight: normal;' + + 'src: local("Droid Sans Mono"), local("DroidSansMono"),'+ + ' url("font/DroidSansMono.woff") format("woff");}' + }); + } + }); + + /* загружаем общие функции для клиента и сервера*/ + Util.jsload(CloudClient.LIBDIR+'cloudfunc.js',function(){ + /* берём из обьекта window общий с сервером функционал */ + CloudFunc=window.CloudFunc; + + /* меняем ссылки на ajax'овые*/ + CloudClient._changeLinks(CloudFunc.LEFTPANEL); + CloudClient._changeLinks(CloudFunc.RIGHTPANEL); + + /* устанавливаем переменную доступности кэша*/ + CloudClient.Cache.isAllowed(); + /* Устанавливаем кэш корневого каталога */ + if(!CloudClient.Cache.get('/'))CloudClient.Cache.set('/',CloudClient._getJSONfromFileTable()); + } + ); + + LoadingImage=CloudClient._images.loading(); + /* загружаем иконку загрузки возле кнопки обновления дерева каталогов*/ + try{ + getByClass('path')[0] + .getElementsByTagName('a')[0] + .appendChild(LoadingImage); + + LoadingImage.className+=' hidden'; /* прячем её */ + }catch(error){console.log(error);} + ErrorImage=CloudClient._images.error(); + + /* устанавливаем размер высоты таблицы файлов + * исходя из размеров разрешения экрана + */ + + /* выделяем строку с первым файлом */ + var lFmHeader=getByClass('fm_header'); + if(lFmHeader && lFmHeader[0].nextSibling) + lFmHeader[0].nextSibling.className=CloudClient.CURRENT_FILE; + + /* показываем элементы, которые будут работать только, если есть js */ + var lFM = getById('fm'); + if(lFM) + lFM.className='localstorage'; + + /* если есть js - показываем правую панель*/ + var lRight=getById('right'); + if(lRight)lRight.className=lRight.className.replace('hidden',''); + + /* формируем и округляем высоту экрана + * при разрешениии 1024x1280: + * 658 -> 700 + */ + + var lHeight = + window.screen.height - + (window.screen.height/3).toFixed(); + + lHeight=(lHeight/100).toFixed()*100; + + CloudClient.HEIGHT = lHeight; + + Util.cssSet({id:'show_2panels', + element:document.head, + inner:'#left{width:46%;}' + + '.panel{height:' + lHeight +'px' + }); +}); + +/* функция меняет ссыки на ajax-овые */ +CloudClient._changeLinks = function(pPanelID) +{ + /* назначаем кнопку очистить кэш и показываем её*/ + var lClearcache=getById('clear-cache'); + if(lClearcache)lClearcache.onclick=CloudClient.Cache.clear; + + /* меняем ссылки на ajax-запросы */ + var lPanel=getById(pPanelID); + var a=lPanel.getElementsByTagName('a'); + + /* Если нажмут на кнопку перезагрузить страниц - её нужно будет обязательно + * перезагрузить + */ + /* номер ссылки очистки кэша*/ + /* номер ссылки иконки обновления страницы */ + var lREFRESHICON=0; + + /* путь в ссылке, который говорит + * что js отключен + */ + var lNoJS_s = CloudFunc.NOJS; + var lFS_s = CloudFunc.FS; + + for(var i=0;i