cloud commander

This commit is contained in:
coderaiser 2012-06-07 13:33:42 +00:00
parent 177f54b654
commit df399b34de
8 changed files with 2625 additions and 0 deletions

2
README.md Normal file
View file

@ -0,0 +1,2 @@
Cloud-Commander
===============

978
client.js Normal file
View file

@ -0,0 +1,978 @@
/* Функция которая возвратит обьект CloudCommander
* @window - обьект window
* @document - обьект document
* @CloudFunc - обьект содержащий общий функционал
* клиентский и серверный
*/
//var CloudCommander=(function(window,document){
var document,window;
var CloudCommander=(function(){
"use strict";
/* если функции console.log нет - создаём заглушку */
var console;
if(!window)window={console:{log:function(pParam){return pParam;}}};
else if(window && !window.console){
console={
'log':function(param){
return param;
}
};
}else console=window.console;
/*
window.jQuery || document.write('<script src="jquery.min.js"><\/script>');
*/
/* Клиентский обьект, содержащий функциональную часть*/
var CloudClient={
/* Конструктор CloudClient, который
* выполняет весь функционал по
* инициализации
*/
init :function(){},
keyBinding :function(){},/* функция нажатий обработки клавишь */
keyBinded :false,/* оброботка нажатий клавишь установлена*/
_loadDir :function(){},
/*
* Функция привязываеться ко всем ссылкам и
* загружает содержимое каталогов
*/
/* Обьект для работы с кэшем */
Cashe :{},
/* ПРИВАТНЫЕ ФУНКЦИИ */
/* функция загружает json-данные о файловой системе */
_ajaxLoad :function(){},
/* Функция генерирует JSON из html-таблицы файлов */
_getJSONfromFileTable :function(){},
/* функция меняет ссыки на ajax-овые */
_changeLinks :function(){},
/* ОБЬЕКТЫ */
/* обьект, который содержит функции для отображения картинок*/
_images :{},
/* КОНСТАНТЫ*/
/* название css-класа текущего файла*/
CURRENT_FILE :'current-file'
};
/*
* Обьект для работы с кэшем
* в него будут включены функции для
* работы с LocalStorage, webdb,
* idexed 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(){
var key_event=function(event){
/*
* Делаем допущение что перезагружать Cloud Commander
* посетителям не придёться, в любом случае, параметр
* должен быть опциональным и должна быть возможность
* его отключить. В любом случае, кроме хакеров и
* разработчиков (при чём сомнительно, что хакерам
* это пригодиться), функция перезагрузки никому не
* нужна, поскольку загружать весь дополнительный
* контент снова (js,css) в готовой версии нет
* необходимости.
*
*/
//console.log(event.keyCode);
var lCurrentFile;
/* если клавиши можно обрабатывать*/
if(CloudClient.keyBinded){
/* если нажали таб:
* переносим курсор на
* правую панель, если
* мы были на левой и
* наоборот
*/
if(event.keyCode===9){
console.log('Tab pressed');
try{
lCurrentFile=document.getElementsByClassName(CloudClient.CURRENT_FILE)[0];
}catch(error){console.log(error);}
}
/* навигация по таблице файлов*/
/* если нажали клавишу вверх*/
else if(event.keyCode===38){
/* получаем выдленный файл*/
lCurrentFile=document.getElementsByClassName(CloudClient.CURRENT_FILE);
/* если ненайдены выделенные файлы - выходим*/
if(lCurrentFile.length===0)return;
lCurrentFile=lCurrentFile[0];
/* если это строка существет и
* если она не заголовок
* файловой таблицы
*/
if(lCurrentFile.previousSibling &&
lCurrentFile.previousSibling.className!=='fm_header' ){
/* убираем выделение с текущего элемента */
lCurrentFile.className='';
/* и выделяем предыдущую строку*/
lCurrentFile.previousSibling.className=CloudClient.CURRENT_FILE;
event.preventDefault();
}
}
/* если нажали клавишу в низ*/
else if(event.keyCode===40){
/* получаем выдленный файл*/
lCurrentFile=document.getElementsByClassName(CloudClient.CURRENT_FILE);
/* если ненайдены выделенные файлы - выходим*/
if(lCurrentFile.length===0)return;
lCurrentFile=lCurrentFile[0];
/* если это не последняя строка */
if(lCurrentFile.nextSibling){
/* убираем с него выделение */
lCurrentFile.className='';
/* выделяем следующую строку*/
lCurrentFile.nextSibling.className=CloudClient.CURRENT_FILE;
event.preventDefault();
}
}
/* если нажали клавишу page up или Home
* переходим к самому верхнему
* элементу
*/
else if(/*event.keyCode===33 ||*/ event.keyCode===36){
lCurrentFile=document.getElementsByClassName(CloudClient.CURRENT_FILE)[0];
/* убираем выделение с текущего файла*/
lCurrentFile.className='';
/* получаем первый элемент*/
lCurrentFile.parentElement.firstElementChild
/* пропускаем путь и заголовки столбиков*/
.nextElementSibling.nextElementSibling
/* выделяем верхий файл */
.className=CloudClient.CURRENT_FILE;
}
/* если нажали клавишу page down или End
* выделяем последний элемент
*/
else if(/*event.keyCode===34 ||*/ event.keyCode===35){
lCurrentFile=document.getElementsByClassName(CloudClient.CURRENT_FILE)[0];
/* снимаем выделение с текущего файла*/
lCurrentFile.className='';
/* выделяем самый нижний файл */
lCurrentFile.parentElement.lastElementChild.className=CloudClient.CURRENT_FILE;
}
/* если нажали Enter - открываем папку*/
else if(event.keyCode===13){
lCurrentFile=document.getElementsByClassName(CloudClient.CURRENT_FILE);
/* если ненайдены выделенные файлы - выходим*/
if(!lCurrentFile.length)return;
lCurrentFile=lCurrentFile[0];
/* из него достаём спан с именем файла*/
var lName=lCurrentFile.getElementsByClassName('name');
/* если нету (что вряд ли) - выходим*/
if(!lName)return false;
/* достаём все ссылки*/
var lATag=lName[0].getElementsByTagName('a');
/* если нету - выходим */
if(!lATag)return false;
/* получаем ссылку на каталог,
* что на уровень выше
*/
/* получаем имя каталога в котором находимся*/
var lHref;
try{
lHref=lCurrentFile.parentElement.getElementsByClassName('path')[0].innerText;
}catch(error){console.log('error');}
lHref=CloudFunc.removeLastSlash(lHref);
var lSubstr=lHref.substr(lHref,lHref.lastIndexOf('/'));
lHref=lHref.replace(lSubstr+'/','');
/* вызываем ajaxload привязанный через changelinks
* пробулем нажать на ссылку, если не получиться
* (opera, ie), вызываем событие onclick,
* которое пока не прописано у файлов
*/
if(lCurrentFile.onclick)lCurrentFile.onclick(true);
else try{
lATag[0].click();
}
catch(error){
console.log(error);
}
}
/* если нажали <ctr>+r */
else if(event.keyCode===82 &&
event.ctrlKey){
console.log('<ctrl>+r pressed');
console.log('reloading page...');
console.log('press <alt>+q to remove all key-handlers');
/* обновляем страницу, */
/* Загружаем содержимое каталога
* при этом данные берём всегда из
* сервера, а не из кэша
* (обновляем кэш)
*/
/* Программно нажимаем на кнопку перезагрузки
* содержимого каталога
*/
var lRefreshIcon=document.getElementsByClassName(CloudFunc.REFRESHICON);
if(lRefreshIcon)lRefreshIcon=lRefreshIcon[0];
if(lRefreshIcon){
/* находим файл который сейчас выделен */
lCurrentFile=document.getElementsByClassName(CloudClient.CURRENT_FILE);
if(lCurrentFile.length>0)lCurrentFile=lCurrentFile[0];
/* получаем название файла*/
var lSelectedName=lCurrentFile.getElementsByTagName('a')[0].innerText;
/* если нашли элемент нажимаем него
* а если не можем - нажимаем на
* ссылку, на которую повешен eventHandler
* onclick
*/
if(lRefreshIcon.click)lRefreshIcon.parentElement.click();
else lRefreshIcon.parentElement.onclick();
/* перебираем файлы левой панели
* в поисках подсвеченого файла
*/
var lLeft=document.getElementById('left');
if(lLeft){
/* перебираем все файлы в панели */
var lLi=lLeft.getElementsByTagName('li');
lCurrentFile.className='';
/* начинаем с 2-ух, по скольку
* 0 - это путь
* 1 - это заголовок файловой таблицы
*/
for(var i=2;i<lLi.length;i++){
var lName=lLi[i].getElementsByTagName('a')[0].innerText;
if(lSelectedName.length===lName.length &&
!lSelectedName.indexOf(lName)){
lLi[i].className=CloudClient.CURRENT_FILE;
break;
}
}
}
event.preventDefault();//запрет на дальнейшее действие
}
}
/* если нажали <ctrl>+d чистим кэш */
else if(event.keyCode===68 &&
event.ctrlKey){
console.log('<ctrl>+d pressed');
console.log('clearing cache...');
console.log('press <alt>+q to remove all key-handlers');
var lClearCache=document.getElementById('clear-cache');
if(lClearCache && lClearCache.onclick)lClearCache.onclick();
event.preventDefault();//запрет на дальнейшее действие
}
/* если нажали <alt>+q
* убираем все обработчики
* нажатий клавиш
*/
else if(event.keyCode===81 &&
event.altKey){
//document.removeEventListener('keydown', key_event,false);
console.log('<alt>+q pressed');
console.log('<ctrl>+r reload key-handerl - removed');
console.log('<ctrl>+s clear cache key-handler - removed');
console.log('press <alt>+s to to set them');
/* обработчик нажатий клавиш снят*/
CloudClient.keyBinded=false;
}
}
/* если нажали <alt>+s
* устанавливаем все обработчики
* нажатий клавиш
*/
else if(event.keyCode===83 &&
event.altKey){
/*
document.addEventListener('keydown', key_event,false);
*/
/* обрабатываем нажатия на клавиши*/
CloudClient.keyBinded=true;
console.log('<alt>+s pressed');
console.log('<ctrl>+r reload key-handerl - set');
console.log('<ctrl>+s clear cache key-handler - set');
console.log('press <alt>+q to remove them');
}
return false;
};
/* добавляем обработчик клавишь */
if(document.addEventListener)
document.addEventListener('keydown', key_event,false);
else document.onkeypress=key_event;
/* клавиши назначены*/
CloudClient.keyBinded=true;
});
/*
* Функция привязываеться ко всем ссылкам и
* загружает содержимое каталогов
*/
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){
/* если <ctrl>+<r>
* кнопка обновления
*/
try{this.firstChild.parentElement.appendChild(LoadingImage);}
catch(error){console.log(error);}
}
var lCurrentFile=document.getElementsByClassName(CloudClient.CURRENT_FILE);
/* получаем имя каталога в котором находимся*/
var lHref;
try{
lHref=lCurrentFile[0].parentElement.getElementsByClassName('path')[0].innerText;
}catch(error){console.log('error');}
lHref=CloudFunc.removeLastSlash(lHref);
var lSubstr=lHref.substr(lHref,lHref.lastIndexOf('/'));
lHref=lHref.replace(lSubstr+'/','');
/* загружаем содержимое каталога*/
CloudClient._ajaxLoad(pLink, pNeedRefresh);
/* получаем все элементы выделенной папки*/
/* при этом, если мы нажали обновить
* или <Ctrl>+R - ссылок мы ненайдём
* и заходить не будем
*/
var lA=this.getElementsByTagName('a');
/* если нажали на ссылку на верхний каталог*/
if(lA.length>0 && lA[0].innerText==='..' &&
lHref!=='/'){
/* функция устанавливает курсор на каталог
* с которого мы пришли, если мы поднялись
* в верх по файловой структуре
*/
CloudClient._currentToParent(lHref);
}
/* что бы не переходить по ссылкам
* а грузить всё ajax'ом,
* возвращаем false на событие
* onclick
*/
return false;
};
});
/* Функция устанавливает текущим файлом, тот
* на который кликнули единожды
*/
CloudClient._setCurrent=(function(){
/*
* @pFromEnter - если мы сюда попали
* из события нажатия на энтер -
* вызоветься _loadDir
*/
return function(pFromEnter){
var lCurrentFile=document.getElementsByClassName(CloudClient.CURRENT_FILE);
if(lCurrentFile && lCurrentFile.length > 0){
/* если мы находимся не на
* пути и не на заголовках
*/
if(this.className!=='path' &&
this.className!=='fm_header'){
lCurrentFile[0].className='';
/* устанавливаем курсор на файл,
* на который нажали */
}
}
this.className=CloudClient.CURRENT_FILE;
/*
console.log('Error. Can\'t find current file.'+
' (CloudClient._setCurrent)');
*/
/* если мы попали сюда с энтера*/
if(pFromEnter===true){
this.ondblclick(this);
}/* если мы попали сюда от клика мышки */
else{pFromEnter.returnValue=false;}
/* что бы не переходить по ссылкам
* а грузить всё ajax'ом,
* возвращаем false на событие
* onclick
*/
return false;
};
});
/* функция устанавливает курсор на каталог
* с которого мы пришли, если мы поднялись
* в верх по файловой структуре
* @pDirName - имя каталога с которого мы пришли
*/
CloudClient._currentToParent = (function(pDirName){
/* опредиляем в какой мы панели:
* правой или левой
*/
var lCurrentFile=document.getElementsByClassName(CloudClient.CURRENT_FILE);
var lPanel;
try{
lPanel=lCurrentFile[0].parentElement.id;
}catch(error){console.log("Current file not found\n"+error);}
/* убираем слэш с имени каталога*/
pDirName=pDirName.replace('/','');
/* ищем файл с таким именем*/
lPanel=document.getElementById(lPanel);
if(!lPanel)return;
var lLi=lPanel.getElementsByTagName('li');
for(var i=0;i<lLi.length;i++){
var lA=lLi[i].getElementsByTagName('a');
if(lA.length && lA[0].innerText===pDirName){
/* если уже выделен какой-то файл, снимаем
* выделение
*/
lCurrentFile=lPanel.getElementsByClassName(CloudClient.CURRENT_FILE);
if(lCurrentFile.length>0)lCurrentFile[0].className='';
lLi[i].className=CloudClient.CURRENT_FILE;
}
}
});
/* глобальные переменные */
var LoadingImage;
var ErrorImage;
var $;
var CloudFunc;
/* Конструктор CloudClient, который
* выполняет весь функционал по
* инициализации
*/
CloudClient.init=(function()
{
/* меняем title
* если js включен - имена папок отображать необязательно...
* а может и обязательно при переходе, можно будет это сделать
*/
var lTitle=document.getElementsByTagName('title');
if(lTitle.length>0)lTitle[0].innerText='Cloud Commander';
/* загружаем jquery: */
CloudClient.jsload('//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js',function(){
/* сохраняем переменную jQuery себе в область видимости */
$=window.jQuery;
window.jQuery || CloudClient.jsload('jquery.min.js',
function(){
$=window.jQuery;
});
});
/* загружаем общие функции для клиента и сервера*/
CloudClient.jsload('/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{
document.getElementsByClassName('path')[0].getElementsByTagName('a')[0].appendChild(LoadingImage);
LoadingImage.className+=' hidden'; /* прячем её */
}catch(error){console.log(error);}
ErrorImage=CloudClient._images.error();
/* устанавливаем размер высоты таблицы файлов
* исходя из размеров разрешения экрана
*/
/* формируем и округляем высоту экрана
* при разрешениии 1024x1280:
* 658 -> 700
*/
var lHeight=window.screen.height - (window.screen.height/3).toFixed();
lHeight=(lHeight/100).toFixed()*100;
var lFm=document.getElementById('fm');
if(lFm)lFm.style.cssText='height:' +
lHeight +
'px';
/* выделяем строку с первым файлом */
var lFmHeader=document.getElementsByClassName('fm_header');
if(lFmHeader && lFmHeader[0].nextSibling)
lFmHeader[0].nextSibling.className=CloudClient.CURRENT_FILE;
/* показываем элементы, которые будут работать только, если есть js */
var lFM=document.getElementById('fm');
if(lFM)lFm.className='localstorage';
/* если есть js - показываем правую панель*/
var lRight=document.getElementById('right');
if(lRight)lRight.className=lRight.className.replace('hidden','');
//var lLeft=document.getElementById('left');
//lLeft.style.cssText='width:50%';
CloudClient.cssSet({id:'show_2panels',
element:document.head,
inner:'#left{width:50%;}'
});
});
/* функция меняет ссыки на ajax-овые */
CloudClient._changeLinks = function(pPanelID)
{
/* назначаем кнопку очистить кэш и показываем её*/
var lClearcache=document.getElementById('clear-cache');
if(lClearcache)lClearcache.onclick=CloudClient.Cache.clear;
/* меняем ссылки на ajax-запросы */
var lPanel=document.getElementById(pPanelID);
var a=lPanel.getElementsByTagName('a');
/* Если нажмут на кнопку перезагрузить страниц - её нужно будет обязательно
* перезагрузить
*/
/* номер ссылки очистки кэша*/
//var lCLEARICON=0;
/* номер ссылки иконки обновления страницы */
var lREFRESHICON=0;
/* путь в ссылке, который говорит
* что js отключен
*/
var lNoJS_s = CloudFunc.NOJS;
var lFS_s = CloudFunc.FS;
for(var i=0;i<a.length;i++)
{
//if(i===2){/*ставим рамку на первый с верху файл*/
// a[i].parentElement.parentElement.className='current-file';
// }
/* если ссылка на папку, а не файл */
if(a[i].target!='_blank')
{
/* убираем адрес хоста*/
var link='/'+a[i].href.replace(document.location.href,'');
/* убираем значения, которые говорят,
* об отсутствии js
*/
if(link.indexOf(lNoJS_s)===lFS_s.length){
link=link.replace(lNoJS_s,'');
}
/* ставим загрузку гифа на клик*/
if(i===lREFRESHICON)
a[i].onclick=CloudClient._loadDir(link,true);
/* если мы попали на кнопку обновления структуры каталогов */
/*
if(a[i].className && a[i].className===CloudFunc.REFRESHICON)
*/
/* устанавливаем обработчики на строку на одинарное и
* двойное нажатие на левую кнопку мышки
*/
else{
try{
a[i].parentElement.parentElement.onclick=CloudClient._setCurrent();
a[i].parentElement.parentElement.ondblclick=CloudClient._loadDir(link);
}catch(error){console.log(error);}
}
}
}
};
/*
* Функция загружает json-данные о Файловой Системе
* через ajax-запрос.
* @path - каталог для чтения
* @pNeedRefresh - необходимость обновить данные о каталоге
*/
CloudClient._ajaxLoad=function(path, pNeedRefresh)
{
/* Отображаем красивые пути */
var lPath=path;
var lFS_s=CloudFunc.FS;
if(lPath.indexOf(lFS_s)===0){
lPath=lPath.replace(lFS_s,'');
if(lPath==='')lPath='/';
}
console.log ('reading dir: "'+lPath+'";');
/* если доступен localStorage и
* в нём есть нужная нам директория -
* читаем данные с него и
* выходим
* если стоит поле обязательной перезагрузки -
* перезагружаемся
*/
/* опредиляем в какой мы панели:
* правой или левой
*/
var lPanel;
try{
lPanel=document.getElementsByClassName(CloudClient.CURRENT_FILE)[0].parentElement.id;
}catch(error){console.log("Current file not found\n"+error);}
if(pNeedRefresh===undefined && lPanel){
var lJSON=CloudClient.Cache.get(lPath);
if (lJSON!==null){
/* переводим из текста в JSON */
if(window && !window.JSON){
try{
lJSON=eval('('+lJSON+')');
}catch(err){
console.log(err);
}
}else lJSON=JSON.parse(lJSON);
CloudClient._createFileTable(lPanel,lJSON);
CloudClient._changeLinks(lPanel);
return;
}
}
/* ######################## */
try{
$.ajax({
url: path,
error: function(jqXHR, textStatus, errorThrown){
console.log(textStatus+' : '+errorThrown);
var lLoading=document.getElementById('loading-image');
ErrorImage.className='icon error';
ErrorImage.title=errorThrown;
lLoading.parentElement.appendChild(ErrorImage);
lLoading.className='hidden';
//document.getElementsByClassName('path')[0].appendChild(ErrorImage);
},
success:function(data, textStatus, jqXHR){
/* если такой папки (или файла) нет
* прячем загрузку и показываем ошибку
*/
/* для совместимости с firefox меняем data
* на jqXHR, он воспринимает data к Document
* когда возвращаеться ошибка, о том, что
* нет файла или нет доступа
*/
var lLoading;
if(!jqXHR.responseText.indexOf('Error:')){
/* если файла не существует*/
if(!jqXHR.responseText.indexOf('Error: ENOENT, ')){
ErrorImage.title=jqXHR.responseText.replace('Error: ENOENT, n','N');
}
/* если не хватает прав для чтения файла*/
else if(!jqXHR.responseText.indexOf('Error: EACCES,')){
ErrorImage.title=jqXHR.responseText.replace('Error: EACCES, p','P');
}
ErrorImage.className='icon error';
lLoading=document.getElementById('loading-image');
lLoading.parentElement.appendChild(ErrorImage);
lLoading.className='hidden';
return;
}
CloudClient._createFileTable(lPanel,data);
CloudClient._changeLinks(lPanel);
/* Сохраняем структуру каталогов в localStorage,
* если он поддерживаеться браузером
*/
/* переводим таблицу файлов в строку, для
* сохранения в localStorage
*/
var lJSON_s=JSON.stringify(data);
console.log(lJSON_s.length);
/* если размер данных не очень бошьой
* сохраняем их в кэше
*/
if(lJSON_s.length<50000)
CloudClient.Cache.set(lPath,lJSON_s);
}
});
}catch(err){console.log(err);}
};
/*
* Функция строит файловую таблицу
* @pEleme - родительский элемент
* @pJSON - данные о файлах
*/
CloudClient._createFileTable = function(pElem,pJSON)
{
var lElem=document.getElementById(pElem);
/* говорим построителю,
* что бы он в нужный момент
* выделил строку с первым файлом
*/
/* очищаем панель */
var i = lElem.childNodes.length;
while(i--){
lElem.removeChild(lElem.lastChild);
}
/* заполняем панель новыми элементами */
lElem.innerHTML=CloudFunc.buildFromJSON(pJSON,true);
};
/*
* Функция создаёт элемент и
* загружает файл с src.
* @pName - название тэга
* @pSrc - путь к файлу
* @pFunc - функци
* @pStyle - стиль
* @pId - id
* @pElement - элемент, дочерним которо будет этот
*/
CloudClient._anyload = function(pName,pSrc,pFunc,pStyle,pId,pElement)
{
//если скрипт еще не загружен
/* убираем путь к файлу, оставляя только название файла */
var lID;
if(pId===undefined){
lID=pSrc.replace(pSrc.substr(pSrc,pSrc.lastIndexOf('/')+1),'');
/* убираем точку*/
lID=lID.replace('.','_');
}else lID=pId;
if(!document.getElementById(lID))
{
var element = document.createElement(pName);
element.src = pSrc;
element.id=lID;
if(arguments.length>=3){
element.onload=pFunc;
if(arguments.length>=4){
element.style.cssText=pStyle;
}
}
//document.body
pElement.appendChild(element);
return element;//'elem '+src+' loaded';
}
/* если js-файл уже загружен
* запускаем функцию onload
*/
else if(pFunc){
try{
pFunc();
}catch(error){console.log(error);}
}
};
/* Функция загружает js-файл */
CloudClient.jsload = function(pSrc,pFunc,pStyle,pId)
{
CloudClient._anyload('script',pSrc,pFunc,pStyle,pId,document.body);
};
/* Функция создаёт елемент style и записывает туда стили
* @pParams_o - структура параметров, заполняеться таким
* образом: {src: ' ',func: '', id: '', element: '', inner: ''}
* все параметры опциональны
*/
CloudClient.cssSet = function(pParams_o){
var lElem=CloudClient._anyload('style',
pParams_o.src,
pParams_o.func,
pParams_o.style,
pParams_o.id,
pParams_o.element?pParams_o.element:document.body);
lElem.innerText=pParams_o.inner;
};
/*
* Функция генерирует JSON из html-таблицы файлов
*/
/*
* Используеться при первом заходе в корень
*/
CloudClient._getJSONfromFileTable=function()
{
var lLeft=document.getElementById('left');
//var lPath=document.getElementById('path').innerText;
var lPath=document.getElementsByClassName('path')[0].innerText;
var lFileTable=[{path:lPath,size:'dir'}];
var lLI=lLeft.getElementsByTagName('li');
var j=1;/* счётчик реальных файлов */
var i=1;/* счётчик элементов файлов в DOM */
/* Если путь отличный от корневного
* второй элемент li - это ссылка на верхний
* каталог '..'
*/
i=2;/* пропускам Path и Header*/
for(;i<lLI.length;i++)
{
var lIsDir=lLI[i].getElementsByClassName('mini-icon')[0]
.className.replace('mini-icon ','')==='directory'?true:false;
var lName=lLI[i].getElementsByClassName('name')[0].innerText;
/* если это папка - выводим слово dir вместо размера*/
var lSize=lIsDir?'dir':lLI[i].getElementsByClassName('size')[0].innerText;
var lMode=lLI[i].getElementsByClassName('mode')[0].innerText;
/* переводим права доступа в цыфровой вид
* для хранения в localStorage
*/
lMode=CloudFunc.convertPermissionsToNumberic(lMode);
lFileTable[j++]={
name:lName,
size:lSize,
mode:lMode
};
}
return JSON.stringify(lFileTable);
};
/* если нет функции поиска по класам,
* а её нет в IE,
* - используем jquery
* при необходимости
* можна заменить на любой другой код
*/
if(!document.getElementsByClassName){
CloudClient.jsload('//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js',function(){
/* сохраняем переменную jQuery себе в область видимости */
document.getElementsByClassName=function(pClassName){
return $('.'+pClassName)[0];
};
$=window.jQuery;
window.jQuery || CloudClient.jsload('jquery.min.js',
function(){
$=window.jQuery;
document.getElementsByClassName=function(pClassName){
return $('.'+pClassName)[0];
};
});
});
}
return CloudClient;
});//(this,this.document);
try{
var Commander;
window.onload=function(){
'use strict';
Commander=new CloudCommander();
/* базовая инициализация*/
Commander.init();
/* привязываем клавиши к функциям */
Commander.keyBinding();
};
}
catch(err){}

511
cloudfunc.js Normal file
View file

@ -0,0 +1,511 @@
/* Модуль, содержащий функции, которые будут работать
* и на клиенте и на сервере
*
* Привала названий:
* varName - имя функции
* lVarName - имя локальной переменной
* pVarName - имя параметра
* fVarName - имя функции созданной внутри функции
* VARNAME - имя константы
*
* Типы переменных:
* varNameS - строка
* varNameN - число
* varNameO - обьект
* varNameM - массив
*/
var CloudFunc={
/* Путь с которым мы сейчас работаем */
Path :'',
/* КОНСТАНТЫ (общие для клиента и сервера)*/
/* название программы */
NAME :'Cloud Commander',
/* если в ссылке будет эта строка -
* в браузере js отключен
*/
NOJS : '/no-js',
FS : '/c/f/s',
/* название css-класа кнопки обновления файловой структуры*/
REFRESHICON : 'refresh-icon',
/* id панелей с файлами */
LEFTPANEL : 'left',
RIGHTPANEL : 'right'
};
/*
* Функция убирает последний слеш,
* если он - последний символ строки
*/
CloudFunc.removeLastSlash = function(pPath){
if(typeof pPath==='string')
return (pPath.lastIndexOf('/')===pPath.length-1)?
pPath.substr(pPath, pPath.length-1):pPath;
else return pPath;
};
/*
* Функция меняет код символа пробела на пробел
* в переданной строке
* @pPath - строка
*/
CloudFunc.replaceSpaces = function(pPath){
if(pPath.indexOf('%20')>0){
do{
pPath=pPath.replace('%20',' ');
}while(pPath.indexOf('%20')>0);
}
return pPath;
};
/* Функция возвращает заголовок веб страницы */
CloudFunc.setTitle = function(){
return CloudFunc.Path===''?CloudFunc.NAME:
CloudFunc.Path +
' - ' +
CloudFunc.NAME;
};
/* Функция переводит права из цыфрового вида в символьный
* @pPerm_s - строка с правами доступа
* к файлу в 8-миричной системе
*/
CloudFunc.convertPermissionsToSymbolic= function(pPerm_s){
/*
S_IRUSR 0000400 protection: readable by owner
S_IWUSR 0000200 writable by owner
S_IXUSR 0000100 executable by owner
S_IRGRP 0000040 readable by group
S_IWGRP 0000020 writable by group
S_IXGRP 0000010 executable by group
S_IROTH 0000004 readable by all
S_IWOTH 0000002 writable by all
S_IXOTH 0000001 executable by all
*/
if(pPerm_s===undefined) return;
/* тип файла */
var lType=pPerm_s.charAt(0);
switch (lType-0) {
case 1: /* обычный файл */
lType='-';
break;
case 2: /* байт-ориентированное (символьное) устройство*/
lType='c';
break;
case 4: /* каталог */
lType='d';
break;
default:
lType='-';
}
/* оставляем последние 3 символа*/
pPerm_s=pPerm_s.length>5?pPerm_s.substr(3):pPerm_s.substr(2);
/* Рекомендации гугла советуют вместо string[3]
* использовать string.charAt(3)
*/
/*
http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml?showone=Standards_features#Standards_features
Always preferred over non-standards featuresFor
maximum portability and compatibility, always
prefer standards features over non-standards
features (e.g., string.charAt(3) over string[3]
and element access with DOM functions instead
of using an application-specific shorthand).
*/
/* Переводим в двоичную систему */
var lOwner=(pPerm_s[0]-0).toString(2);
var lGroup=(pPerm_s[1]-0).toString(2);
var lAll =(pPerm_s[2]-0).toString(2);
/*
console.log(lOwner+' '+lGroup+' '+lAll);
*/
/* переводим в символьную систему*/
var lPermissions=//lType+' '+
(lOwner[0]-0>0?'r':'-')+
(lOwner[1]-0>0?'w':'-')+
(lOwner[2]-0>0?'x':'-')+
' ' +
(lGroup[0]-0>0?'r':'-')+
(lGroup[1]-0>0?'w':'-')+
(lGroup[2]-0>0?'x':'-')+
' ' +
(lAll[0]-0>0?'r':'-')+
(lAll[1]-0>0?'w':'-')+
(lAll[2]-0>0?'x':'-');
/*
console.log(lPermissions);
*/
return lPermissions;
}
/* Функция конвертирует права доступа к файлам из символьного вида
* в цыфровой
*/
CloudFunc.convertPermissionsToNumberic= function(pPerm_s){
/* если передана правильная строка, конвертированная
* функциец convertPermissionsToSymbolic
*/
if(!pPerm_s || pPerm_s.length!==11)return pPerm_s;
var lOwner= (pPerm_s[0]==='r'?4:0) +
(pPerm_s[1]==='w'?2:0) +
(pPerm_s[2]==='x'?1:0);
var lGroup= (pPerm_s[4]==='r'?4:0) +
(pPerm_s[5]==='w'?2:0) +
(pPerm_s[6]==='x'?1:0);
var lAll = (pPerm_s[8]==='r'?4:0) +
(pPerm_s[9]==='w'?2:0) +
(pPerm_s[10]==='x'?1:0);
/* добавляем 2 цыфры до 5 */
return '00'+lOwner+lGroup+lAll;
}
/* Функция получает короткие размеры
* конвертируя байт в килобайты, мегабойты,
* гигайбайты и терабайты
* @pSize - размер в байтах
*/
CloudFunc.getShortedSize=function(pSize){
/* Константі размеров, что используются
* внутри функции
*/
var l1BMAX=1024;
var l1KBMAX=1048576;
var l1MBMAX=1073741824;
var l1GBMAX=1099511627776;
var l1TBMAX=1125899906842624;
var lShorted;
if(pSize<l1BMAX)lShorted=pSize+'b';
else if(pSize<l1KBMAX)lShorted=(pSize/l1BMAX) .toFixed(2)+'kb'
else if(pSize<l1MBMAX)lShorted=(pSize/l1KBMAX).toFixed(2)+'mb'
else if(pSize<l1GBMAX)lShorted=(pSize/l1MBMAX).toFixed(2)+'gb'
else if(pSize<l1TBMAX)lShorted=(pSize/l1GBMAX).toFixed(2)+'tb'
return lShorted;
}
/* Функция парсит uid и имена пользователей
* из переданного в строке вычитаного файла /etc/passwd
* и возвращает массив обьектов имён и uid пользователей
* @pPasswd_s - строка, в которой находиться файл /etc/passwd
*/
CloudFunc.getUserUIDsAndNames=function(pPasswd_s){
var lUsers={name:'',uid:''};
var lUsersData=[];
var i=0;
do{
/* получаем первую строку */
var lLine=pPasswd_s.substr(pPasswd_s,pPasswd_s.indexOf('\n')+1);
if(lLine){
/* удаляем первую строку из /etc/passwd*/
pPasswd_s=pPasswd_s.replace(lLine,'');
/* получаем первое слово строки */
var lName=lLine.substr(lLine,lLine.indexOf(':'));
lLine=lLine.replace(lName+':x:','');
/* получаем uid*/
var lUID=lLine.substr(lLine,lLine.indexOf(':'));
if((lUID-0).toString()!=='NaN'){
lUsers.name=lName;
lUsers.uid=lUID;
lUsersData[i++]=lUsers;
console.log('uid='+lUID+' name='+lName);
}
}
}while(pPasswd_s!=='');
return lUsersData;
}
/* Функция получает адреса каждого каталога в пути
* возвращаеться массив каталогов
* @url - адрес каталога
*/
CloudFunc._getDirPath=function(url)
{
var folders=new Array();
var i=0;
do{
folders[i++]=url; url=url.substr(url,url.lastIndexOf('/'));
}while(url!='');
/* сохраяем адрес предыдущего каталога */
/*
if(i>2){
if(folders[0].lastIndexOf('/')===folders[0].length)
LPrevDir=folders[1];
else LPrevDir=folders[2];
}else LPrevDir='/';
*/
/* ################################### */
/* Формируем ссылки на каждый каталог в пути */
var lHref='<a class=links href=';
var lTitle=' title=';
var _l='>';
var lHrefEnd='</a>';
var lHtmlPath;
/* путь в ссылке, который говорит
* что js отключен
*/
var lNoJS_s=CloudFunc.NOJS;
var lFS_s=CloudFunc.FS;
/* корневой каталог */
lHtmlPath=lHref+lFS_s+lNoJS_s+lTitle+'"/"'+_l+'/'+lHrefEnd;
for(i=folders.length-1;i>0;i--)
{
var lUrl=folders[i];
var lShortName=lUrl.replace(lUrl.substr(lUrl,lUrl.lastIndexOf('/')+1),'');
if(i!=1)
{
lHtmlPath+=lHref+lFS_s+lNoJS_s+lUrl+lTitle+lUrl+_l+lShortName+lHrefEnd+'/';
}
else
lHtmlPath+=lShortName+'/';
}
/* *** */
return lHtmlPath;
}
/*
* Функция ищет в имени файла расширение
* и если находит возвращает true
* @pName - получает имя файла
* @pExt - расширение
*/
CloudFunc.checkExtension=function(pName,pExt)
{
/* если длина имени больше
* длинны расширения -
* имеет смысл продолжать
*/
if(pName.length>pExt.length){
var lLength=pName.length; /* длина имени*/
var lExtNum=pName.lastIndexOf(pExt);/* последнее вхождение расширения*/
var lExtSub=lLength-lExtNum; /* длина расширения*/
/* если pExt - расширение pName */
if(lExtSub===pExt.length)
return true;
else
return false;
}
else return false;
}
/*
* Функция формирует заголовки столбиков
* @pFileTableTitles - массив названий столбиков
*/
CloudFunc._getFileTableHeader=function(pFileTableTitles)
{
var lHeader='<li class=fm_header>';
lHeader+='<span class=mini-icon></span>';
for(var i=0;i<pFileTableTitles.length;i++)
{
var lStr=pFileTableTitles[i];
lHeader+='<span class='+lStr+'>'+
lStr+
'</span>';
}
lHeader+='</li>';
return lHeader;
}
/*
* Функция строит таблицу файлв из JSON-информации о файлах
* @pJSON - информация о файлах
* @pKeyBinded - если клавиши назначены, выделяем верхний файл
* [{path:'путь',size:'dir'},
* {name:'имя',size:'размер',mode:'права доступа'}]
*/
CloudFunc.buildFromJSON=function(pJSON,pKeyBinded)
{
var files;
/*
* если пропарсить стандартными
* функциями нельзя -
* пробуем eval,
*/
/*
* Если мы на клиенте и нет JSON -
* через eval парсим.
* Если-же мы на сервере,
* или на клиенте всё есть
* парсим стандарным методом
*/
/* По скольку мы прописали заголовок application/json
* нет необходимости его конвертировать,
* но она есть, если мы вытягиваем данные из
* localStorage
*/
/*
if(typeof pJSON==='string'){
if(window && !window.JSON){
try{
files=eval('('+pJSON+')');
}catch(err){
console.log(err);
}
}
else files=JSON.parse(pJSON);
}else
*/
files=pJSON;
/* сохраняем путь каталога в котором мы сейчас находимся*/
var lPath=files[0].path;
/* сохраняем путь */
CloudFunc.Path=lPath;
/*
* Строим путь каталога в котором мы находимся
* со всеми подкаталогами
*/
var lHtmlPath=CloudFunc._getDirPath(lPath);
/* Убираем последний слэш
* с пути для кнопки обновить страницу
* если он есть
*/
var lRefreshPath=CloudFunc.removeLastSlash(lPath);
/* путь в ссылке, который говорит
* что js отключен
*/
var lNoJS_s=CloudFunc.NOJS;
var lFS_s=CloudFunc.FS;
var lFileTable='<li class=path>'+
'<span class="path_icon clear-cache" id=clear-cache title="clear cache (Ctrl+D)"></span>'+
'<a href="'+lFS_s+lNoJS_s+lRefreshPath+'">'+
'<span class="path_icon ' + CloudFunc.REFRESHICON + '"' +
' title="refresh (Ctrl+R)">' +
'</span>'+
'</a>'+
'<span>'+lHtmlPath+'</span>'+
'</li>';
var fileTableTitles=['name','size','owner','mode'];
lFileTable+=CloudFunc._getFileTableHeader(fileTableTitles);
/* Если мы не в корне */
if(lPath!=='/'){
/* ссылка на верхний каталог*/
var lDotDot;
/* убираем последний слеш и каталог в котором мы сейчас находимся*/
lDotDot=lPath.substr(lPath,lPath.lastIndexOf('/'));
lDotDot=lDotDot.substr(lDotDot,lDotDot.lastIndexOf('/'));
/* Если предыдущий каталог корневой */
if(lDotDot==='')lDotDot='/';
/* Сохраняем путь к каталогу верхнего уровня*/
lFileTable += '<li class=current-file>'+
'<span class="mini-icon directory">' +
'</span>' +
'<span class=name>' +
'<a href="'+lFS_s+lNoJS_s +
lDotDot +
'">'+"..</a>" +
'</span>' +
'<span class=size>&lt;dir&gt;</span>'+
'<span class=owner>.</span>' +
'<span class=mode></span>' +
'</li>';
}
var lLength=files.length;
for(var i=1;i<lLength;i++){
lFileTable +='<li class>';
lFileTable += '<span class="mini-icon ';
/* если папка - выводим другую иконку */
lFileTable += (files[i].size==='dir'?
'directory':'text-file') +
'">';
lFileTable +='</span>';
lFileTable +='<span class=name>' +
'<a href="'+lFS_s+lNoJS_s +
lPath+files[i].name +
'"' +
/* открываем файлы */
/*в новой вкладке */
(files[i].size==='dir'?'': ' target="_blank"')+
/* если длина имени файла больше 16 символов
* отрезаем лишнее, оставляя лишь 16,
* и добавляем две точки и тайтл
*/
(files[i].name.length>16?
' title="'+files[i].name+'">' +
files[i].name.substr(
files[i].name,16)+
'..':'>'+files[i].name) +
"</a>" +
'</span>';
/* если папка - не выводим размер */
lFileTable +='<span class=size>' +
(files[i].size==='dir'?
'&lt;dir&gt;':
/* если это файл - получаем
* короткий размер
*/
CloudFunc.getShortedSize(
files[i].size));
lFileTable +='</span>' +
'<span class=owner>' +
(!files[i].uid?'root':files[i].uid) +
'</span>' +
'<span class=mode>' +
/* конвертируем названия разрешений
* из числового формата в буквенный
* при этом корневой каталог не трогаем
* по скольку в нём и так всё уже
* установлено еще на сервере
*/
(//lPath==='/'?files[i].mode:
CloudFunc.convertPermissionsToSymbolic(files[i].mode)) +
'</span>';
lFileTable +='</li>';
}
/* если клавиши назначены и
* мы в корневом каталоге и
* верхний файл еще не выделен -
* выделяем верхний файл
*/
if(pKeyBinded && lPath==='/'&&
lFileTable.indexOf('<li class=current-file>')<0){
lFileTable=lFileTable.replace('<li class>','<li class=current-file>');
}
return lFileTable;
}
/*
* Если мы на стороне сервера -
* прописываем экспортируемые функции
*/
try{
if(exports){
/* экспортируемые функции */
exports.checkExtension = CloudFunc.checkExtension;
exports.buildFromJSON = CloudFunc.buildFromJSON;
exports.replaceSpaces = CloudFunc.replaceSpaces;
exports.setTitle = CloudFunc.setTitle;
exports.convertPermissions = CloudFunc.convertPermissions;
exports.getUserUIDsAndNames = CloudFunc.getUserUIDsAndNames;
/* константы*/
exports.Name = CloudFunc.NAME;
exports.NOJS = CloudFunc.NOJS;
exports.FS =CloudFunc.FS;
console.log('cloudfunc.js loaded...');
}
}catch(err){
/* если мы на клиенте */
}

59
index.html Normal file
View file

@ -0,0 +1,59 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<!-- moblie first design -->
<meta content="width=device-width,initial-scale=1" name="viewport" />
<title>Cloud Commander</title>
<!--
<link rel="stylesheet" type="text/css" href="http://dl.dropbox.com/u/74212301/mnemonia/css/reset.css">
<link rel="stylesheet" type="text/css" href="http://rastacoding.kodingen.com/online/header/css/button.css">
-->
<link rel="stylesheet" href="/reset.css">
<link rel="stylesheet" href="/style.css">
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Droid+Sans+Mono">
<!--
<script async src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script async src=http://dl.dropbox.com/u/74212301/mnemonia/js/prefixfree.min.js></script>
<script async src="//modernizr.com/downloads/modernizr-2.5.3.js"></script>
<script async src="http://dl.dropbox.com/u/78163899/mnemonia/js/css3-mediaqueries.js"></script>
<script async src="dist/html5shiv.js"></script>
-->
<!--[if lt IE 9]>
<![endif]-->
</head>
<body>
<!--
<input placeholder=bin style="width:100%;position:relative;top:-1px">
-->
<!--
http://gtmetrix.com/reports/cloudcmd.cloudfoundry.com/b8hQTONq
-->
<a class="yellow hidden">Cloud Commander</a>
<ul class="menu hidden">
<li>
<!-- http://css-tricks.com/examples/IconFont/ -->
<a href=settings onclick="return false;">
<span class="menu_icon settings hidden"></span>
</a>
<a href=refresh onclick="return false;">
<span class="menu_icon refresh" id=refresh></span>
</a>
</li>
</ul>
<div id=fm class=no-js>
</div>
<div id=keyspanel class=hidden>
<button>F1 - help</button>
<button>F2 - rename</button>
<button>F3 - view</button>
<button>F4 - edit</button>
<button>F5 - copy</button>
<button>F6 - move</button>
<button>F7 - make dir</button>
<button>F8 - remove</button>
</div>
<script src=/client.js></script>
</body>
</html>

81
minify.js Normal file
View file

@ -0,0 +1,81 @@
/* Модуль сжатия js-скриптов*/
/*
https://github.com/GoalSmashers/clean-css
*/
exports.jsScripts=jsScripts;
function jsScripts(){
'use strict';
/* подключаем модуль uglify-js
* если его нет - дальнейшая
* работа модуля не имеет смысла
*/
try{
var jsp = require("uglify-js").parser;
var pro = require("uglify-js").uglify;
}catch(error){
return console.log('ERROR. error loading minificatoin js\n' +
'to use minification you need to install uglify-js\n' +
'npm install uglify-js\n' +
'https://github.com/mishoo/UglifyJS\n' +
error);
}
var fs = require('fs');
/* Константы */
var CLIENT_JS='client.js';
var CLOUDFUNC_JS='cloudfunc.js';
console.log('reading file ' + CLIENT_JS+'...');
fs.readFile(CLIENT_JS,fileReaded(CLIENT_JS));
console.log('reading file ' + CLOUDFUNC_JS+'...');
fs.readFile(CLOUDFUNC_JS,fileReaded(CLOUDFUNC_JS));
/* Функция создаёт асинхроную версию
* для чтения файла
* @pFileName - имя считываемого файла
*/
function fileReaded(pFileName){
return function(error,data){
/* функция в которую мы попадаем,
* если данные считались
*/
var dataReaded=function(){
console.log('file ' + pFileName + ' readed');
/*********************************/
/* сжимаем код через uglify-js */
var orig_code = data.toString();
var ast = jsp.parse(orig_code); // parse code and get the initial AST
ast = pro.ast_mangle(ast); // get a new AST with mangled names
ast = pro.ast_squeeze(ast); // get an AST with compression optimizations
var final_code = pro.gen_code(ast); // compressed code here
/*********************************/
var minFileName=pFileName.replace('.js','.min.js');
/* если мы сжимаем client.js -
* меняем строку cloudfunc.js на
* cloudfunc.min.js и выводим сообщение
* если другой файл - ничего не деалем
*/
(pFileName===CLIENT_JS)?
console.log('file name of '+CLOUDFUNC_JS+' in '+CLIENT_JS+' changed. size:',
(final_code=final_code.replace(CLOUDFUNC_JS,
CLOUDFUNC_JS.replace('.js',
'.min.js'))).length):
'';
var fileWrited=function(error){
console.log(error?error:('file '+minFileName+' writed...'));
};
/* записываем сжатый js-скрипт*/
fs.writeFile(minFileName, final_code, fileWrited);
};
error?console.log(error):dataReaded();
};
}
};

115
reset.css Normal file
View file

@ -0,0 +1,115 @@
/*
* HTML5 Boilerplate
*
* What follows is the result of much research on cross-browser styling.
* Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal,
* Kroc Camen, and the H5BP dev community and team.
*
* Detailed information about this CSS: h5bp.com/css
*
* ==|== normalize ==========================================================
*/
/* =============================================================================
HTML5 display definitions
========================================================================== */
/*
article, aside, details, figcaption, figure, footer, header, hgroup, nav, section { display: block; }
audio, canvas, video { display: inline-block; *display: inline; *zoom: 1; }
audio:not([controls]) { display: none; }
[hidden] { display: none; }
*/
/* =============================================================================
Base
========================================================================== */
/*
* 1. Correct text resizing oddly in IE6/7 when body font-size is set using em units
* 2. Prevent iOS text size adjust on device orientation change, without disabling user zoom: h5bp.com/g
*/
html { font-size: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; font-family: sans-serif; color: #222;}
body { margin: 0; font-size: 1em; line-height: 1.4; }
/*
* Remove text-shadow in selection highlight: h5bp.com/i
* These selection declarations have to be separate
* Also: hot pink! (or customize the background color to match your design)
*/
/*
::-moz-selection { background: #fe57a1; color: #fff; text-shadow: none; }
::selection { background: #fe57a1; color: #fff; text-shadow: none; }
*/
::selection { text-shadow: none; opacity: 0;}
/* =============================================================================
Links
========================================================================== */
a {text-decoration:none; color: #00e; }
a:visited { color: #551a8b; }
a:hover { color: #06e; }
a:focus { outline: thin dotted; }
/* Improve readability when focused and hovered in all browsers: h5bp.com/h */
a:hover, a:active { outline: 0; }
ul{ margin: 1em 0; padding: 0 0 0 40px; }
/*
* 1. Display hand cursor for clickable form elements
* 2. Allow styling of clickable form elements in iOS
* 3. Correct inner spacing displayed oddly in IE7 (doesn't effect IE6)
*/
/* =============================================================================
Chrome Frame Prompt
========================================================================== */
.chromeframe { margin: 0.2em 0; background: #ccc; color: black; padding: 0.2em 0; }
/* ==|== non-semantic helper classes ========================================
Please define your styles before this section.
========================================================================== */
/* For image replacement */
.ir { display: block; border: 0; text-indent: -999em; overflow: hidden; background-color: transparent; background-repeat: no-repeat; text-align: left; direction: ltr; *line-height: 0; }
.ir br { display: none; }
/* Hide from both screenreaders and browsers: h5bp.com/u */
.hidden { display: none !important; visibility: hidden; }
/* Hide only visually, but have it available for screenreaders: h5bp.com/v */
.visuallyhidden { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; }
/* Extends the .visuallyhidden class to allow the element to be focusable when navigated to via the keyboard: h5bp.com/p */
.visuallyhidden.focusable:active, .visuallyhidden.focusable:focus { clip: auto; height: auto; margin: 0; overflow: visible; position: static; width: auto; }
/* Hide visually and from screenreaders, but maintain layout */
.invisible { visibility: hidden; }
/* Contain floats: h5bp.com/q */
.clearfix:before, .clearfix:after { content: ""; display: table; }
.clearfix:after { clear: both; }
.clearfix { *zoom: 1; }
/* ==|== print styles =======================================================
Print styles.
Inlined to avoid required HTTP connection: h5bp.com/r
========================================================================== */
@media print {
* { background: transparent !important; color: black !important; box-shadow:none !important; text-shadow: none !important; filter:none !important; -ms-filter: none !important; } /* Black prints faster: h5bp.com/s */
a, a:visited { text-decoration: underline; }
a[href]:after { content: " (" attr(href) ")"; }
.ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } /* Don't show links for images, or javascript/internal links */
@page { margin: 0.5cm; }
}

563
server.js Normal file
View file

@ -0,0 +1,563 @@
"use strict";
/* Обьект содержащий все функции и переменные
* серверной части Cloud Commander'а
*/
var CloudServer={
/* функция, которая генерирует заголовки
* файлов, отправляемые сервером клиенту
*/
generateHeaders :function(){},
/* функция высылает
* данные клиенту
*/
sendResponse :function(){},
/* Структура содержащая функции,
* и переменные, в которых
* говориться о поддерживаемых
* браузером технологиях
*/
BrowserSuport :{},
/* Обьект для работы с кэшем */
Cashe :{},
/* Обьект через который
* выполняеться сжатие
* скриптов и стилей
*/
Minify :{},
/* Асоциативный масив обьектов для
* работы с ответами сервера
* высылаемыми на запрос о файле и
* хранащий информацию в виде
* Responces[name]=responce;
*/
Responses :{},
/* ПЕРЕМЕННЫЕ */
/* Поддержка браузером JS*/
NoJS :true,
/* обьект содержит данные
* о необходимости сжатия
* данных и скриптов
*/
Minimize :{
scriptSize:false,
styleSize:false
},
/* Поддержка gzip-сжатия
* браузером
*/
Gzip :undefined,
/* КОНСТАНТЫ */
/* index.html */
INDEX :'index.html'
};
/*
* Обьект для работы с кэшем
* аналог клиентского обьекта
* с тем отличием, что в нём
* будут храниться серверные
* данные, такие как файлы
* отдаваемые клиенту
* (файлы проэкта по большому
* счёту, для ускорения
* первичной загрузки)
*/
CloudServer.Cache={
_allowed :true, /* приватный переключатель возможности работы с кэшем */
/* данные в которых храняться файлы
* в формате <поле> : <значение>
* _data[name]=pData;
* одному имени соответствуют
* одни данные
*/
_data :{},
/* функция говорит можно ли работать с кэшем */
isAllowed :(function(){
return CloudServer.Cache._allowed;
}),
/* функция устанавливает возможность работать с кэшем */
setAllowed :(function(pAllowed){
CloudServer.Cache._allowed=pAllowed;
}),
/* Если доступен кэш
* сохраняем в него данные
*/
set :(function(pName, pData){
if(CloudServer.Cache._allowed && pName && pData){
CloudServer.Cache._data[pName]=pData;
}
}),
/* Если доступен Cache принимаем из него данные*/
get :(function(pName){
if(CloudServer.Cache._allowed && pName){
return CloudServer.Cache._data[pName];
}
else return null;
}),
/* Функция очищает кэш*/
clear :(function(){
if(CloudServer.Cache._allowed){
CloudServer.Cache._data={};
}
})
};
/* Обьект для сжатия скриптов и стилей
*/
CloudServer.Minify={
scripts : function(){
if(CloudServer.Minimize.scriptSize){
var lMinify = require('./minify');
lMinify.jsScripts();
}
}
};
//var DirContent;
var LeftDir='/';
var RightDir=LeftDir;
//var LPrevDir;
//var RPrevDir;
var Fs = require('fs'); /* модуль для работы с файловой системой*/
var Path = require('path'); /* модуль для работы с путями*/
var Zlib = require('zlib'); /* модуль для сжатия данных gzip-ом*/
var CloudFunc=CloudServer.Minimize.scripts?/* если стоит минификация*/
require('./cloudfunc.min'):/* добавляем сжатый - иначе обычный */
require('./cloudfunc'); /* модуль с функциями */
/* конструктор*/
CloudServer.init=(function(){
/* Переменная в которой храниться кэш*/
CloudServer.Cache.setAllowed(false);
CloudServer.Minimize.scriptSize=true;
/* Если нужно минимизируем скрипты */
CloudServer.Minify.scripts();
});
/* создаём сервер на порту 31337*/
CloudServer.start=function()
{
CloudServer.init();
var http = require('http');
http.createServer(CloudServer._controller).listen(process.env.PORT ||
process.env.VCAP_APP_PORT /* cloudfoundry */ ||
31337,
'0.0.0.0' || '127.0.0.1');
console.log('Cloud Commander server running at http://127.0.0.1:'+
(process.env.PORT===undefined?31337:process.env.PORT));
};
/* Функция создаёт заголовки файлов
* в зависимости от расширения файла
* перед отправкой их клиенту
* @pName - имя файла
* @pGzip - данные сжаты gzip'ом
*/
CloudServer.generateHeaders = function(pName, pGzip){
var lType='';
/* высылаем заголовок в зависимости от типа файла */
/* если расширение у файла css -
* загружаем стили
*/
if(CloudFunc.checkExtension(pName,'css'))
lType='text/css';
/* загружаем js */
else if(CloudFunc.checkExtension(pName,'js'))
lType='text/javascript';
/* загружаем картинки*/
else if(CloudFunc.checkExtension(pName,'png'))
lType='img/png';
/* загружаем json*/
else if(CloudFunc.checkExtension(pName,'json'))
lType='application/json';
else if(CloudFunc.checkExtension(pName,'html'))
lType='text/html';
/* если это неизвестный тип файла -
* высылаем его просто как текст
*/
else lType='text/plain';
return {
'Content-Type': lType+'; charset=UTF-8',
'cache-control': 'max-age='+(31337*21),
'last-modified': new Date().toString(),
'content-encoding': pGzip?'gzip':'',
/* https://developers.google.com/speed/docs/best-practices/caching?hl=ru#LeverageProxyCaching */
'Vary': 'Accept-Encoding'
};
};
/*
* Главная функция, через которую проихсодит
* взаимодействие обмен данными с клиентом
* @req - запрос клиента (Request)
* @res - ответ сервера (Response)
*/
CloudServer._controller=function(pReq, pRes)
{
/* Читаем содержимое папки,
переданное в url
*/
var url = require("url");
var pathname = url.parse(pReq.url).pathname;
console.log('pathname: '+pathname);
/* получаем поддерживаемые браузером кодировки*/
var lAcceptEncoding = pReq.headers['accept-encoding'];
/* запоминаем поддерживает ли браузер
* gzip-сжатие при первом заходе на сайт
*/
if (lAcceptEncoding &&
lAcceptEncoding.match(/\bgzip\b/)){
CloudServer.Gzip=true;
}else
CloudServer.Gzip=false;
/* путь в ссылке, который говорит
* что js отключен
*/
var lNoJS_s=CloudFunc.NOJS;
var lFS_s=CloudFunc.FS;
if(pathname!=='/favicon.ico')
{
console.log("request for " + pathname + " received...");
var lName;
/* если в пути нет информации ни о ФС,
* ни об отсутствии js,
* ни о том, что это корневой
* каталог - загружаем файлы проэкта
*/
console.log(lFS_s+pathname);
if(pathname.indexOf(lFS_s)<0 &&
pathname.indexOf(lNoJS_s)<0 &&
pathname!=='/'){
/* если имена файлов проекта - загружаем их*/
/* убираем слеш и читаем файл с текущец директории*/
lName=Path.basename(pathname);
console.log('reading '+lName);
/* сохраняем указатель на responce и имя */
CloudServer.Responses[lName]=pRes;
/* Берём значение из кэша
* сжатый файл - если gzip-поддерживаеться браузером
* не сжатый - в обратном случае
*/
var lFileData=CloudServer.Cache.get(CloudServer.Gzip?(lName+'_gzip'):lName);
var lReadFileFunc_f=CloudServer.getReadFileFunc(lName);
/* если там что-то есть передаём данные в функцию
* readFile
*/
if(lFileData){
console.log('readed from cache');
/* передаём данные с кэша,
* если gzip включен - сжатые
* в обратном случае - несжатые
*/
lReadFileFunc_f(undefined,lFileData,true);
}
else Fs.readFile(lName,lReadFileFunc_f);
}else{/* если мы имеем дело с файловой системой*/
/* если путь не начинаеться с no-js - значит
* js включен
*/
/* убираем пометку cloud, без которой c9.io
* не работает поскольку путь из двух слешей
* (/fs/no-js/) - очень короткий, нужно
* длиннее
*/
if(pathname.indexOf(lNoJS_s)!=lFS_s.length && pathname!='/'){
CloudServer.NoJS=false;
}else pathname=pathname.replace(lNoJS_s,'');
/* убираем индекс файловой системы */
if(pathname.indexOf(lFS_s)===0){
pathname=pathname.replace(lFS_s,'');
/* если посетитель только зашел на сайт
* no-js будет пустым, как и fs
*/
/* если в пути нету fs - посетитель только зашел на сайт
* загружаем его полностью.
*/
}else CloudServer.NoJS=true;
/* если в итоге путь пустой
* делаем его корневым
*/
if(pathname==='')pathname='/';
RightDir=pathname;
LeftDir=pathname;
//DirContent=fs.readdirSync(LeftDir);
/* если встретиться пробел -
* меня код символа пробела на пробел
*/
LeftDir=CloudFunc.replaceSpaces(LeftDir);
RightDir=CloudFunc.replaceSpaces(RightDir);
/* Проверяем с папкой ли мы имеем дело */
/* читаем сновные данные о файле */
var lStat;
try{
lStat=Fs.statSync(LeftDir);
}catch(error){
console.log(error);
CloudServer.Responses[LeftDir]=pRes;
CloudServer.sendResponse('OK',error.toString(),LeftDir);
}
/* если это каталог -
* читаем его содержимое
*/
try{
/*
* сохраним указатель на response
*/
CloudServer.Responses[CloudServer.INDEX]=pRes;
if(lStat.isDirectory())
Fs.readdir(LeftDir,CloudServer._readDir);
/* отдаём файл */
else if(lStat.isFile()){
CloudServer.Responses[LeftDir]=pRes;
Fs.readFile(LeftDir,CloudServer.getReadFileFunc(LeftDir));
console.log('reading file: '+LeftDir);
}
}catch(error){console.log(error);}
}
}
};
/* Функция читает ссылку или выводит информацию об ошибке*/
CloudServer._readDir=function (pError, pFiles)
{
if(!pError)
{
/* данные о файлах в формате JSON*/
var lJSON=[];
var lJSONFile={};
/* Если мы не в корне добавляем слеш к будующим ссылкам */
if(LeftDir!='/')
{
RightDir+='/';
LeftDir+='/';
}
//DirContent=
pFiles=pFiles.sort();
lJSON[0]={path:LeftDir,size:'dir'};
var fReturnFalse=function returnFalse(){return false;};
for(var i=0;i<pFiles.length;i++)
{
/* Получаем информацию о файле*/
var lStats;
try{
lStats=Fs.statSync(RightDir+pFiles[i]);
}catch(err){
console.log(err);
lStats={
'mode':undefined,
'size':undefined,
'isDirectory':fReturnFalse
};
}
/*
*Переводим права доступа в 8-ричную систему
*/
var lMode=(lStats.mode-0).toString(8);
/* Если папка - выводим пиктограмму папки */
if(lStats.isDirectory())
{
lJSONFile={'name':pFiles[i],'size':'dir','uid':lStats.uid,'mode':lMode};
lJSON[i+1]=lJSONFile;
}
/* В противоположном случае - файла */
else
{
lJSONFile={'name':pFiles[i],'uid':lStats.uid,'size':lStats.size,'mode':lMode};
lJSON[i+1]=lJSONFile;
}
}
/* заголовок ответа сервера */
var lHeader;
var lList;
/* если js недоступен */
/* если javascript отключен вылылаем html-код
* и прописываем соответствующие заголовки
*/
if(CloudServer.NoJS){
var lPanel=CloudFunc.buildFromJSON(lJSON);
lList='<ul id=left class=panel>';
lList+=lPanel;
lList+='</ul>';
lList+='<ul id=right class="panel hidden">';
lList+=lPanel;
lList+='</ul>';
try{
var lIndex;
/* пробуем достать данные из кэша
* с жатием или без, взависимости
* от настроек
*/
var lFileData=CloudServer.Cache.get(CloudServer.INDEX);
/* если их нет там - вычитываем из файла*/
if(!lFileData){
lIndex=Fs.readFileSync(CloudServer.INDEX);
/* и сохраняем в кэш */
CloudServer.Cache.set(CloudServer.INDEX,lIndex);
}else lIndex=lFileData;
/* если выбрана опция минифизировать скрпиты
* меняем в index.html обычный client.js на
* минифицированый
*/
lIndex=lIndex.toString();
CloudServer.Minimize.scriptSize?
lIndex=lIndex.replace('client.js','client.min.js'):'';
lIndex=lIndex.toString().replace('<div id=fm class=no-js>','<div id=fm class=no-js>'+lList);
/* меняем title */
lIndex=lIndex.replace('<title>Cloud Commander</title>',
'<title>'+CloudFunc.setTitle()+'</title>');
/* отображаем панель быстрых клавишь */
lList=lIndex;
/* если браузер поддерживает gzip-сжатие*/
lHeader=CloudServer.generateHeaders('text/html',CloudServer.Gzip);
}catch(error){console.log(error);}
}else{
/* в обычном режиме(когда js включен
* высылаем json-структуру файлов
* с соответствующими заголовками
*/
lList=JSON.stringify(lJSON);
lHeader=CloudServer.generateHeaders('application/json',CloudServer.Gzip);
}
/* если браузер поддерживает gzip-сжатие - сжимаем данные*/
if(CloudServer.Gzip){
Zlib.gzip(lList,CloudServer.getGzipDataFunc(lHeader,CloudServer.INDEX));
}
/* если не поддерживаеться - отсылаем данные без сжатия*/
else
CloudServer.sendResponse(lHeader,lList,CloudServer.INDEX);
}
else
{
console.log(pError);
CloudServer.sendResponse('OK',pError.toString());
}
};
/* Функция генерирует функция считывания файла
* таким образом, что бы у нас было
* имя считываемого файла
* @pName - полное имя файла
*/
CloudServer.getReadFileFunc = function(pName){
/*
* @pError - ошибка
* @pData - данные
* @pFromFile - прочитано с файла bool
*/
var lReadFile=function(pError,pData,pFromCache_b){
if (!pError){
console.log('file ' + pName + ' readed');
/* берём из кэша данные файла
* если их нет в кэше -
* сохраняем
*/
if(!pFromCache_b && CloudServer.Cache.isAllowed)
CloudServer.Cache.set(pName,pData);
/* если кэш есть
* сохраняем его в переменную
* которая до этого будет пустая
* по скольку мы будем вызывать этот метод
* сами, ведь файл уже вычитан
*/
var lHeader=CloudServer.generateHeaders(pName,CloudServer.Gzip);
/* если браузер поддерживает gzip-сжатие - сжимаем данные*/
if(CloudServer.Gzip &&!pFromCache_b){
/* сжимаем содержимое */
Zlib.gzip(pData,CloudServer.getGzipDataFunc(lHeader,pName));
}
else{
/* высылаем несжатые данные */
CloudServer.sendResponse(lHeader,pData,pName);
}
}
else
{
console.log(pError.path);
if(pError.path!='passwd.json')
{
console.log(pError);
CloudServer.sendResponse('OK',pError.toString());
}else{
CloudServer.sendResponse('OK','passwd.json');
}
}
};
return lReadFile;
};
/* Функция получает сжатые данные
* @pHeader - заголовок файла
*/
CloudServer.getGzipDataFunc=function(pHeader,pName){
return function(error,pResult){
if(!error){
/* отправляем сжатые данные
* вместе с заголовком
*/
/* если установлена работа с кэшем
* сохраняем сжатые данные
*/
if(CloudServer.Cache.isAllowed){
/* устанавливаем кєш */
console.log(pName+' gziped');
CloudServer.Cache.set(pName+'_gzip',pResult);
}
CloudServer.sendResponse(pHeader,pResult,pName);
}
else{
console.log(error);
CloudServer.sendResponse(pHeader,error);
}
};
};
/* Функция высылает ответ серверу
* @pHead - заголовок
* @pData - данные
* @pName - имя отсылаемого файла
*/
CloudServer.sendResponse = function(pHead, pData,pName){
/* если у нас есть указатель на responce
* для соответствующего файла -
* высылаем его
*/
var lResponse=CloudServer.Responses[pName];
if(lResponse){
lResponse.writeHead(200,pHead);
lResponse.end(pData);
console.log(pName+' sended');
}
};
CloudServer.start();

316
style.css Normal file
View file

@ -0,0 +1,316 @@
/*
@import url(//fonts.googleapis.com/css?family=Droid+Sans+Mono);
*/
/* Foundation Icons General Enclosed */
@font-face {
font-family: 'FoundationIconsGeneralEnclosed';
src: url('//dl.dropbox.com/u/78163899/mnemonia/fonts/foundation-icons-general-enclosed.woff') format('woff');
font-weight: normal;
font-style: normal;
}
/* символьный шрифт от гитхаба*/
@font-face {
font-family: 'Octicons Regular';
font-style: normal;
font-weight: normal;
src: local('Octicons Regular'), url('//dl.dropbox.com/u/78163899/mnemonia/fonts/octicons-regular-webfont.woff') format('woff');
}
body{
font:16px "Droid Sans Mono";
}
/* убираем элементы,
* которые будут работать только,
* если есть js
*/
.no-js .refresh-icon{
display:none;
}
.menu{
font: 16px 'Octicons Regular';
margin-bottom: 0;
}
.path_icon{
font-family:'FoundationIconsGeneralEnclosed';
font-size:30px;
color: #46A4C3;/*#55BF3F; green*/
text-shadow:black 0 2px 1px;
/* размер иконки и позиция на png-файле*/
width: 15px;
height: 15px;
display: inline-block;
position: relative;
top: 3px;
left: -4px;
}
.path_icon:hover{
/*
color:#45D827;
*/
cursor:pointer;
}
.path_icon:active{
text-shadow:black 0 0 1px;
position: relative;
top: 4px;
}
.icon{
font-family: 'Octicons Regular';
font-size:16px;
width:16px;
height:16px;
display:inline-block;
margin-left:0.5%;
}
.error::before{
content:'\f026';
cursor:default;
color:rgb(222, 41, 41);
position: relative;
top: -3px;
}
.loading{
background:url(//dl.dropbox.com/u/74212301/mnemonia/images/icons/spinner.gif);
position:relative;
top:1px;
}
.error:hover{
color:rgba(222, 41, 41, 0.81);
}
.refresh-icon{
background:url(//dl.dropbox.com/u/78163899/mnemonia/images/icons/panel_refresh.png) no-repeat;
}
.refresh-icon:active{
/*background-position-y: -15px;*/
background:url(//dl.dropbox.com/u/78163899/mnemonia/images/icons/panel_refresh.png) 0 -15px no-repeat;
}
.clear-cache{
background:url(//dl.dropbox.com/u/78163899/mnemonia/images/icons/console_clear.png) -4px -4px no-repeat;
margin-right: 6px;
margin-left: 7px;
}
.clear-cache:active{
/*
background-position-y: -25px;
*/
top:5px;
}
.settings:before{
content:'k';
}
.links{
color:red;
}
.mini-icon {
float: left;
height: 16px;
left: -5px;
margin-left: 6px;
position: relative;
top: 2px;
width: 16px;
/* отступ перед картинкой
* для нормального отображения
* рамки
*/
}
/* freeupex */
.directory{
/*list-style-image*/
background-image:url('//dl.dropbox.com/u/74212301/mnemonia/images/icons/dir.png');
background-repeat: no-repeat;
background-position: 0 0;
}
.text-file{
/*list-style-image*/
background-image:url('//dl.dropbox.com/u/74212301/mnemonia/images/icons/txt.png');
background-repeat: no-repeat;
background-position: 0 0;
}
#fm{
height:90%;
overflow-y: scroll;
}
.fm_header{
font-weight: bold;
}
#path{
margin-left:1.5%;
}
#left{
float:left;
width:90%;
}
/* фон файла, на котором курсор*/
.current-file{
border: 2px solid rgba(49, 123, 249, .40);
}
.selected-file{
background-color: rgba(49, 123, 249, .40);
color:white;
}
#right{
float:right;
}
.panel{
display: table;
width:50%;
}
#keyspanel{
text-align: center;
}
/* информация о файлах и папках*/
.name{
float: left;
width: 37%;
}
.size{
float:left;
width:16%;
/* Ставим выравнивание,
* что бы размер был в
* одной ровной колонке
*/
text-align: right;
/* Ставим отступ, что бы
* size не налазил на uid
* (owner)
*/
margin-right: 27px;
}
.owner{
}
.mode{
float: right;
width: 25%;
}
ul,li{list-style-type:none;}
button{
width:10%;
}
a{
text-decoration:none;
}
a:hover, a:active { cursor:pointer;outline: 0; color: #06e; }
a:focus { outline: thin dotted; }
/* Если размер окна очень маленький
* располагаем имя и атрибуты файла
* друг-под-другом
*/
/* responsive design */
@media only screen and (max-width: 600px){
#left{
width:90% !important;
}
.panel >li{
margin:10px;
}
/* если правая панель не помещаеться - прячем её */
#right{
display:none;
}
/* текущий файл под курсором */
.current-file{
background-color: rgba(49, 123, 249, .40);
color:white;
}
/* делаем иконки под курсом белыми*/
.current-file > .mini-icon{
color:white;
}
.current-file > .text-file::before{
color:white;
}
/* меняем иконки на шрифтовые*/
.mini-icon {
font: 60px 'Octicons Regular';
width: 40%;
height: 0;
margin-left: 0;
float: right;
position: relative;
top: -17px;
color: rgba(246, 224, 124, 0.56);
}
.directory::before{
content: '\f216';
}
.text-file::before{
color: rgba(26, 224, 124, 0.56);
content: '\f211';
}
.text-file{
background-image:none;
}
/* убираем заголовок*/
.fm_header{
display:none;
}
.mode,.size,.owner{
/* располагаем элементы
* один под другим
*/
display: table;
float: none;
width: 0;
text-align: left;
}
/* выводим заголовки рядом с полями */
.name::before{
content: 'name:';
font-weight: bold;
font-size: 13px;
}
.mode::before{
content: 'mode:';
font-weight: bold;
font-size: 13px;
}
.size::before{
content: 'size:';
font-weight: bold;
font-size: 13px;
}
.owner::before{
content: 'owner:';
font-weight: bold;
font-size: 13px;
}
.name{
float: none;
width:100%;
font-size: 18px;
}
}
@media only screen and (min-width: 601px) and (max-width: 767px){
#left{
width:90% !important;
}
#right{
display:none;
}
}
@media only screen and (min-width:767px) and (max-width: 1060px){
#left{
width:90% !important;
}
/* если правая панель не помещаеться - прячем её */
#right{
display:none;
}
}