mirror of
https://github.com/coderaiser/cloudcmd.git
synced 2026-01-23 10:45:47 +00:00
minor changes
This commit is contained in:
commit
42d0570ef0
27 changed files with 7815 additions and 2042 deletions
11
.travis.yml
11
.travis.yml
|
|
@ -1,11 +0,0 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
#- 0.4 #jshint fails to install (no gzip, jshint test not working out)
|
||||
- 0.6
|
||||
- 0.8
|
||||
notifications:
|
||||
webhooks:
|
||||
#http://requestb.in/12h5bl71
|
||||
urls:
|
||||
# http://webhooks.jit.su/deploy ##
|
||||
- http://webhooks.nodejitsu.com/1/deploy
|
||||
40
ChangeLog
40
ChangeLog
|
|
@ -1,3 +1,43 @@
|
|||
2012.07.*, Version 0.1.5
|
||||
|
||||
* Added tab support.
|
||||
|
||||
* Fixed bug with Path links.
|
||||
|
||||
* From now CodeMirror js files loads, when f4 key pressed.
|
||||
|
||||
* Fixed bug with showing CodeMirror after first show.
|
||||
|
||||
* Added ability to read files in CodeMirror.
|
||||
|
||||
* Added ability rename files.
|
||||
|
||||
* Fixed bug with CodeMirror vertical scroll bar.
|
||||
|
||||
* Added loading image when file going to be edited.
|
||||
|
||||
* All sync functions changed to async equivalents.
|
||||
|
||||
* On f4 key pressed when current-file is folder
|
||||
CodeMirror opens json data of folder.
|
||||
|
||||
* Fixed bug with response when we have no rihgts
|
||||
read dir server sends 404 response for now,
|
||||
not 200 ok.
|
||||
|
||||
* Fixed bug with forming error of loading dir.
|
||||
Was : "not found".
|
||||
Now : "Error: EACCES, readdir '/root'"
|
||||
|
||||
* Fixed bug with showing loading spinner, when f4 key
|
||||
pressed couple times and CodeMirror not loaded fully.
|
||||
|
||||
* Setted readOnly mode, when directory opened in CodeMirror.
|
||||
|
||||
* Added api for getting curent file, getting active
|
||||
and passive panels and show/hide any of panels.
|
||||
|
||||
|
||||
2012.07.27, Version 0.1.4
|
||||
|
||||
* Added local version of Droids font for offline mode
|
||||
|
|
|
|||
829
client.js
829
client.js
|
|
@ -1,829 +0,0 @@
|
|||
/* Функция которая возвратит обьект 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 */
|
||||
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
|
||||
};
|
||||
|
||||
/*
|
||||
* Обьект для работы с кэшем
|
||||
* в него будут включены функции для
|
||||
* работы с 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 */
|
||||
Util.jsload(CloudClient.LIBDIRCLIENT+'keyBinding.js',function(){
|
||||
CloudCommander.keyBinding();
|
||||
});
|
||||
});
|
||||
|
||||
/* function loads and shows editor */
|
||||
CloudClient.Editor = (function(){
|
||||
/* loading CloudMirror plagin */
|
||||
Util.jsload(CloudClient.LIBDIRCLIENT +
|
||||
'editor.js',{
|
||||
onload:(function(){
|
||||
CloudCommander.Editor.Keys();
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
/* function loads and shows viewer */
|
||||
CloudClient.Viewer = (function(){
|
||||
Util.jsload(CloudClient.LIBDIRCLIENT +
|
||||
'viewer.js',{
|
||||
onload: (function(){
|
||||
CloudCommander.Viewer.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){
|
||||
/* если <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].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);
|
||||
|
||||
/* получаем все элементы выделенной папки*/
|
||||
/* при этом, если мы нажали обновить
|
||||
* или <Ctrl>+R - ссылок мы ненайдём
|
||||
* и заходить не будем
|
||||
*/
|
||||
var lA=this.getElementsByTagName('a');
|
||||
/* если нажали на ссылку на верхний каталог*/
|
||||
if(lA.length>0 && lA[0].textContent==='..' &&
|
||||
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;
|
||||
}
|
||||
}
|
||||
/* если мы попали сюда с энтера*/
|
||||
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 = lCurrentFile[0].parentElement;
|
||||
|
||||
/* убираем слэш с имени каталога*/
|
||||
pDirName=pDirName.replace('/','');
|
||||
|
||||
var lRootDir = document.getElementById(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{
|
||||
document.getElementsByClassName('path')[0].getElementsByTagName('a')[0].appendChild(LoadingImage);
|
||||
LoadingImage.className+=' hidden'; /* прячем её */
|
||||
}catch(error){console.log(error);}
|
||||
ErrorImage=CloudClient._images.error();
|
||||
|
||||
/* устанавливаем размер высоты таблицы файлов
|
||||
* исходя из размеров разрешения экрана
|
||||
*/
|
||||
|
||||
/* выделяем строку с первым файлом */
|
||||
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','');
|
||||
|
||||
/* формируем и округляем высоту экрана
|
||||
* при разрешениии 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=document.getElementById('clear-cache');
|
||||
if(lClearcache)lClearcache.onclick=CloudClient.Cache.clear;
|
||||
|
||||
/* меняем ссылки на ajax-запросы */
|
||||
var lPanel=document.getElementById(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<a.length;i++)
|
||||
{
|
||||
/* если ссылка на папку, а не файл */
|
||||
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);
|
||||
/* устанавливаем обработчики на строку на одинарное и
|
||||
* двойное нажатие на левую кнопку мышки
|
||||
*/
|
||||
else{
|
||||
try{
|
||||
var lLi = a[i].parentElement.parentElement;
|
||||
|
||||
lLi.onclick = CloudClient._setCurrent();
|
||||
lLi.ondblclick = CloudClient._loadDir(link);
|
||||
lLi.id = (a[i].title ? a[i].title : a[i].textContent) +
|
||||
'(' + pPanelID + ')';
|
||||
}catch(error){console.log(error);}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Функция загружает json-данные о Файловой Системе
|
||||
* через ajax-запрос.
|
||||
* @path - каталог для чтения
|
||||
* @pNeedRefresh - необходимость обновить данные о каталоге
|
||||
*/
|
||||
CloudClient._ajaxLoad=function(path, pNeedRefresh)
|
||||
{
|
||||
/* Отображаем красивые пути */
|
||||
/* added supporting of russian language */
|
||||
var lPath=decodeURI(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');
|
||||
} else
|
||||
ErrorImage.title = jqXHR.responseText;
|
||||
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);
|
||||
};
|
||||
|
||||
/*
|
||||
* Function gets id by src
|
||||
* from http://domain.com/1.js to
|
||||
* 1_js
|
||||
*/
|
||||
CloudClient._getIdBySrc = function(pSrc){
|
||||
var lID=pSrc.replace(pSrc.substr(pSrc,
|
||||
pSrc.lastIndexOf('/')+1),
|
||||
'');
|
||||
/* убираем точку*/
|
||||
return lID.replace('.','_');
|
||||
};
|
||||
|
||||
/*
|
||||
* Функция создаёт элемент и
|
||||
* загружает файл с src.
|
||||
* @pName - название тэга
|
||||
* @pSrc - путь к файлу
|
||||
* @pFunc - обьект, содержаий одну из функций
|
||||
* или сразу две onload и onerror
|
||||
* {onload: function(){}, onerror: function();}
|
||||
* @pStyle - стиль
|
||||
* @pId - id
|
||||
* @pElement - элемент, дочерним которо будет этот
|
||||
* @pParams_o = {name: '', src: ' ',func: '', style: '', id: '', element: '',
|
||||
async: false}
|
||||
*/
|
||||
CloudClient._anyload = function(pParams_o)
|
||||
{
|
||||
/* убираем путь к файлу, оставляя только название файла */
|
||||
var lID = pParams_o.id;
|
||||
var lSrc = pParams_o.src;
|
||||
var lFunc = pParams_o.func;
|
||||
var lAsync = pParams_o.async;
|
||||
|
||||
if(!lID){
|
||||
lID = this._getIdBySrc(lSrc);
|
||||
}
|
||||
var element = document.getElementById(lID);
|
||||
/* если скрипт еще не загружен */
|
||||
if(!element)
|
||||
{
|
||||
element = document.createElement(pParams_o.name);
|
||||
/* if working with external css
|
||||
* using href in any other case
|
||||
* using src
|
||||
*/
|
||||
pParams_o.name === 'link' ?
|
||||
element.href = lSrc
|
||||
: element.src = lSrc;
|
||||
element.id=lID;
|
||||
|
||||
|
||||
/* if passed arguments function
|
||||
* then it's onload by default
|
||||
*/
|
||||
if(pParams_o.func)
|
||||
if(typeof lFunc === 'function'){
|
||||
element.onload = lFunc;
|
||||
/* if object - then onload or onerror */
|
||||
}else if (typeof lFunc === 'object') {
|
||||
if(lFunc.onload &&
|
||||
typeof lFunc.onload === 'function')
|
||||
element.onload = lFunc.onload;
|
||||
|
||||
if(lFunc.onerror &&
|
||||
typeof lFunc.onerror === 'function')
|
||||
element.onerror = (function(){
|
||||
(pParams_o.element || document.body)
|
||||
.removeChild(element);
|
||||
|
||||
lFunc.onerror();
|
||||
});
|
||||
}
|
||||
|
||||
if(pParams_o.style){
|
||||
element.style.cssText=pParams_o.style;
|
||||
}
|
||||
|
||||
if(lAsync || lAsync === undefined)
|
||||
element.async = true;
|
||||
|
||||
(pParams_o.element || document.body).appendChild(element);
|
||||
}
|
||||
/* если js-файл уже загружен
|
||||
* запускаем функцию onload
|
||||
*/
|
||||
else if(lFunc && typeof lFunc==='function'){
|
||||
try{
|
||||
lFunc();
|
||||
}catch(error){console.log(error);}
|
||||
}
|
||||
return element;
|
||||
};
|
||||
|
||||
/* Функция загружает js-файл */
|
||||
CloudClient.jsload = function(pSrc,pFunc,pStyle,pId,pAsync)
|
||||
{
|
||||
CloudClient._anyload({
|
||||
name : 'script',
|
||||
src : pSrc,
|
||||
func : pFunc,
|
||||
stle : pStyle,
|
||||
id : pId,
|
||||
async: pAsync
|
||||
});
|
||||
};
|
||||
/* Функция создаёт елемент style и записывает туда стили
|
||||
* @pParams_o - структура параметров, заполняеться таким
|
||||
* образом: {src: ' ',func: '', id: '', element: '', inner: ''}
|
||||
* все параметры опциональны
|
||||
*/
|
||||
CloudClient.cssSet = function(pParams_o){
|
||||
pParams_o.name = 'style';
|
||||
pParams_o.element = pParams_o.element || document.head;
|
||||
var lElem=CloudClient._anyload(pParams_o);
|
||||
|
||||
pParams_o.inner &&
|
||||
(lElem.innerHTML = pParams_o.inner);
|
||||
};
|
||||
/* Function loads external css files
|
||||
* @pParams_o - структура параметров, заполняеться таким
|
||||
* образом: {src: ' ',func: '', id: '', element: '', inner: ''}
|
||||
* все параметры опциональны
|
||||
*/
|
||||
CloudClient.cssLoad = function(pParams_o){
|
||||
pParams_o.name = 'link';
|
||||
pParams_o.element = pParams_o.element || document.head;
|
||||
var lElem=CloudClient._anyload(pParams_o);
|
||||
|
||||
lElem &&
|
||||
(lElem.rel = 'stylesheet');
|
||||
|
||||
pParams_o.inner &&
|
||||
(lElem.innerHTML = pParams_o.inner);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Функция генерирует JSON из html-таблицы файлов и
|
||||
* используеться при первом заходе в корень
|
||||
*/
|
||||
CloudClient._getJSONfromFileTable=function()
|
||||
{
|
||||
var lLeft=document.getElementById('left');
|
||||
var lPath=document.getElementsByClassName('path')[0].textContent;
|
||||
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];
|
||||
lName &&
|
||||
(lName = lName.getElementsByTagName('a'));
|
||||
/* if found link to folder
|
||||
* cheking is it a full name
|
||||
* or short
|
||||
*/
|
||||
/* if short we got title
|
||||
* if full - getting textConent
|
||||
*/
|
||||
lName.length &&
|
||||
(lName = lName[0]);
|
||||
lName.title &&
|
||||
(lName = lName.title) ||
|
||||
(lName = lName.textContent);
|
||||
|
||||
/* если это папка - выводим слово dir вместо размера*/
|
||||
var lSize=lIsDir?'dir':lLI[i].getElementsByClassName('size')[0].textContent;
|
||||
var lMode=lLI[i].getElementsByClassName('mode')[0].textContent;
|
||||
/* переводим права доступа в цыфровой вид
|
||||
* для хранения в 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',{
|
||||
onload: function(){
|
||||
/* сохраняем переменную jQuery себе в область видимости */
|
||||
document.getElementsByClassName=function(pClassName){
|
||||
return window.jQuery('.'+pClassName)[0];
|
||||
};
|
||||
},
|
||||
onerror: function(){
|
||||
CloudClient.jsload(CloudClient.LIBDIRCLIENT + 'jquery.js',
|
||||
function(){
|
||||
document.getElementsByClassName=function(pClassName){
|
||||
return window.jQuery('.'+pClassName)[0];
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return CloudClient;
|
||||
})();
|
||||
|
||||
try{
|
||||
window.onload=function(){
|
||||
'use strict';
|
||||
/* базовая инициализация*/
|
||||
CloudCommander.init();
|
||||
/* привязываем клавиши к функциям */
|
||||
CloudCommander.keyBinding();
|
||||
CloudCommander.Editor();
|
||||
CloudCommander.Viewer();
|
||||
};
|
||||
}
|
||||
catch(err){}
|
||||
|
|
@ -1,121 +1,185 @@
|
|||
var CloudCommander, CodeMirror;
|
||||
var CloudCommander, CloudFunc, CodeMirror;
|
||||
/* object contains editors CodeMirror
|
||||
* and later will be Ace
|
||||
*/
|
||||
CloudCommander.Editor = {};
|
||||
CloudCommander.Editor.CodeMirror = {
|
||||
load: (function(){ /* function loads CodeMirror js and css files */
|
||||
|
||||
/* function shows editor */
|
||||
var showEditor = function (pParent){
|
||||
return function(){
|
||||
if (!document.getElementById('CloudEditor')) {
|
||||
var lEditor=document.createElement('div');
|
||||
lEditor.id ='CloudEditor';
|
||||
lEditor.className = 'hidden';
|
||||
var lFM = document.getElementById('fm');
|
||||
|
||||
if(lFM){
|
||||
lFM.appendChild(lEditor);
|
||||
|
||||
CodeMirror(lEditor,{
|
||||
mode : "xml",
|
||||
htmlMode : true,
|
||||
theme : 'night',
|
||||
lineNumbers : true,
|
||||
//переносим длинные строки
|
||||
lineWrapping: true,
|
||||
extraKeys: {
|
||||
//Сохранение
|
||||
"Esc": pParent.hide(pParent)
|
||||
}
|
||||
});
|
||||
}else console.log('Error. Something went wrong FM not found');
|
||||
}
|
||||
};
|
||||
};
|
||||
/* function loads css files
|
||||
* of CodeMirror
|
||||
*/
|
||||
var loadAll = function(pParent) {
|
||||
return function(){
|
||||
CloudCommander.cssLoad({
|
||||
src : 'http://codemirror.net/lib/codemirror.css'
|
||||
CloudCommander.Editor.CodeMirror = new CloudCommander.Util();
|
||||
|
||||
/* indicator says CodeMirror still loads */
|
||||
CloudCommander.Editor.CodeMirror.loading = false;
|
||||
|
||||
/* function loads CodeMirror js and css files */
|
||||
CloudCommander.Editor.CodeMirror.load = (function(pParent){
|
||||
/* function shows editor */
|
||||
var createEditorDiv = function(){
|
||||
if (!pParent.getById('CloudEditor')) {
|
||||
var lFM = document.getElementById('fm');
|
||||
if(lFM)
|
||||
pParent.anyload({
|
||||
name : 'div',
|
||||
id : 'CloudEditor',
|
||||
element : lFM
|
||||
});
|
||||
|
||||
CloudCommander.cssLoad({
|
||||
src : 'http://codemirror.net/theme/night.css'
|
||||
});
|
||||
|
||||
CloudCommander.cssSet({id:'editor',
|
||||
inner : '.CodeMirror{' +
|
||||
'font-family:\'Droid Sans Mono\';' +
|
||||
'font-size:15px;' +
|
||||
'resize:vertical;' +
|
||||
'padding:20px;' +
|
||||
'}' +
|
||||
'.CodeMirror-scroll{' +
|
||||
'height: 660px;' +
|
||||
'}' +
|
||||
'.CodeMirror-scrollbar{' +
|
||||
'overflow-y:auto' +
|
||||
'}'
|
||||
});
|
||||
|
||||
CloudCommander.jsload('http://codemirror.net/mode/xml/xml.js',
|
||||
showEditor(pParent));
|
||||
};
|
||||
else
|
||||
console.log('Error. Something went wrong FM not found');
|
||||
|
||||
pParent.show();
|
||||
}
|
||||
};
|
||||
/* function loads css files
|
||||
* of CodeMirror
|
||||
*/
|
||||
var loadAll = function(pParent) {
|
||||
return function(){
|
||||
pParent.cssLoad({
|
||||
src : 'lib/client/editor/codemirror/pack/codemirror.pack.css'
|
||||
});
|
||||
|
||||
pParent.cssLoad({
|
||||
src : 'lib/client/editor/codemirror/pack/night.pack.css'
|
||||
});
|
||||
|
||||
pParent.cssSet({id:'editor',
|
||||
inner : '.CodeMirror{' +
|
||||
'font-family :\'Droid Sans Mono\';' +
|
||||
'font-size :15px;' +
|
||||
'padding :20px;' +
|
||||
'}' +
|
||||
'.CodeMirror-scroll{' +
|
||||
'height : 660px;' +
|
||||
'}'
|
||||
});
|
||||
|
||||
pParent.jsload('lib/client/editor/' +
|
||||
'codemirror/pack/javascript.pack.js',
|
||||
createEditorDiv);
|
||||
};
|
||||
|
||||
/* load CodeMirror main module */
|
||||
CloudCommander.jsload('http://codemirror.net/lib/codemirror.js', loadAll(this));
|
||||
}),
|
||||
};
|
||||
|
||||
show : (function(){ /* function shows CodeMirror editor */
|
||||
/* if CloudEditor is not loaded - loading him */
|
||||
document.getElementById('CloudEditor') ||
|
||||
this.load();
|
||||
/* removing keyBinding if set */
|
||||
CloudCommander.keyBinded = false;
|
||||
/* load CodeMirror main module */
|
||||
pParent.jsload('lib/client/editor/' +
|
||||
'codemirror/pack/codemirror.pack.js',
|
||||
loadAll(this));
|
||||
});
|
||||
|
||||
/* function shows CodeMirror editor */
|
||||
CloudCommander.Editor.CodeMirror.show = (function(){
|
||||
/* if CloudEditor is not loaded - loading him */
|
||||
if(!this.getById('CloudEditor'))
|
||||
return this.load(this);
|
||||
|
||||
/* if CodeMirror function show already
|
||||
* called do not call it again
|
||||
* if f4 key pressed couple times
|
||||
*/
|
||||
if(this.loading)
|
||||
return;
|
||||
|
||||
var lLeft = this.getById('left');
|
||||
var lCloudEditor = this.getById('CloudEditor');
|
||||
|
||||
lLeft &&
|
||||
(lLeft.className = 'panel hidden');
|
||||
/* when folder view
|
||||
* is no need to edit
|
||||
* data
|
||||
*/
|
||||
var lReadOnly = false;
|
||||
|
||||
var lParent = this;
|
||||
var initCodeMirror_f = function(pValue){
|
||||
CodeMirror(lCloudEditor,{
|
||||
mode : 'javascript',
|
||||
value : pValue,
|
||||
theme : 'night',
|
||||
lineNumbers : true,
|
||||
//переносим длинные строки
|
||||
lineWrapping: false,
|
||||
autofocus : true,
|
||||
extraKeys: {
|
||||
//Сохранение
|
||||
"Esc": lParent.hide(lParent)
|
||||
},
|
||||
readOnly : lReadOnly
|
||||
});
|
||||
};
|
||||
|
||||
var lCloudEditor = this.getById('CloudEditor');
|
||||
|
||||
var lCurrent = this.getCurrentFile();
|
||||
var lA;
|
||||
|
||||
/* getting link */
|
||||
lA = lCurrent.getElementsByTagName('a');
|
||||
if(!lA.length)
|
||||
return console.log('Error:' +
|
||||
'can not find links in current file');
|
||||
|
||||
lA = lA[0].href;
|
||||
|
||||
lCloudEditor &&
|
||||
(lCloudEditor.className = '');
|
||||
}),
|
||||
hide : (function(pParent) {
|
||||
return function(){
|
||||
CloudCommander.keyBinded = true;
|
||||
|
||||
var lLeft = pParent.getById('left');
|
||||
var lCloudEditor = pParent.getById('CloudEditor');
|
||||
|
||||
lCloudEditor &&
|
||||
(lCloudEditor.className = 'hidden');
|
||||
|
||||
lLeft &&
|
||||
(lLeft.className = 'panel');
|
||||
};
|
||||
}),
|
||||
getById: function(pId){return document.getElementById(pId);},
|
||||
/* убираем адрес хоста*/
|
||||
lA = '/' + lA.replace(document.location.href,'');
|
||||
|
||||
getPanel: function(){
|
||||
var lCurrent = document.getElementsByClassName('current-file');
|
||||
lCurrent.length &&
|
||||
(lCurrent = lCurrent[0].parentElement);
|
||||
/* checking is this link is to directory */
|
||||
var lSize = lCurrent.getElementsByClassName('size');
|
||||
if(lSize){
|
||||
lSize = lSize[0].textContent;
|
||||
|
||||
return lCurrent && lCurrent.id;
|
||||
/* if directory - load json
|
||||
* not html data
|
||||
*/
|
||||
if (lSize === '<dir>'){
|
||||
if (lA.indexOf(CloudFunc.NOJS) ===
|
||||
CloudFunc.FS.length) {
|
||||
|
||||
lA = lA.replace(CloudFunc.NOJS, '');
|
||||
lReadOnly = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.loading = true;
|
||||
/* reading data from current file */
|
||||
$.ajax({
|
||||
url:lA,
|
||||
error: (function(jqXHR, textStatus, errorThrown){
|
||||
lParent.loading = false;
|
||||
return lParent.Images.showError(jqXHR, textStatus, errorThrown);
|
||||
}),
|
||||
|
||||
success:function(data, textStatus, jqXHR){
|
||||
/* if we got json - show it */
|
||||
if(typeof data === 'object')
|
||||
data = JSON.stringify(data, null, 4);
|
||||
|
||||
initCodeMirror_f(data);
|
||||
|
||||
/* removing keyBinding if set */
|
||||
CloudCommander.keyBinded = false;
|
||||
|
||||
lParent.hidePanel();
|
||||
lParent.Images.hideLoad();
|
||||
|
||||
lParent.loading = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/* function hides CodeMirror editor */
|
||||
CloudCommander.Editor.CodeMirror.hide = (function(pParent) {
|
||||
return function(){
|
||||
CloudCommander.keyBinded = true;
|
||||
pParent.showPanel();
|
||||
|
||||
var lCloudEditor = pParent.getById('CloudEditor');
|
||||
var lCodeMirror = pParent.getByClass('CodeMirror');
|
||||
|
||||
if(lCodeMirror.length)
|
||||
lCloudEditor
|
||||
.removeChild(lCodeMirror[0]);
|
||||
};
|
||||
});
|
||||
|
||||
CloudCommander.Editor.Keys = (function(){
|
||||
"use strict";
|
||||
|
||||
|
||||
/* loading js and css of CodeMirror */
|
||||
CloudCommander.Editor.CodeMirror.load();
|
||||
this.CodeMirror.show(this.CodeMirror);
|
||||
|
||||
var key_event=function(event){
|
||||
|
||||
|
|
@ -134,7 +198,4 @@ CloudCommander.Editor.Keys = (function(){
|
|||
|
||||
else
|
||||
document.onkeypress=key_event;
|
||||
|
||||
/* клавиши назначены*/
|
||||
CloudCommander.keyBinded=true;
|
||||
});
|
||||
169
lib/client/editor/codemirror/codemirror.css
Normal file
169
lib/client/editor/codemirror/codemirror.css
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
.CodeMirror {
|
||||
line-height: 1em;
|
||||
font-family: monospace;
|
||||
|
||||
/* Necessary so the scrollbar can be absolutely positioned within the wrapper on Lion. */
|
||||
position: relative;
|
||||
/* This prevents unwanted scrollbars from showing up on the body and wrapper in IE. */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.CodeMirror-scroll {
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
height: 300px;
|
||||
/* This is needed to prevent an IE[67] bug where the scrolled content
|
||||
is visible outside of the scrolling box. */
|
||||
position: relative;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Vertical scrollbar */
|
||||
.CodeMirror-scrollbar {
|
||||
float: right;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
|
||||
/* This corrects for the 1px gap introduced to the left of the scrollbar
|
||||
by the rule for .CodeMirror-scrollbar-inner. */
|
||||
margin-left: -1px;
|
||||
}
|
||||
.CodeMirror-scrollbar-inner {
|
||||
/* This needs to have a nonzero width in order for the scrollbar to appear
|
||||
in Firefox and IE9. */
|
||||
width: 1px;
|
||||
}
|
||||
.CodeMirror-scrollbar.cm-sb-overlap {
|
||||
/* Ensure that the scrollbar appears in Lion, and that it overlaps the content
|
||||
rather than sitting to the right of it. */
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
float: none;
|
||||
right: 0;
|
||||
min-width: 12px;
|
||||
}
|
||||
.CodeMirror-scrollbar.cm-sb-nonoverlap {
|
||||
min-width: 12px;
|
||||
}
|
||||
.CodeMirror-scrollbar.cm-sb-ie7 {
|
||||
min-width: 18px;
|
||||
}
|
||||
|
||||
.CodeMirror-gutter {
|
||||
position: absolute; left: 0; top: 0;
|
||||
z-index: 10;
|
||||
background-color: #f7f7f7;
|
||||
border-right: 1px solid #eee;
|
||||
min-width: 2em;
|
||||
height: 100%;
|
||||
}
|
||||
.CodeMirror-gutter-text {
|
||||
color: #aaa;
|
||||
text-align: right;
|
||||
padding: .4em .2em .4em .4em;
|
||||
white-space: pre !important;
|
||||
cursor: default;
|
||||
}
|
||||
.CodeMirror-lines {
|
||||
padding: .4em;
|
||||
white-space: pre;
|
||||
cursor: text;
|
||||
}
|
||||
.CodeMirror-lines * {
|
||||
/* Necessary for throw-scrolling to decelerate properly on Safari. */
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.CodeMirror pre {
|
||||
-moz-border-radius: 0;
|
||||
-webkit-border-radius: 0;
|
||||
-o-border-radius: 0;
|
||||
border-radius: 0;
|
||||
border-width: 0; margin: 0; padding: 0; background: transparent;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
padding: 0; margin: 0;
|
||||
white-space: pre;
|
||||
word-wrap: normal;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.CodeMirror-wrap pre {
|
||||
word-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
word-break: normal;
|
||||
}
|
||||
.CodeMirror-wrap .CodeMirror-scroll {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.CodeMirror textarea {
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
.CodeMirror pre.CodeMirror-cursor {
|
||||
z-index: 10;
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
border-left: 1px solid black;
|
||||
border-right: none;
|
||||
width: 0;
|
||||
}
|
||||
.cm-keymap-fat-cursor pre.CodeMirror-cursor {
|
||||
width: auto;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
background: rgba(0, 200, 0, .4);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#6600c800, endColorstr=#4c00c800);
|
||||
}
|
||||
/* Kludge to turn off filter in ie9+, which also accepts rgba */
|
||||
.cm-keymap-fat-cursor pre.CodeMirror-cursor:not(#nonsense_id) {
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||
}
|
||||
.CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {}
|
||||
.CodeMirror-focused pre.CodeMirror-cursor {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
div.CodeMirror-selected { background: #d9d9d9; }
|
||||
.CodeMirror-focused div.CodeMirror-selected { background: #d7d4f0; }
|
||||
|
||||
.CodeMirror-searching {
|
||||
background: #ffa;
|
||||
background: rgba(255, 255, 0, .4);
|
||||
}
|
||||
|
||||
/* Default theme */
|
||||
|
||||
.cm-s-default span.cm-keyword {color: #708;}
|
||||
.cm-s-default span.cm-atom {color: #219;}
|
||||
.cm-s-default span.cm-number {color: #164;}
|
||||
.cm-s-default span.cm-def {color: #00f;}
|
||||
.cm-s-default span.cm-variable {color: black;}
|
||||
.cm-s-default span.cm-variable-2 {color: #05a;}
|
||||
.cm-s-default span.cm-variable-3 {color: #085;}
|
||||
.cm-s-default span.cm-property {color: black;}
|
||||
.cm-s-default span.cm-operator {color: black;}
|
||||
.cm-s-default span.cm-comment {color: #a50;}
|
||||
.cm-s-default span.cm-string {color: #a11;}
|
||||
.cm-s-default span.cm-string-2 {color: #f50;}
|
||||
.cm-s-default span.cm-meta {color: #555;}
|
||||
.cm-s-default span.cm-error {color: #f00;}
|
||||
.cm-s-default span.cm-qualifier {color: #555;}
|
||||
.cm-s-default span.cm-builtin {color: #30a;}
|
||||
.cm-s-default span.cm-bracket {color: #cc7;}
|
||||
.cm-s-default span.cm-tag {color: #170;}
|
||||
.cm-s-default span.cm-attribute {color: #00c;}
|
||||
.cm-s-default span.cm-header {color: blue;}
|
||||
.cm-s-default span.cm-quote {color: #090;}
|
||||
.cm-s-default span.cm-hr {color: #999;}
|
||||
.cm-s-default span.cm-link {color: #00c;}
|
||||
|
||||
span.cm-header, span.cm-strong {font-weight: bold;}
|
||||
span.cm-em {font-style: italic;}
|
||||
span.cm-emstrong {font-style: italic; font-weight: bold;}
|
||||
span.cm-link {text-decoration: underline;}
|
||||
|
||||
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
|
||||
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
|
||||
3229
lib/client/editor/codemirror/codemirror.js
Normal file
3229
lib/client/editor/codemirror/codemirror.js
Normal file
File diff suppressed because it is too large
Load diff
361
lib/client/editor/codemirror/mode/javascript.js
vendored
Normal file
361
lib/client/editor/codemirror/mode/javascript.js
vendored
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
var indentUnit = config.indentUnit;
|
||||
var jsonMode = parserConfig.json;
|
||||
|
||||
// Tokenizer
|
||||
|
||||
var keywords = function(){
|
||||
function kw(type) {return {type: type, style: "keyword"};}
|
||||
var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
|
||||
var operator = kw("operator"), atom = {type: "atom", style: "atom"};
|
||||
return {
|
||||
"if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
|
||||
"return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C,
|
||||
"var": kw("var"), "const": kw("var"), "let": kw("var"),
|
||||
"function": kw("function"), "catch": kw("catch"),
|
||||
"for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
|
||||
"in": operator, "typeof": operator, "instanceof": operator,
|
||||
"true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom
|
||||
};
|
||||
}();
|
||||
|
||||
var isOperatorChar = /[+\-*&%=<>!?|]/;
|
||||
|
||||
function chain(stream, state, f) {
|
||||
state.tokenize = f;
|
||||
return f(stream, state);
|
||||
}
|
||||
|
||||
function nextUntilUnescaped(stream, end) {
|
||||
var escaped = false, next;
|
||||
while ((next = stream.next()) != null) {
|
||||
if (next == end && !escaped)
|
||||
return false;
|
||||
escaped = !escaped && next == "\\";
|
||||
}
|
||||
return escaped;
|
||||
}
|
||||
|
||||
// Used as scratch variables to communicate multiple values without
|
||||
// consing up tons of objects.
|
||||
var type, content;
|
||||
function ret(tp, style, cont) {
|
||||
type = tp; content = cont;
|
||||
return style;
|
||||
}
|
||||
|
||||
function jsTokenBase(stream, state) {
|
||||
var ch = stream.next();
|
||||
if (ch == '"' || ch == "'")
|
||||
return chain(stream, state, jsTokenString(ch));
|
||||
else if (/[\[\]{}\(\),;\:\.]/.test(ch))
|
||||
return ret(ch);
|
||||
else if (ch == "0" && stream.eat(/x/i)) {
|
||||
stream.eatWhile(/[\da-f]/i);
|
||||
return ret("number", "number");
|
||||
}
|
||||
else if (/\d/.test(ch) || ch == "-" && stream.eat(/\d/)) {
|
||||
stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
|
||||
return ret("number", "number");
|
||||
}
|
||||
else if (ch == "/") {
|
||||
if (stream.eat("*")) {
|
||||
return chain(stream, state, jsTokenComment);
|
||||
}
|
||||
else if (stream.eat("/")) {
|
||||
stream.skipToEnd();
|
||||
return ret("comment", "comment");
|
||||
}
|
||||
else if (state.reAllowed) {
|
||||
nextUntilUnescaped(stream, "/");
|
||||
stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
|
||||
return ret("regexp", "string-2");
|
||||
}
|
||||
else {
|
||||
stream.eatWhile(isOperatorChar);
|
||||
return ret("operator", null, stream.current());
|
||||
}
|
||||
}
|
||||
else if (ch == "#") {
|
||||
stream.skipToEnd();
|
||||
return ret("error", "error");
|
||||
}
|
||||
else if (isOperatorChar.test(ch)) {
|
||||
stream.eatWhile(isOperatorChar);
|
||||
return ret("operator", null, stream.current());
|
||||
}
|
||||
else {
|
||||
stream.eatWhile(/[\w\$_]/);
|
||||
var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
|
||||
return (known && state.kwAllowed) ? ret(known.type, known.style, word) :
|
||||
ret("variable", "variable", word);
|
||||
}
|
||||
}
|
||||
|
||||
function jsTokenString(quote) {
|
||||
return function(stream, state) {
|
||||
if (!nextUntilUnescaped(stream, quote))
|
||||
state.tokenize = jsTokenBase;
|
||||
return ret("string", "string");
|
||||
};
|
||||
}
|
||||
|
||||
function jsTokenComment(stream, state) {
|
||||
var maybeEnd = false, ch;
|
||||
while (ch = stream.next()) {
|
||||
if (ch == "/" && maybeEnd) {
|
||||
state.tokenize = jsTokenBase;
|
||||
break;
|
||||
}
|
||||
maybeEnd = (ch == "*");
|
||||
}
|
||||
return ret("comment", "comment");
|
||||
}
|
||||
|
||||
// Parser
|
||||
|
||||
var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true};
|
||||
|
||||
function JSLexical(indented, column, type, align, prev, info) {
|
||||
this.indented = indented;
|
||||
this.column = column;
|
||||
this.type = type;
|
||||
this.prev = prev;
|
||||
this.info = info;
|
||||
if (align != null) this.align = align;
|
||||
}
|
||||
|
||||
function inScope(state, varname) {
|
||||
for (var v = state.localVars; v; v = v.next)
|
||||
if (v.name == varname) return true;
|
||||
}
|
||||
|
||||
function parseJS(state, style, type, content, stream) {
|
||||
var cc = state.cc;
|
||||
// Communicate our context to the combinators.
|
||||
// (Less wasteful than consing up a hundred closures on every call.)
|
||||
cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc;
|
||||
|
||||
if (!state.lexical.hasOwnProperty("align"))
|
||||
state.lexical.align = true;
|
||||
|
||||
while(true) {
|
||||
var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
|
||||
if (combinator(type, content)) {
|
||||
while(cc.length && cc[cc.length - 1].lex)
|
||||
cc.pop()();
|
||||
if (cx.marked) return cx.marked;
|
||||
if (type == "variable" && inScope(state, content)) return "variable-2";
|
||||
return style;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Combinator utils
|
||||
|
||||
var cx = {state: null, column: null, marked: null, cc: null};
|
||||
function pass() {
|
||||
for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
|
||||
}
|
||||
function cont() {
|
||||
pass.apply(null, arguments);
|
||||
return true;
|
||||
}
|
||||
function register(varname) {
|
||||
var state = cx.state;
|
||||
if (state.context) {
|
||||
cx.marked = "def";
|
||||
for (var v = state.localVars; v; v = v.next)
|
||||
if (v.name == varname) return;
|
||||
state.localVars = {name: varname, next: state.localVars};
|
||||
}
|
||||
}
|
||||
|
||||
// Combinators
|
||||
|
||||
var defaultVars = {name: "this", next: {name: "arguments"}};
|
||||
function pushcontext() {
|
||||
if (!cx.state.context) cx.state.localVars = defaultVars;
|
||||
cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
|
||||
}
|
||||
function popcontext() {
|
||||
cx.state.localVars = cx.state.context.vars;
|
||||
cx.state.context = cx.state.context.prev;
|
||||
}
|
||||
function pushlex(type, info) {
|
||||
var result = function() {
|
||||
var state = cx.state;
|
||||
state.lexical = new JSLexical(state.indented, cx.stream.column(), type, null, state.lexical, info);
|
||||
};
|
||||
result.lex = true;
|
||||
return result;
|
||||
}
|
||||
function poplex() {
|
||||
var state = cx.state;
|
||||
if (state.lexical.prev) {
|
||||
if (state.lexical.type == ")")
|
||||
state.indented = state.lexical.indented;
|
||||
state.lexical = state.lexical.prev;
|
||||
}
|
||||
}
|
||||
poplex.lex = true;
|
||||
|
||||
function expect(wanted) {
|
||||
return function expecting(type) {
|
||||
if (type == wanted) return cont();
|
||||
else if (wanted == ";") return pass();
|
||||
else return cont(arguments.callee);
|
||||
};
|
||||
}
|
||||
|
||||
function statement(type) {
|
||||
if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex);
|
||||
if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
|
||||
if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
|
||||
if (type == "{") return cont(pushlex("}"), block, poplex);
|
||||
if (type == ";") return cont();
|
||||
if (type == "function") return cont(functiondef);
|
||||
if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"),
|
||||
poplex, statement, poplex);
|
||||
if (type == "variable") return cont(pushlex("stat"), maybelabel);
|
||||
if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
|
||||
block, poplex, poplex);
|
||||
if (type == "case") return cont(expression, expect(":"));
|
||||
if (type == "default") return cont(expect(":"));
|
||||
if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
|
||||
statement, poplex, popcontext);
|
||||
return pass(pushlex("stat"), expression, expect(";"), poplex);
|
||||
}
|
||||
function expression(type) {
|
||||
if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator);
|
||||
if (type == "function") return cont(functiondef);
|
||||
if (type == "keyword c") return cont(maybeexpression);
|
||||
if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeoperator);
|
||||
if (type == "operator") return cont(expression);
|
||||
if (type == "[") return cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator);
|
||||
if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator);
|
||||
return cont();
|
||||
}
|
||||
function maybeexpression(type) {
|
||||
if (type.match(/[;\}\)\],]/)) return pass();
|
||||
return pass(expression);
|
||||
}
|
||||
|
||||
function maybeoperator(type, value) {
|
||||
if (type == "operator" && /\+\+|--/.test(value)) return cont(maybeoperator);
|
||||
if (type == "operator" || type == ":") return cont(expression);
|
||||
if (type == ";") return;
|
||||
if (type == "(") return cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator);
|
||||
if (type == ".") return cont(property, maybeoperator);
|
||||
if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator);
|
||||
}
|
||||
function maybelabel(type) {
|
||||
if (type == ":") return cont(poplex, statement);
|
||||
return pass(maybeoperator, expect(";"), poplex);
|
||||
}
|
||||
function property(type) {
|
||||
if (type == "variable") {cx.marked = "property"; return cont();}
|
||||
}
|
||||
function objprop(type) {
|
||||
if (type == "variable") cx.marked = "property";
|
||||
if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expression);
|
||||
}
|
||||
function commasep(what, end) {
|
||||
function proceed(type) {
|
||||
if (type == ",") return cont(what, proceed);
|
||||
if (type == end) return cont();
|
||||
return cont(expect(end));
|
||||
}
|
||||
return function commaSeparated(type) {
|
||||
if (type == end) return cont();
|
||||
else return pass(what, proceed);
|
||||
};
|
||||
}
|
||||
function block(type) {
|
||||
if (type == "}") return cont();
|
||||
return pass(statement, block);
|
||||
}
|
||||
function vardef1(type, value) {
|
||||
if (type == "variable"){register(value); return cont(vardef2);}
|
||||
return cont();
|
||||
}
|
||||
function vardef2(type, value) {
|
||||
if (value == "=") return cont(expression, vardef2);
|
||||
if (type == ",") return cont(vardef1);
|
||||
}
|
||||
function forspec1(type) {
|
||||
if (type == "var") return cont(vardef1, forspec2);
|
||||
if (type == ";") return pass(forspec2);
|
||||
if (type == "variable") return cont(formaybein);
|
||||
return pass(forspec2);
|
||||
}
|
||||
function formaybein(type, value) {
|
||||
if (value == "in") return cont(expression);
|
||||
return cont(maybeoperator, forspec2);
|
||||
}
|
||||
function forspec2(type, value) {
|
||||
if (type == ";") return cont(forspec3);
|
||||
if (value == "in") return cont(expression);
|
||||
return cont(expression, expect(";"), forspec3);
|
||||
}
|
||||
function forspec3(type) {
|
||||
if (type != ")") cont(expression);
|
||||
}
|
||||
function functiondef(type, value) {
|
||||
if (type == "variable") {register(value); return cont(functiondef);}
|
||||
if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext);
|
||||
}
|
||||
function funarg(type, value) {
|
||||
if (type == "variable") {register(value); return cont();}
|
||||
}
|
||||
|
||||
// Interface
|
||||
|
||||
return {
|
||||
startState: function(basecolumn) {
|
||||
return {
|
||||
tokenize: jsTokenBase,
|
||||
reAllowed: true,
|
||||
kwAllowed: true,
|
||||
cc: [],
|
||||
lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
|
||||
localVars: parserConfig.localVars,
|
||||
context: parserConfig.localVars && {vars: parserConfig.localVars},
|
||||
indented: 0
|
||||
};
|
||||
},
|
||||
|
||||
token: function(stream, state) {
|
||||
if (stream.sol()) {
|
||||
if (!state.lexical.hasOwnProperty("align"))
|
||||
state.lexical.align = false;
|
||||
state.indented = stream.indentation();
|
||||
}
|
||||
if (stream.eatSpace()) return null;
|
||||
var style = state.tokenize(stream, state);
|
||||
if (type == "comment") return style;
|
||||
state.reAllowed = !!(type == "operator" || type == "keyword c" || type.match(/^[\[{}\(,;:]$/));
|
||||
state.kwAllowed = type != '.';
|
||||
return parseJS(state, style, type, content, stream);
|
||||
},
|
||||
|
||||
indent: function(state, textAfter) {
|
||||
if (state.tokenize != jsTokenBase) return 0;
|
||||
var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
|
||||
if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
|
||||
var type = lexical.type, closing = firstChar == type;
|
||||
if (type == "vardef") return lexical.indented + 4;
|
||||
else if (type == "form" && firstChar == "{") return lexical.indented;
|
||||
else if (type == "stat" || type == "form") return lexical.indented + indentUnit;
|
||||
else if (lexical.info == "switch" && !closing)
|
||||
return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
|
||||
else if (lexical.align) return lexical.column + (closing ? 0 : 1);
|
||||
else return lexical.indented + (closing ? 0 : indentUnit);
|
||||
},
|
||||
|
||||
electricChars: ":{}"
|
||||
};
|
||||
});
|
||||
|
||||
CodeMirror.defineMIME("text/javascript", "javascript");
|
||||
CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
|
||||
326
lib/client/editor/codemirror/mode/xml.js
vendored
Normal file
326
lib/client/editor/codemirror/mode/xml.js
vendored
Normal file
|
|
@ -0,0 +1,326 @@
|
|||
CodeMirror.defineMode("xml", function(config, parserConfig) {
|
||||
var indentUnit = config.indentUnit;
|
||||
var Kludges = parserConfig.htmlMode ? {
|
||||
autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
|
||||
'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
|
||||
'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
|
||||
'track': true, 'wbr': true},
|
||||
implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
|
||||
'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
|
||||
'th': true, 'tr': true},
|
||||
contextGrabbers: {
|
||||
'dd': {'dd': true, 'dt': true},
|
||||
'dt': {'dd': true, 'dt': true},
|
||||
'li': {'li': true},
|
||||
'option': {'option': true, 'optgroup': true},
|
||||
'optgroup': {'optgroup': true},
|
||||
'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
|
||||
'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
|
||||
'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
|
||||
'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
|
||||
'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
|
||||
'rp': {'rp': true, 'rt': true},
|
||||
'rt': {'rp': true, 'rt': true},
|
||||
'tbody': {'tbody': true, 'tfoot': true},
|
||||
'td': {'td': true, 'th': true},
|
||||
'tfoot': {'tbody': true},
|
||||
'th': {'td': true, 'th': true},
|
||||
'thead': {'tbody': true, 'tfoot': true},
|
||||
'tr': {'tr': true}
|
||||
},
|
||||
doNotIndent: {"pre": true},
|
||||
allowUnquoted: true,
|
||||
allowMissing: false
|
||||
} : {
|
||||
autoSelfClosers: {},
|
||||
implicitlyClosed: {},
|
||||
contextGrabbers: {},
|
||||
doNotIndent: {},
|
||||
allowUnquoted: false,
|
||||
allowMissing: false
|
||||
};
|
||||
var alignCDATA = parserConfig.alignCDATA;
|
||||
|
||||
// Return variables for tokenizers
|
||||
var tagName, type;
|
||||
|
||||
function inText(stream, state) {
|
||||
function chain(parser) {
|
||||
state.tokenize = parser;
|
||||
return parser(stream, state);
|
||||
}
|
||||
|
||||
var ch = stream.next();
|
||||
if (ch == "<") {
|
||||
if (stream.eat("!")) {
|
||||
if (stream.eat("[")) {
|
||||
if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
|
||||
else return null;
|
||||
}
|
||||
else if (stream.match("--")) return chain(inBlock("comment", "-->"));
|
||||
else if (stream.match("DOCTYPE", true, true)) {
|
||||
stream.eatWhile(/[\w\._\-]/);
|
||||
return chain(doctype(1));
|
||||
}
|
||||
else return null;
|
||||
}
|
||||
else if (stream.eat("?")) {
|
||||
stream.eatWhile(/[\w\._\-]/);
|
||||
state.tokenize = inBlock("meta", "?>");
|
||||
return "meta";
|
||||
}
|
||||
else {
|
||||
type = stream.eat("/") ? "closeTag" : "openTag";
|
||||
stream.eatSpace();
|
||||
tagName = "";
|
||||
var c;
|
||||
while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c;
|
||||
state.tokenize = inTag;
|
||||
return "tag";
|
||||
}
|
||||
}
|
||||
else if (ch == "&") {
|
||||
var ok;
|
||||
if (stream.eat("#")) {
|
||||
if (stream.eat("x")) {
|
||||
ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
|
||||
} else {
|
||||
ok = stream.eatWhile(/[\d]/) && stream.eat(";");
|
||||
}
|
||||
} else {
|
||||
ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
|
||||
}
|
||||
return ok ? "atom" : "error";
|
||||
}
|
||||
else {
|
||||
stream.eatWhile(/[^&<]/);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function inTag(stream, state) {
|
||||
var ch = stream.next();
|
||||
if (ch == ">" || (ch == "/" && stream.eat(">"))) {
|
||||
state.tokenize = inText;
|
||||
type = ch == ">" ? "endTag" : "selfcloseTag";
|
||||
return "tag";
|
||||
}
|
||||
else if (ch == "=") {
|
||||
type = "equals";
|
||||
return null;
|
||||
}
|
||||
else if (/[\'\"]/.test(ch)) {
|
||||
state.tokenize = inAttribute(ch);
|
||||
return state.tokenize(stream, state);
|
||||
}
|
||||
else {
|
||||
stream.eatWhile(/[^\s\u00a0=<>\"\'\/?]/);
|
||||
return "word";
|
||||
}
|
||||
}
|
||||
|
||||
function inAttribute(quote) {
|
||||
return function(stream, state) {
|
||||
while (!stream.eol()) {
|
||||
if (stream.next() == quote) {
|
||||
state.tokenize = inTag;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return "string";
|
||||
};
|
||||
}
|
||||
|
||||
function inBlock(style, terminator) {
|
||||
return function(stream, state) {
|
||||
while (!stream.eol()) {
|
||||
if (stream.match(terminator)) {
|
||||
state.tokenize = inText;
|
||||
break;
|
||||
}
|
||||
stream.next();
|
||||
}
|
||||
return style;
|
||||
};
|
||||
}
|
||||
function doctype(depth) {
|
||||
return function(stream, state) {
|
||||
var ch;
|
||||
while ((ch = stream.next()) != null) {
|
||||
if (ch == "<") {
|
||||
state.tokenize = doctype(depth + 1);
|
||||
return state.tokenize(stream, state);
|
||||
} else if (ch == ">") {
|
||||
if (depth == 1) {
|
||||
state.tokenize = inText;
|
||||
break;
|
||||
} else {
|
||||
state.tokenize = doctype(depth - 1);
|
||||
return state.tokenize(stream, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
return "meta";
|
||||
};
|
||||
}
|
||||
|
||||
var curState, setStyle;
|
||||
function pass() {
|
||||
for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]);
|
||||
}
|
||||
function cont() {
|
||||
pass.apply(null, arguments);
|
||||
return true;
|
||||
}
|
||||
|
||||
function pushContext(tagName, startOfLine) {
|
||||
var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent);
|
||||
curState.context = {
|
||||
prev: curState.context,
|
||||
tagName: tagName,
|
||||
indent: curState.indented,
|
||||
startOfLine: startOfLine,
|
||||
noIndent: noIndent
|
||||
};
|
||||
}
|
||||
function popContext() {
|
||||
if (curState.context) curState.context = curState.context.prev;
|
||||
}
|
||||
|
||||
function element(type) {
|
||||
if (type == "openTag") {
|
||||
curState.tagName = tagName;
|
||||
return cont(attributes, endtag(curState.startOfLine));
|
||||
} else if (type == "closeTag") {
|
||||
var err = false;
|
||||
if (curState.context) {
|
||||
if (curState.context.tagName != tagName) {
|
||||
if (Kludges.implicitlyClosed.hasOwnProperty(curState.context.tagName.toLowerCase())) {
|
||||
popContext();
|
||||
}
|
||||
err = !curState.context || curState.context.tagName != tagName;
|
||||
}
|
||||
} else {
|
||||
err = true;
|
||||
}
|
||||
if (err) setStyle = "error";
|
||||
return cont(endclosetag(err));
|
||||
}
|
||||
return cont();
|
||||
}
|
||||
function endtag(startOfLine) {
|
||||
return function(type) {
|
||||
if (type == "selfcloseTag" ||
|
||||
(type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(curState.tagName.toLowerCase()))) {
|
||||
maybePopContext(curState.tagName.toLowerCase());
|
||||
return cont();
|
||||
}
|
||||
if (type == "endTag") {
|
||||
maybePopContext(curState.tagName.toLowerCase());
|
||||
pushContext(curState.tagName, startOfLine);
|
||||
return cont();
|
||||
}
|
||||
return cont();
|
||||
};
|
||||
}
|
||||
function endclosetag(err) {
|
||||
return function(type) {
|
||||
if (err) setStyle = "error";
|
||||
if (type == "endTag") { popContext(); return cont(); }
|
||||
setStyle = "error";
|
||||
return cont(arguments.callee);
|
||||
}
|
||||
}
|
||||
function maybePopContext(nextTagName) {
|
||||
var parentTagName;
|
||||
while (true) {
|
||||
if (!curState.context) {
|
||||
return;
|
||||
}
|
||||
parentTagName = curState.context.tagName.toLowerCase();
|
||||
if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) ||
|
||||
!Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
|
||||
return;
|
||||
}
|
||||
popContext();
|
||||
}
|
||||
}
|
||||
|
||||
function attributes(type) {
|
||||
if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);}
|
||||
if (type == "endTag" || type == "selfcloseTag") return pass();
|
||||
setStyle = "error";
|
||||
return cont(attributes);
|
||||
}
|
||||
function attribute(type) {
|
||||
if (type == "equals") return cont(attvalue, attributes);
|
||||
if (!Kludges.allowMissing) setStyle = "error";
|
||||
return (type == "endTag" || type == "selfcloseTag") ? pass() : cont();
|
||||
}
|
||||
function attvalue(type) {
|
||||
if (type == "string") return cont(attvaluemaybe);
|
||||
if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();}
|
||||
setStyle = "error";
|
||||
return (type == "endTag" || type == "selfCloseTag") ? pass() : cont();
|
||||
}
|
||||
function attvaluemaybe(type) {
|
||||
if (type == "string") return cont(attvaluemaybe);
|
||||
else return pass();
|
||||
}
|
||||
|
||||
return {
|
||||
startState: function() {
|
||||
return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, context: null};
|
||||
},
|
||||
|
||||
token: function(stream, state) {
|
||||
if (stream.sol()) {
|
||||
state.startOfLine = true;
|
||||
state.indented = stream.indentation();
|
||||
}
|
||||
if (stream.eatSpace()) return null;
|
||||
|
||||
setStyle = type = tagName = null;
|
||||
var style = state.tokenize(stream, state);
|
||||
state.type = type;
|
||||
if ((style || type) && style != "comment") {
|
||||
curState = state;
|
||||
while (true) {
|
||||
var comb = state.cc.pop() || element;
|
||||
if (comb(type || style)) break;
|
||||
}
|
||||
}
|
||||
state.startOfLine = false;
|
||||
return setStyle || style;
|
||||
},
|
||||
|
||||
indent: function(state, textAfter, fullLine) {
|
||||
var context = state.context;
|
||||
if ((state.tokenize != inTag && state.tokenize != inText) ||
|
||||
context && context.noIndent)
|
||||
return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
|
||||
if (alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
|
||||
if (context && /^<\//.test(textAfter))
|
||||
context = context.prev;
|
||||
while (context && !context.startOfLine)
|
||||
context = context.prev;
|
||||
if (context) return context.indent + indentUnit;
|
||||
else return 0;
|
||||
},
|
||||
|
||||
compareStates: function(a, b) {
|
||||
if (a.indented != b.indented || a.tokenize != b.tokenize) return false;
|
||||
for (var ca = a.context, cb = b.context; ; ca = ca.prev, cb = cb.prev) {
|
||||
if (!ca || !cb) return ca == cb;
|
||||
if (ca.tagName != cb.tagName || ca.indent != cb.indent) return false;
|
||||
}
|
||||
},
|
||||
|
||||
electricChars: "/"
|
||||
};
|
||||
});
|
||||
|
||||
CodeMirror.defineMIME("text/xml", "xml");
|
||||
CodeMirror.defineMIME("application/xml", "xml");
|
||||
if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
|
||||
CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
|
||||
1
lib/client/editor/codemirror/pack/codemirror.pack.css
Normal file
1
lib/client/editor/codemirror/pack/codemirror.pack.css
Normal file
|
|
@ -0,0 +1 @@
|
|||
.CodeMirror{line-height:1em;font-family:monospace;position:relative;overflow:hidden}.CodeMirror-scroll{overflow-x:auto;overflow-y:hidden;height:300px;position:relative;outline:0}.CodeMirror-scrollbar{float:right;overflow-x:hidden;overflow-y:scroll;margin-left:-1px}.CodeMirror-scrollbar-inner{width:1px}.CodeMirror-scrollbar.cm-sb-overlap{position:absolute;z-index:1;float:none;right:0;min-width:12px}.CodeMirror-scrollbar.cm-sb-nonoverlap{min-width:12px}.CodeMirror-scrollbar.cm-sb-ie7{min-width:18px}.CodeMirror-gutter{position:absolute;left:0;top:0;z-index:10;background-color:#f7f7f7;border-right:1px solid #eee;min-width:2em;height:100%}.CodeMirror-gutter-text{color:#aaa;text-align:right;padding:.4em .2em .4em .4em;white-space:pre!important;cursor:default}.CodeMirror-lines{padding:.4em;white-space:pre;cursor:text}.CodeMirror-lines *{pointer-events:none}.CodeMirror pre{-moz-border-radius:0;-webkit-border-radius:0;-o-border-radius:0;border-radius:0;border-width:0;margin:0;padding:0;background:transparent;font-family:inherit;font-size:inherit;padding:0;margin:0;white-space:pre;word-wrap:normal;line-height:inherit;color:inherit}.CodeMirror-wrap pre{word-wrap:break-word;white-space:pre-wrap;word-break:normal}.CodeMirror-wrap .CodeMirror-scroll{overflow-x:hidden}.CodeMirror textarea{outline:0!important}.CodeMirror pre.CodeMirror-cursor{z-index:10;position:absolute;visibility:hidden;border-left:1px solid black;border-right:0;width:0}.cm-keymap-fat-cursor pre.CodeMirror-cursor{width:auto;border:0;background:transparent;background:rgba(0,200,0,.4);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#6600c800, endColorstr=#4c00c800)}.cm-keymap-fat-cursor pre.CodeMirror-cursor:not(#nonsense_id){filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite{}.CodeMirror-focused pre.CodeMirror-cursor{visibility:visible}div.CodeMirror-selected{background:#d9d9d9}.CodeMirror-focused div.CodeMirror-selected{background:#d7d4f0}.CodeMirror-searching{background:#ffa;background:rgba(255,255,0,.4)}.cm-s-default span.cm-keyword{color:#708}.cm-s-default span.cm-atom{color:#219}.cm-s-default span.cm-number{color:#164}.cm-s-default span.cm-def{color:#00f}.cm-s-default span.cm-variable{color:#000}.cm-s-default span.cm-variable-2{color:#05a}.cm-s-default span.cm-variable-3{color:#085}.cm-s-default span.cm-property{color:#000}.cm-s-default span.cm-operator{color:#000}.cm-s-default span.cm-comment{color:#a50}.cm-s-default span.cm-string{color:#a11}.cm-s-default span.cm-string-2{color:#f50}.cm-s-default span.cm-meta{color:#555}.cm-s-default span.cm-error{color:red}.cm-s-default span.cm-qualifier{color:#555}.cm-s-default span.cm-builtin{color:#30a}.cm-s-default span.cm-bracket{color:#cc7}.cm-s-default span.cm-tag{color:#170}.cm-s-default span.cm-attribute{color:#00c}.cm-s-default span.cm-header{color:blue}.cm-s-default span.cm-quote{color:#090}.cm-s-default span.cm-hr{color:#999}.cm-s-default span.cm-link{color:#00c}span.cm-header,span.cm-strong{font-weight:700}span.cm-em{font-style:italic}span.cm-emstrong{font-style:italic;font-weight:700}span.cm-link{text-decoration:underline}div.CodeMirror span.CodeMirror-matchingbracket{color:#0f0}div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#f22}
|
||||
1
lib/client/editor/codemirror/pack/codemirror.pack.js
Normal file
1
lib/client/editor/codemirror/pack/codemirror.pack.js
Normal file
File diff suppressed because one or more lines are too long
1
lib/client/editor/codemirror/pack/javascript.pack.js
Normal file
1
lib/client/editor/codemirror/pack/javascript.pack.js
Normal file
File diff suppressed because one or more lines are too long
1
lib/client/editor/codemirror/pack/night.pack.css
Normal file
1
lib/client/editor/codemirror/pack/night.pack.css
Normal file
|
|
@ -0,0 +1 @@
|
|||
.cm-s-night{background:#0a001f;color:#f8f8f8}.cm-s-night div.CodeMirror-selected{background:#447!important}.cm-s-night .CodeMirror-gutter{background:#0a001f;border-right:1px solid #aaa}.cm-s-night .CodeMirror-gutter-text{color:#f8f8f8}.cm-s-night .CodeMirror-cursor{border-left:1px solid white!important}.cm-s-night span.cm-comment{color:#6900a1}.cm-s-night span.cm-atom{color:#845dc4}.cm-s-night span.cm-number,.cm-s-night span.cm-attribute{color:#ffd500}.cm-s-night span.cm-keyword{color:#599eff}.cm-s-night span.cm-string{color:#37f14a}.cm-s-night span.cm-meta{color:#7678e2}.cm-s-night span.cm-variable-2,.cm-s-night span.cm-tag{color:#99b2ff}.cm-s-night span.cm-variable-3,.cm-s-night span.cm-def{color:#fff}.cm-s-night span.cm-error{color:#9d1e15}.cm-s-night span.cm-bracket{color:#8da6ce}.cm-s-night span.cm-comment{color:#6900a1}.cm-s-night span.cm-builtin,.cm-s-night span.cm-special{color:#ff9e59}.cm-s-night span.cm-link{color:#845dc4}
|
||||
1
lib/client/editor/codemirror/pack/xml.pack.js
Normal file
1
lib/client/editor/codemirror/pack/xml.pack.js
Normal file
File diff suppressed because one or more lines are too long
21
lib/client/editor/codemirror/theme/night.css
vendored
Normal file
21
lib/client/editor/codemirror/theme/night.css
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/* Loosely based on the Midnight Textmate theme */
|
||||
|
||||
.cm-s-night { background: #0a001f; color: #f8f8f8; }
|
||||
.cm-s-night div.CodeMirror-selected { background: #447 !important; }
|
||||
.cm-s-night .CodeMirror-gutter { background: #0a001f; border-right: 1px solid #aaa; }
|
||||
.cm-s-night .CodeMirror-gutter-text { color: #f8f8f8; }
|
||||
.cm-s-night .CodeMirror-cursor { border-left: 1px solid white !important; }
|
||||
|
||||
.cm-s-night span.cm-comment { color: #6900a1; }
|
||||
.cm-s-night span.cm-atom { color: #845dc4; }
|
||||
.cm-s-night span.cm-number, .cm-s-night span.cm-attribute { color: #ffd500; }
|
||||
.cm-s-night span.cm-keyword { color: #599eff; }
|
||||
.cm-s-night span.cm-string { color: #37f14a; }
|
||||
.cm-s-night span.cm-meta { color: #7678e2; }
|
||||
.cm-s-night span.cm-variable-2, .cm-s-night span.cm-tag { color: #99b2ff; }
|
||||
.cm-s-night span.cm-variable-3, .cm-s-night span.cm-def { color: white; }
|
||||
.cm-s-night span.cm-error { color: #9d1e15; }
|
||||
.cm-s-night span.cm-bracket { color: #8da6ce; }
|
||||
.cm-s-night span.cm-comment { color: #6900a1; }
|
||||
.cm-s-night span.cm-builtin, .cm-s-night span.cm-special { color: #ff9e59; }
|
||||
.cm-s-night span.cm-link { color: #845dc4; }
|
||||
|
|
@ -1,248 +1,283 @@
|
|||
var CloudCommander;
|
||||
var CloudFunc;
|
||||
CloudCommander.keyBinding=(function(){
|
||||
"use strict";
|
||||
var key_event=function(event){
|
||||
var lCurrentFile;
|
||||
var lName, lTop;
|
||||
/* если клавиши можно обрабатывать*/
|
||||
if(CloudCommander.keyBinded){
|
||||
/* если нажали таб:
|
||||
* переносим курсор на
|
||||
* правую панель, если
|
||||
* мы были на левой и
|
||||
* наоборот
|
||||
*/
|
||||
if(event.keyCode===9){
|
||||
console.log('Tab pressed');
|
||||
try{
|
||||
lCurrentFile=document.getElementsByClassName(CloudCommander.CURRENT_FILE)[0];
|
||||
}catch(error){console.log(error);}
|
||||
}
|
||||
/* навигация по таблице файлов*/
|
||||
/* если нажали клавишу вверх*/
|
||||
else if(event.keyCode===38){
|
||||
/* получаем выдленный файл*/
|
||||
lCurrentFile=document.getElementsByClassName(CloudCommander.CURRENT_FILE);
|
||||
/* если ненайдены выделенные файлы - выходим*/
|
||||
if(lCurrentFile.length===0)return;
|
||||
lCurrentFile=lCurrentFile[0];
|
||||
/* если это строка существет и
|
||||
* если она не заголовок
|
||||
* файловой таблицы
|
||||
*/
|
||||
if(lCurrentFile.previousSibling &&
|
||||
lCurrentFile.previousSibling.className!=='fm_header' ){
|
||||
/* убираем выделение с текущего элемента */
|
||||
lCurrentFile.className='';
|
||||
/* и выделяем предыдущую строку*/
|
||||
lCurrentFile.previousSibling.className = CloudCommander.CURRENT_FILE;
|
||||
|
||||
/* if we on the top of
|
||||
* the screan then
|
||||
* moving down the scroolbar
|
||||
*/
|
||||
lTop = lCurrentFile.previousSibling.offsetTop;
|
||||
lTop %
|
||||
(CloudCommander.HEIGHT -
|
||||
CloudCommander.HEIGHT/10) < 70 &&
|
||||
lCurrentFile.parentElement.scrollByLines(-2);
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
/* если нажали клавишу в низ*/
|
||||
else if(event.keyCode===40){
|
||||
/* получаем выдленный файл*/
|
||||
lCurrentFile=document.getElementsByClassName(CloudCommander.CURRENT_FILE);
|
||||
/* если ненайдены выделенные файлы - выходим*/
|
||||
if(lCurrentFile.length===0)return;
|
||||
lCurrentFile=lCurrentFile[0];
|
||||
/* если это не последняя строка */
|
||||
if(lCurrentFile.nextSibling){
|
||||
/* убираем с него выделение */
|
||||
lCurrentFile.className='';
|
||||
/* выделяем следующую строку*/
|
||||
lCurrentFile.nextSibling.className = CloudCommander.CURRENT_FILE;
|
||||
|
||||
/* if we on the bottom of
|
||||
* the screan then
|
||||
* moving down the scroolbar
|
||||
*/
|
||||
lTop = lCurrentFile.previousSibling.offsetTop;
|
||||
var lHeight = CloudCommander.HEIGHT;
|
||||
var lMod = lTop % (lHeight - lHeight/10) < 70;
|
||||
lTop > (lHeight/10) &&
|
||||
lMod < 70 && lMod > 50 &&
|
||||
console.log(lCurrentFile
|
||||
.parentElement
|
||||
.scrollByLines(2) || '!');
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
/* если нажали клавишу page up или Home
|
||||
* переходим к самому верхнему
|
||||
* элементу
|
||||
*/
|
||||
else if(/*event.keyCode===33 ||*/ event.keyCode===36){
|
||||
lCurrentFile=document.getElementsByClassName(CloudCommander.CURRENT_FILE)[0];
|
||||
/* убираем выделение с текущего файла*/
|
||||
lCurrentFile.className='';
|
||||
/* получаем первый элемент*/
|
||||
lCurrentFile.parentElement.firstElementChild
|
||||
/* пропускаем путь и заголовки столбиков*/
|
||||
.nextElementSibling.nextElementSibling
|
||||
/* выделяем верхий файл */
|
||||
.className=CloudCommander.CURRENT_FILE;
|
||||
}
|
||||
/* если нажали клавишу page down или End
|
||||
* выделяем последний элемент
|
||||
*/
|
||||
else if(/*event.keyCode===34 ||*/ event.keyCode===35){
|
||||
lCurrentFile=document.getElementsByClassName(CloudCommander.CURRENT_FILE)[0];
|
||||
/* снимаем выделение с текущего файла*/
|
||||
lCurrentFile.className='';
|
||||
/* выделяем самый нижний файл */
|
||||
lCurrentFile.parentElement.lastElementChild.className=CloudCommander.CURRENT_FILE;
|
||||
}
|
||||
/* если нажали Enter - открываем папку*/
|
||||
else if(event.keyCode===13){
|
||||
lCurrentFile=document.getElementsByClassName(CloudCommander.CURRENT_FILE);
|
||||
/* если ненайдены выделенные файлы - выходим*/
|
||||
if(!lCurrentFile.length)return;
|
||||
lCurrentFile=lCurrentFile[0];
|
||||
/* из него достаём спан с именем файла*/
|
||||
lName=lCurrentFile.getElementsByClassName('name');
|
||||
/* если нету (что вряд ли) - выходим*/
|
||||
if(!lName)return false;
|
||||
/* достаём все ссылки*/
|
||||
var lATag=lName[0].getElementsByTagName('a');
|
||||
/* если нету - выходим */
|
||||
if(!lATag)return false;
|
||||
|
||||
/* вызываем 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(CloudCommander.CURRENT_FILE);
|
||||
if(lCurrentFile.length>0)lCurrentFile=lCurrentFile[0];
|
||||
/* получаем название файла*/
|
||||
var lSelectedName=lCurrentFile.getElementsByTagName('a')[0].textContent;
|
||||
/* если нашли элемент нажимаем него
|
||||
* а если не можем - нажимаем на
|
||||
* ссылку, на которую повешен 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++){
|
||||
lName=lLi[i].getElementsByTagName('a')[0].textContent;
|
||||
if(lSelectedName.length===lName.length &&
|
||||
!lSelectedName.indexOf(lName)){
|
||||
lLi[i].className=CloudCommander.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');
|
||||
|
||||
/* обработчик нажатий клавиш снят*/
|
||||
CloudCommander.keyBinded=false;
|
||||
}
|
||||
}
|
||||
/* если нажали <alt>+s
|
||||
* устанавливаем все обработчики
|
||||
* нажатий клавиш
|
||||
*/
|
||||
else if(event.keyCode===83 &&
|
||||
event.altKey){
|
||||
/*
|
||||
document.addEventListener('keydown', key_event,false);
|
||||
*/
|
||||
/* обрабатываем нажатия на клавиши*/
|
||||
CloudCommander.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;
|
||||
/* клавиши назначены*/
|
||||
CloudCommander.keyBinded=true;
|
||||
var CloudCommander;
|
||||
var CloudFunc;
|
||||
CloudCommander.keyBinding=(function(){
|
||||
"use strict";
|
||||
|
||||
var Util = new CloudCommander.Util();
|
||||
|
||||
var lTabPanel = {
|
||||
left : 0,
|
||||
right : 0
|
||||
|
||||
};
|
||||
var key_event=function(event){
|
||||
var lCurrentFile;
|
||||
var lName, lTop;
|
||||
/* если клавиши можно обрабатывать*/
|
||||
if(CloudCommander.keyBinded){
|
||||
/* если нажали таб:
|
||||
* переносим курсор на
|
||||
* правую панель, если
|
||||
* мы были на левой и
|
||||
* наоборот
|
||||
*/
|
||||
if(event.keyCode===9){
|
||||
console.log('Tab pressed');
|
||||
|
||||
var lCURRENT_FILE = CloudCommander.CURRENT_FILE;
|
||||
try{
|
||||
lCurrentFile = Util.getByClass(lCURRENT_FILE)[0];
|
||||
|
||||
/* changing parent panel of curent-file */
|
||||
var lId = lCurrentFile.parentElement.id;
|
||||
|
||||
lTabPanel[lId] = lCurrentFile;
|
||||
|
||||
lId = (lId === 'right')?
|
||||
'left':'right';
|
||||
|
||||
if(lTabPanel[lId])
|
||||
lTabPanel[lId].className = lCURRENT_FILE;
|
||||
|
||||
else
|
||||
Util.getById(lId).getElementsByTagName('li')[2]
|
||||
.className = lCURRENT_FILE;
|
||||
|
||||
lCurrentFile.className = '';
|
||||
|
||||
}catch(error){console.log(error);}
|
||||
}
|
||||
/* if f2 pressed */
|
||||
else if(event.keyCode===113){
|
||||
|
||||
}
|
||||
/* if f3 pressed */
|
||||
else if(event.keyCode===114){
|
||||
if (typeof CloudCommander.Viewer === 'function')
|
||||
CloudCommander.Viewer();
|
||||
}
|
||||
/* if alt+f3 pressed */
|
||||
else if(event.keyCode===114 &&
|
||||
event.altKey){
|
||||
if (typeof CloudCommander.Terminal === 'function')
|
||||
CloudCommander.Terminal();
|
||||
}
|
||||
/* if f4 pressed */
|
||||
else if(event.keyCode === 115) {
|
||||
Util.Images.showLoad();
|
||||
|
||||
if (typeof CloudCommander.Editor === 'function')
|
||||
CloudCommander.Editor();
|
||||
}
|
||||
/* навигация по таблице файлов*/
|
||||
/* если нажали клавишу вверх*/
|
||||
else if(event.keyCode===38){
|
||||
/* получаем выдленный файл*/
|
||||
lCurrentFile=Util.getByClass(CloudCommander.CURRENT_FILE);
|
||||
/* если ненайдены выделенные файлы - выходим*/
|
||||
if(lCurrentFile.length===0)return;
|
||||
lCurrentFile=lCurrentFile[0];
|
||||
/* если это строка существет и
|
||||
* если она не заголовок
|
||||
* файловой таблицы
|
||||
*/
|
||||
if(lCurrentFile.previousSibling &&
|
||||
lCurrentFile.previousSibling.className!=='fm_header' ){
|
||||
/* убираем выделение с текущего элемента */
|
||||
lCurrentFile.className='';
|
||||
/* и выделяем предыдущую строку*/
|
||||
lCurrentFile.previousSibling.className = CloudCommander.CURRENT_FILE;
|
||||
|
||||
/* if we on the top of
|
||||
* the screan then
|
||||
* moving down the scroolbar
|
||||
*/
|
||||
lTop = lCurrentFile.previousSibling.offsetTop;
|
||||
lTop %
|
||||
(CloudCommander.HEIGHT -
|
||||
CloudCommander.HEIGHT/10) < 70 &&
|
||||
lCurrentFile.parentElement.scrollByLines(-2);
|
||||
}
|
||||
}
|
||||
/* если нажали клавишу в низ*/
|
||||
else if(event.keyCode===40){
|
||||
/* получаем выдленный файл*/
|
||||
lCurrentFile = Util.getByClass(CloudCommander.CURRENT_FILE);
|
||||
/* если ненайдены выделенные файлы - выходим*/
|
||||
if(lCurrentFile.length===0)return;
|
||||
lCurrentFile=lCurrentFile[0];
|
||||
/* если это не последняя строка */
|
||||
if(lCurrentFile.nextSibling){
|
||||
/* убираем с него выделение */
|
||||
lCurrentFile.className='';
|
||||
/* выделяем следующую строку*/
|
||||
lCurrentFile.nextSibling.className = CloudCommander.CURRENT_FILE;
|
||||
|
||||
/* if we on the bottom of
|
||||
* the screan then
|
||||
* moving down the scroolbar
|
||||
*/
|
||||
lTop = lCurrentFile.previousSibling.offsetTop;
|
||||
var lHeight = CloudCommander.HEIGHT;
|
||||
var lMod = lTop % (lHeight - lHeight/10) < 70;
|
||||
lTop > (lHeight/10) &&
|
||||
lMod < 70 && lMod > 50 &&
|
||||
console.log(lCurrentFile
|
||||
.parentElement
|
||||
.scrollByLines(2) || '!');
|
||||
}
|
||||
}
|
||||
/* если нажали клавишу page up или Home
|
||||
* переходим к самому верхнему
|
||||
* элементу
|
||||
*/
|
||||
else if(/*event.keyCode===33 ||*/ event.keyCode===36){
|
||||
lCurrentFile = Util.getByClass(CloudCommander.CURRENT_FILE)[0];
|
||||
/* убираем выделение с текущего файла*/
|
||||
lCurrentFile.className='';
|
||||
/* получаем первый элемент*/
|
||||
lCurrentFile.parentElement.firstElementChild
|
||||
/* пропускаем путь и заголовки столбиков*/
|
||||
.nextElementSibling.nextElementSibling
|
||||
/* выделяем верхий файл */
|
||||
.className=CloudCommander.CURRENT_FILE;
|
||||
}
|
||||
/* если нажали клавишу page down или End
|
||||
* выделяем последний элемент
|
||||
*/
|
||||
else if(/*event.keyCode===34 ||*/ event.keyCode===35){
|
||||
lCurrentFile = Util.getByClass(CloudCommander.CURRENT_FILE)[0];
|
||||
/* снимаем выделение с текущего файла*/
|
||||
lCurrentFile.className='';
|
||||
/* выделяем самый нижний файл */
|
||||
lCurrentFile.parentElement.lastElementChild.className=CloudCommander.CURRENT_FILE;
|
||||
}
|
||||
/* если нажали Enter - открываем папку*/
|
||||
else if(event.keyCode===13){
|
||||
lCurrentFile = Util.getByClass(CloudCommander.CURRENT_FILE);
|
||||
/* если ненайдены выделенные файлы - выходим*/
|
||||
if(!lCurrentFile.length)return;
|
||||
lCurrentFile=lCurrentFile[0];
|
||||
/* из него достаём спан с именем файла*/
|
||||
lName=lCurrentFile.getElementsByClassName('name');
|
||||
/* если нету (что вряд ли) - выходим*/
|
||||
if(!lName)return false;
|
||||
/* достаём все ссылки*/
|
||||
var lATag=lName[0].getElementsByTagName('a');
|
||||
/* если нету - выходим */
|
||||
if(!lATag)return false;
|
||||
|
||||
/* вызываем 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 = Util
|
||||
.getByClass(CloudFunc.REFRESHICON);
|
||||
|
||||
if(lRefreshIcon)lRefreshIcon = lRefreshIcon[0];
|
||||
if(lRefreshIcon){
|
||||
/* находим файл который сейчас выделен */
|
||||
lCurrentFile = Util
|
||||
.getByClass(CloudCommander.CURRENT_FILE);
|
||||
|
||||
if(lCurrentFile.length > 0)
|
||||
lCurrentFile=lCurrentFile[0];
|
||||
/* получаем название файла*/
|
||||
var lSelectedName = lCurrentFile
|
||||
.getElementsByTagName('a')[0].textContent;
|
||||
/* если нашли элемент нажимаем него
|
||||
* а если не можем - нажимаем на
|
||||
* ссылку, на которую повешен eventHandler
|
||||
* onclick
|
||||
*/
|
||||
if(lRefreshIcon.click)lRefreshIcon.parentElement.click();
|
||||
else lRefreshIcon.parentElement.onclick();
|
||||
|
||||
/* перебираем файлы левой панели
|
||||
* в поисках подсвеченого файла
|
||||
*/
|
||||
var lLeft = Util.getById('left');
|
||||
if(lLeft)
|
||||
CloudCommander._currentToParent(lSelectedName);
|
||||
}
|
||||
}
|
||||
/* если нажали <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 = Util.getById('clear-cache');
|
||||
if(lClearCache && lClearCache.onclick)lClearCache.onclick();
|
||||
}
|
||||
/* если нажали <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');
|
||||
|
||||
/* обработчик нажатий клавиш снят*/
|
||||
CloudCommander.keyBinded=false;
|
||||
}
|
||||
|
||||
event.preventDefault();//запрет на дальнейшее действие
|
||||
}
|
||||
/* если нажали <alt>+s
|
||||
* устанавливаем все обработчики
|
||||
* нажатий клавиш
|
||||
*/
|
||||
else if(event.keyCode===83 &&
|
||||
event.altKey){
|
||||
/*
|
||||
document.addEventListener('keydown', key_event,false);
|
||||
*/
|
||||
/* обрабатываем нажатия на клавиши*/
|
||||
CloudCommander.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;
|
||||
/* клавиши назначены*/
|
||||
CloudCommander.keyBinded=true;
|
||||
});
|
||||
76
lib/client/terminal.js
Normal file
76
lib/client/terminal.js
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
var CloudCommander, jqconsole;
|
||||
/* object contains terminal jqconsole
|
||||
*/
|
||||
CloudCommander.Terminal = {};
|
||||
CloudCommander.Terminal.jqconsole = {
|
||||
load: function(pParent){
|
||||
CloudCommander.cssLoad({
|
||||
src : 'lib/client/terminal/ansi.css'
|
||||
});
|
||||
|
||||
CloudCommander.jsload({
|
||||
src : 'lib/client/terminal/jqconsole-2.7.min.js',
|
||||
func : function(){
|
||||
pParent.init();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
init: (function(){
|
||||
var pConsole = document.getById('terminal');
|
||||
if(!pConsole){
|
||||
CloudCommander.anyload({
|
||||
name : 'div',
|
||||
id :'terminal'
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
show: function(){
|
||||
$(function () {
|
||||
var jqconsole = $('#terminal').jqconsole('Hi\n', '>>>');
|
||||
var startPrompt = function () {
|
||||
// Start the prompt with history enabled.
|
||||
jqconsole.Prompt(true, function (input) {
|
||||
// Output input with the class jqconsole-output.
|
||||
jqconsole.Write(input + '\n', 'jqconsole-output');
|
||||
// Restart the prompt.
|
||||
startPrompt();
|
||||
});
|
||||
};
|
||||
startPrompt();
|
||||
});
|
||||
},
|
||||
|
||||
getById : function(pId){return document.getElementById(pId);},
|
||||
|
||||
getByClass : function(pClass){
|
||||
return document.getElementsByClassName(pClass);
|
||||
}
|
||||
};
|
||||
|
||||
CloudCommander.Terminal.Keys = (function(){
|
||||
"use strict";
|
||||
|
||||
/* loading js and css of CodeMirror */
|
||||
CloudCommander.Editor.Terminal.load(this.jqconsole);
|
||||
|
||||
var key_event=function(event){
|
||||
|
||||
/* если клавиши можно обрабатывать */
|
||||
if(CloudCommander.keyBinded){
|
||||
/* if f4 pressed */
|
||||
if(event.keyCode===114 &&
|
||||
event.altKey){
|
||||
CloudCommander.Terminal.jqconsole.show();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* добавляем обработчик клавишь */
|
||||
if (document.addEventListener)
|
||||
document.addEventListener('keydown', key_event,false);
|
||||
|
||||
else
|
||||
document.onkeypress=key_event;
|
||||
});
|
||||
107
lib/client/terminal/README-v1.md
Normal file
107
lib/client/terminal/README-v1.md
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
#jq-console
|
||||
|
||||
A simple jQuery terminal plugin written in CoffeeScript.
|
||||
|
||||
This project was spawned because of our need for a simple web terminal plugin
|
||||
for the <a href="http://github.com/amasad/jsrepl">jsREPL</a> project. It
|
||||
tries to simulate a low level terminal by providing (almost) raw input/output
|
||||
streams as well as input and output states.
|
||||
|
||||
##Tested Browsers
|
||||
|
||||
The plugin has been tested on the following browsers:
|
||||
|
||||
* IE 8
|
||||
* Chrome 10
|
||||
* Firefox 3.6
|
||||
* Safari 4
|
||||
* Opera 10
|
||||
|
||||
##Getting Started
|
||||
|
||||
###Instantiating
|
||||
|
||||
var jqconsole = $(div).jqconsole(welcomeString);
|
||||
|
||||
* `div` is the div element or selector.
|
||||
* `welcomeString` is the string to be shown when the terminal is first rendered.
|
||||
|
||||
###Configuration
|
||||
|
||||
There isn't much initial configuration needed, because the user must supply
|
||||
options and callbacks with each state change. The only config method is used to
|
||||
create custom shortcuts:
|
||||
|
||||
* `jqconsole.RegisterShortcut`: Registers a callback for a keyboard shortcut.
|
||||
It takes two arguments:
|
||||
|
||||
* `int keyCode`: The code of the key pressing which (when Ctrl is held) will
|
||||
trigger this shortcut.
|
||||
|
||||
* `function callback`: A function called when the shortcut is pressed;
|
||||
"this" will point to the JQConsole object.
|
||||
|
||||
Example:
|
||||
|
||||
// Ctrl+R: resets the console.
|
||||
jqconsole.RegisterShortCut(82, function() {
|
||||
this.Reset();
|
||||
});
|
||||
|
||||
##Usage
|
||||
|
||||
Unlike most terminal plugins, jq-console gives you complete low-level control
|
||||
over the execution; you have to call the appropriate methods to start input
|
||||
or output:
|
||||
|
||||
* `jqconsole.Input`: Asks user for input. It takes three arguments:
|
||||
|
||||
* `bool history_enabled`: Whether this input should use history. If true,
|
||||
the user can select the input from history, and their input will also be
|
||||
added as a new history item.
|
||||
|
||||
* `function result_callback`: A function called with the user's input when
|
||||
the user presses Enter and the input operation is complete.
|
||||
|
||||
* `function multiline_callback`: If specified, this function is called when
|
||||
the user presses Enter to check whether the input should continue to the
|
||||
next line. If this function returns a falsy value, the input operation
|
||||
is completed. Otherwise, input continues and the cursor moves to the next
|
||||
line.
|
||||
|
||||
Example:
|
||||
|
||||
jqconsole.Input(true, function(input) {
|
||||
alert(input);
|
||||
}, function (input) {
|
||||
return /\\$/.test('asdasd \\');
|
||||
});
|
||||
|
||||
* `jqconsole.Write`: Writes the given text to the console in a `<span>`, with an
|
||||
optional class. This is used for output and writing prompt labels. It takes
|
||||
two arguments:
|
||||
|
||||
* `string text`: The text to write.
|
||||
|
||||
* `string cls`: The class to give the span containing the text. Optional.
|
||||
|
||||
Examples:
|
||||
|
||||
jqconsole.Write('>>>', 'prompt')
|
||||
jqconsole.Write(output, 'output')
|
||||
jqconsole.Write(err.message, 'error')
|
||||
|
||||
* `jqconsole.SetPromptText` Sets the text currently in the input prompt. Takes
|
||||
only one parameter:
|
||||
|
||||
* `string text`: The text to put in the prompt.
|
||||
|
||||
Examples:
|
||||
|
||||
jqconsole.SetPromptText('ls')
|
||||
jqconsole.SetPromptText('print [i ** 2 for i in range(10)]')
|
||||
|
||||
##Contributors
|
||||
|
||||
[Max Shawabkeh](http://max99x.com/)
|
||||
[Amjad Masad](http://twitter.com/amjad_masad)
|
||||
678
lib/client/terminal/README.md
Normal file
678
lib/client/terminal/README.md
Normal file
|
|
@ -0,0 +1,678 @@
|
|||
#jq-console
|
||||
|
||||
A jQuery terminal plugin written in CoffeeScript.
|
||||
|
||||
This project was spawned because of our need for a simple web terminal plugin
|
||||
for the <a href="http://repl.it">repl.it</a> project. It tries to simulate a low level terminal by providing (almost)
|
||||
raw input/output streams as well as input and output states.
|
||||
|
||||
Version 2.0 adds baked-in support for rich multi-line prompting and operation
|
||||
queueing.
|
||||
|
||||
NOTE: This info is for jq-console v2.0. For jq-console v1.0 see README-v1.md.
|
||||
|
||||
|
||||
##Tested Browsers
|
||||
|
||||
The plugin has been tested on the following browsers:
|
||||
|
||||
* IE 9-10
|
||||
* Chrome 10-14
|
||||
* Firefox 3.6-6
|
||||
* Opera 11
|
||||
|
||||
|
||||
##Getting Started
|
||||
|
||||
###Echo example
|
||||
|
||||
```css
|
||||
/* The console container element */
|
||||
#console {
|
||||
position: absolute;
|
||||
width: 400px;
|
||||
height: 500px;
|
||||
background-color:black;
|
||||
}
|
||||
/* The inner console element. */
|
||||
.jqconsole {
|
||||
padding: 10px;
|
||||
}
|
||||
/* The cursor. */
|
||||
.jqconsole-cursor {
|
||||
background-color: gray;
|
||||
}
|
||||
/* The cursor color when the console looses focus. */
|
||||
.jqconsole-blurred .jqconsole-cursor {
|
||||
background-color: #666;
|
||||
}
|
||||
/* The current prompt text color */
|
||||
.jqconsole-prompt {
|
||||
color: #0d0;
|
||||
}
|
||||
/* The command history */
|
||||
.jqconsole-old-prompt {
|
||||
color: #0b0;
|
||||
font-weight: normal;
|
||||
}
|
||||
/* The text color when in input mode. */
|
||||
.jqconsole-input {
|
||||
color: #dd0;
|
||||
}
|
||||
/* Previously entered input. */
|
||||
.jqconsole-old-input {
|
||||
color: #bb0;
|
||||
font-weight: normal;
|
||||
}
|
||||
/* The text color of the output. */
|
||||
.jqconsole-output {
|
||||
color: white;
|
||||
}
|
||||
```
|
||||
|
||||
```html
|
||||
<div id="console"></div>
|
||||
<script src="jquery.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="jqconsole.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script>
|
||||
$(function () {
|
||||
var jqconsole = $('#console').jqconsole('Hi\n', '>>>');
|
||||
var startPrompt = function () {
|
||||
// Start the prompt with history enabled.
|
||||
jqconsole.Prompt(true, function (input) {
|
||||
// Output input with the class jqconsole-output.
|
||||
jqconsole.Write(input + '\n', 'jqconsole-output');
|
||||
// Restart the prompt.
|
||||
startPrompt();
|
||||
});
|
||||
};
|
||||
startPrompt();
|
||||
});
|
||||
</script>
|
||||
```
|
||||
<iframe src="demo/echo.html" style="width:400px;height:500px">
|
||||
</iframe>
|
||||
|
||||
###Instantiating
|
||||
|
||||
```javascript
|
||||
$(div).jqconsole(welcomeString, promptLabel, continueLabel);
|
||||
```
|
||||
|
||||
* `div` is the div element or selector. Note that this element must be
|
||||
explicity sized and positioned `absolute` or `relative`.
|
||||
* `welcomeString` is the string to be shown when the terminal is first rendered.
|
||||
* `promptLabel` is the label to be shown before the input when using Prompt().
|
||||
* `continueLabel` is the label to be shown before the continued lines of the
|
||||
input when using Prompt().
|
||||
|
||||
##Configuration
|
||||
|
||||
There isn't much initial configuration needed, because the user must supply
|
||||
options and callbacks with each state change. There are a few config methods
|
||||
provided to create custom shortcuts and change indentation width:
|
||||
|
||||
###jqconsole.RegisterShortcut
|
||||
Registers a callback for a keyboard shortcut.
|
||||
Takes two arguments:
|
||||
|
||||
* __(int|string)__ *keyCode*: The code of the key pressing which (when Ctrl is
|
||||
held) will trigger this shortcut. If a string is provided, the ASCII code
|
||||
of the first character is taken.
|
||||
|
||||
* __function__ *callback*: A function called when the shortcut is pressed;
|
||||
"this" will point to the JQConsole object.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
// Ctrl+R: resets the console.
|
||||
jqconsole.RegisterShortcut('R', function() {
|
||||
this.Reset();
|
||||
});
|
||||
|
||||
###jqconsole.SetIndentWidth
|
||||
Sets the number of spaces inserted when indenting and removed when unindenting.
|
||||
Takes one argument:
|
||||
|
||||
* __int__ *width*: The code of the key pressing which (when Ctrl is held) will
|
||||
trigger this shortcut.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
// Sets the indent width to 4 spaces.
|
||||
jqconsole.SetIndentWidth(4);
|
||||
|
||||
###jqconsole.RegisterMatching
|
||||
Registers an opening and closing characters to match and wraps each of the
|
||||
opening and closing characters with a span with the specified class.
|
||||
Takes one parameters:
|
||||
|
||||
* __char__ *open*: The opening character of a "block".
|
||||
* __char__ *close*: The closing character of a "block".
|
||||
* __string__ *class*: The css class that is applied to the matched characters.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
jqconsole.RegisterMatching('{', '}', 'brackets');
|
||||
|
||||
##Usage
|
||||
|
||||
Unlike most terminal plugins, jq-console gives you complete low-level control
|
||||
over the execution; you have to call the appropriate methods to start input
|
||||
or output:
|
||||
|
||||
###jqconsole.Input:
|
||||
Asks user for input. If another input or prompt operation is currently underway,
|
||||
the new input operation is enqueued and will be called when the current
|
||||
operation and all previously enqueued operations finish. Takes one argument:
|
||||
|
||||
* __function__ *input_callback*: A function called with the user's input when
|
||||
the user presses Enter and the input operation is complete.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
// Echo the input.
|
||||
jqconsole.Input(function(input) {
|
||||
jqconsole.Write(input);
|
||||
});
|
||||
|
||||
|
||||
###jqconsole.Prompt
|
||||
Asks user for input. If another input or prompt operation is currently underway
|
||||
the new prompt operation is enqueued and will be called when the current
|
||||
peration and all previously enqueued operations finish. Takes three arguments:
|
||||
|
||||
* __bool__ *history_enabled*: Whether this input should use history. If true,
|
||||
the user can select the input from history, and their input will also be
|
||||
added as a new history item.
|
||||
|
||||
* __function__ *result_callback*: A function called with the user's input when
|
||||
the user presses Enter and the prompt operation is complete.
|
||||
|
||||
* __function__ *multiline_callback*: If specified, this function is called when
|
||||
the user presses Enter to check whether the input should continue to the
|
||||
next line. The function must return one of the following values:
|
||||
|
||||
* `false`: the input operation is completed.
|
||||
|
||||
* `0`: the input continues to the next line with the current indent.
|
||||
|
||||
* `N` (int): the input continues to the next line, and the current
|
||||
indent is adjusted by `N`, e.g. `-2` to unindent two levels.
|
||||
|
||||
|
||||
* __bool__ *async_multiline*: Whether the multiline callback function should
|
||||
be treated as an asynchronous operation and be passed a continuation
|
||||
function that should be called with one of the return values mentioned
|
||||
above: `false`/`0`/`N`.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
jqconsole.Prompt(true, function(input) {
|
||||
// Alert the user with the command.
|
||||
alert(input);
|
||||
}, function (input) {
|
||||
// Continue if the last character is a backslash.
|
||||
return /\\$/.test(input);
|
||||
});
|
||||
|
||||
###jqconsole.AbortPrompt
|
||||
Aborts the current prompt operation and returns to output mode or the next
|
||||
queued input/prompt operation. Takes no arguments.
|
||||
|
||||
Example:
|
||||
|
||||
jqconsole.Prompt(true, function(input) {
|
||||
alert(input);
|
||||
});
|
||||
// Give the user 2 seconds to enter the command.
|
||||
setTimeout(function() {
|
||||
jqconsole.AbortPrompt();
|
||||
}, 2000);
|
||||
|
||||
###jqconsole.Write
|
||||
Writes the given text to the console in a `<span>`, with an
|
||||
optional class. If a prompt is currently being shown, the text is inserted
|
||||
before it. Takes two arguments:
|
||||
|
||||
* __string__ *text*: The text to write.
|
||||
|
||||
* __string__ *cls*: The class to give the span containing the text. Optional.
|
||||
|
||||
* __bool__ *escape*: Whether the text to write should be html escaped.
|
||||
Optional, defaults to true.
|
||||
|
||||
|
||||
Examples:
|
||||
|
||||
jqconsole.Write(output, 'my-output-class')
|
||||
jqconsole.Write(err.message, 'my-error-class')
|
||||
|
||||
|
||||
###jqconsole.SetPromptText
|
||||
Sets the text currently in the input prompt. Takes one parameter:
|
||||
|
||||
* __string__ *text*: The text to put in the prompt.
|
||||
|
||||
Examples:
|
||||
|
||||
jqconsole.SetPromptText('ls')
|
||||
jqconsole.SetPromptText('print [i ** 2 for i in range(10)]')
|
||||
|
||||
|
||||
###jqconsole.ClearPromptText
|
||||
Clears all the text currently in the input prompt. Takes one parameter:
|
||||
|
||||
* __bool__ *clear_label*: If specified and true, also clears the main prompt
|
||||
label (e.g. ">>>").
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
jqconsole.ClearPromptText()
|
||||
|
||||
|
||||
###jqconsole.GetPromptText
|
||||
Returns the contents of the prompt. Takes one parameter:
|
||||
|
||||
* __bool__ *full*: If specified and true, also includes the prompt labels
|
||||
(e.g. ">>>").
|
||||
|
||||
|
||||
Examples:
|
||||
|
||||
var currentCommand = jqconsole.GetPromptText()
|
||||
var logEntry = jqconsole.GetPromptText(true)
|
||||
|
||||
|
||||
###jqconsole.Reset
|
||||
Resets the console to its initial state, cancelling all current and pending
|
||||
operations. Takes no parameters.
|
||||
|
||||
Example:
|
||||
|
||||
jqconsole.Reset()
|
||||
|
||||
|
||||
###jqconsole.GetColumn
|
||||
Returns the 0-based number of the column on which the cursor currently is.
|
||||
Takes no parameters.
|
||||
|
||||
Example:
|
||||
|
||||
// Show the current line and column in a status area.
|
||||
$('#status').text(jqconsole.GetLine() + ', ' + jqconsole.GetColumn())
|
||||
|
||||
|
||||
###jqconsole.GetLine
|
||||
Returns the 0-based number of the line on which the cursor currently is.
|
||||
Takes no parameters.
|
||||
|
||||
Example:
|
||||
|
||||
// Show the current line and column in a status area.
|
||||
$('#status').text(jqconsole.GetLine() + ', ' + jqconsole.GetColumn())
|
||||
|
||||
###jqconsole.Focus
|
||||
Forces the focus onto the console so events can be captured.
|
||||
Takes no parameters.
|
||||
|
||||
Example:
|
||||
|
||||
// Redirect focus to the console whenever the user clicks anywhere.
|
||||
$(window).click(function() {
|
||||
jqconsole.Focus();
|
||||
})
|
||||
|
||||
|
||||
###jqconsole.GetIndentWidth
|
||||
Returns the number of spaces inserted when indenting. Takes no parameters.
|
||||
|
||||
Example:
|
||||
|
||||
jqconsole.SetIndentWidth(4);
|
||||
console.assert(jqconsole.GetIndentWidth() == 4);
|
||||
|
||||
|
||||
###jqconsole.UnRegisterMatching
|
||||
Deletes a certain matching settings set by `jqconsole.RegisterMatching`.
|
||||
Takes two paramaters:
|
||||
|
||||
* __char__ *open*: The opening character of a "block".
|
||||
* __char__ *close*: The closing character of a "block".
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
jqconsole.UnRegisterMatching('{', '}');
|
||||
|
||||
|
||||
###jqconsole.Dump
|
||||
Returns the text content of the console.
|
||||
|
||||
###jqconsole.GetState
|
||||
Returns the current state of the console. Could be one of the following:
|
||||
|
||||
* Input: `"input"`
|
||||
* Output: `"output"`
|
||||
* Prompt: `"prompt"`
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
jqconsole.GetState(); //output
|
||||
|
||||
|
||||
###jqconsole.MoveToStart
|
||||
Moves the cursor to the start of the current line.
|
||||
Takes one parameter:
|
||||
|
||||
* __bool__ *all_lines*: If true moves the cursor to the beginning of the first
|
||||
line in the current prompt. Defaults to false.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
// Move to line start Ctrl+A.
|
||||
jqconsole.RegisterShortcut('A', function() {
|
||||
jqconsole.MoveToStart();
|
||||
handler();
|
||||
});
|
||||
|
||||
|
||||
###jqconsole.MoveToEnd
|
||||
Moves the cursor to the end of the current line.
|
||||
Takes one parameter:
|
||||
|
||||
* __bool__ *all_lines*: If true moves the cursor to the end of the first
|
||||
line in the current prompt. Defaults to false.
|
||||
|
||||
Example:
|
||||
|
||||
// Move to line end Ctrl+E.
|
||||
jqconsole.RegisterShortcut('E', function() {
|
||||
jqconsole.MoveToEnd();
|
||||
handler();
|
||||
});
|
||||
|
||||
###jqconsole.Disable
|
||||
Disables input and focus on the console.
|
||||
|
||||
|
||||
###jqconsole.Enable
|
||||
Enables input and focus on the console.
|
||||
|
||||
|
||||
###jqconsole.IsDisabled
|
||||
Returns true if the console is disabled.
|
||||
|
||||
|
||||
###jqconsole.ResetHistory
|
||||
Resets the console history.
|
||||
|
||||
|
||||
###jqconsole.ResetMatchings
|
||||
Resets the character matching configuration.
|
||||
|
||||
|
||||
###jqconsole.ResetShortcuts
|
||||
Resets the shortcut configuration.
|
||||
|
||||
|
||||
##Default Key Config
|
||||
|
||||
The console responds to the followind keys and key combinations by default:
|
||||
|
||||
* `Delete`: Delete the following character.
|
||||
* `Ctrl+Delete`: Delete the following word.
|
||||
* `Backspace`: Delete the preceding character.
|
||||
* `Ctrl+Backspace`: Delete the preceding word.
|
||||
* `Ctrl+Left`: Move one word to the left.
|
||||
* `Ctrl+Right`: Move one word to the right.
|
||||
* `Home`: Move to the beginning of the current line.
|
||||
* `Ctrl+Home`: Move to the beginnig of the first line.
|
||||
* `End`: Move to the end of the current line.
|
||||
* `Ctrl+End`: Move to the end of the last line.
|
||||
* `Shift+Up`, `Ctrl+Up`: Move cursor to the line above the current one.
|
||||
* `Shift+Down`, `Ctrl+Down`: Move cursor to the line below the current one.
|
||||
* `Tab`: Indent.
|
||||
* `Shift+Tab`: Unindent.
|
||||
* `Up`: Previous history item.
|
||||
* `Down`: Next history item.
|
||||
* `Enter`: Finish input/prompt operation. See Input() and Prompt() for details.
|
||||
* `Shift+Enter`: New line.
|
||||
* `Page Up`: Scroll console one page up.
|
||||
* `Page Down`: Scroll console one page down.
|
||||
|
||||
##ANSI escape code SGR support
|
||||
|
||||
jq-console implements a large subset of the ANSI escape code graphics.
|
||||
Using the `.Write` method you could add style to the console using
|
||||
the following syntax:
|
||||
|
||||
`ASCII 27 (decimal) or 0x1b (hex)` `[` `SGR code` `m`
|
||||
|
||||
Example:
|
||||
|
||||
jqconsole.Write('\033[31mRed Text');
|
||||
|
||||
Note that the third parameter `escape` must be true which defaults to it.
|
||||
|
||||
You'll need to include the `ansi.css` file for default effects or create your
|
||||
own using the css classes from the table below.
|
||||
|
||||
###SGR
|
||||
[Reference](http://en.wikipedia.org/wiki/ANSI_escape_code#graphics).
|
||||
<table>
|
||||
<tr>
|
||||
<th>Code</th>
|
||||
<th>Effect</th>
|
||||
<th>Class</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>0</td>
|
||||
<td>Reset / Normal</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>Bold</td>
|
||||
<td>`jqconsole-ansi-bold`</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>Faint</td>
|
||||
<td>`jqconsole-ansi-lighter`</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td>Italic</td>
|
||||
<td>`jqconsole-ansi-italic`</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>4</td>
|
||||
<td>Line below text</td>
|
||||
<td>`jqconsole-ansi-underline`</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>5</td>
|
||||
<td>Blink: 1s delay</td>
|
||||
<td>`jqconsole-ansi-blink`</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>6</td>
|
||||
<td>Blink: 0.5s delay</td>
|
||||
<td>`jqconsole-ansi-blink-rapid`</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>8</td>
|
||||
<td>Hide text</td>
|
||||
<td>`jqconsole-ansi-hidden`</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>9</td>
|
||||
<td>Line through text</td>
|
||||
<td>`jqconsole-ansi-line-through`</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>10</td>
|
||||
<td>Remove all fonts</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>11-19</td>
|
||||
<td>Add custom font</td>
|
||||
<td>`jqconsole-ansi-fonts-{N}` where N is code - 10</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>20</td>
|
||||
<td>Add Fraktur font (not implemented in ansi.css)</td>
|
||||
<td>`jqconsole-ansi-fraktur`</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>21</td>
|
||||
<td>Remove Bold and Faint effects</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>22</td>
|
||||
<td>Same as 21</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>23</td>
|
||||
<td>Remove italic and fraktur effects</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>24</td>
|
||||
<td>Remove underline effect</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>25</td>
|
||||
<td>Remove blinking effect(s).</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>28</td>
|
||||
<td>Reveal text</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>29</td>
|
||||
<td>Remove line-through effect</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>30-37</td>
|
||||
<td>Set foreground color to color from the color table below</td>
|
||||
<td>jqconsole-ansi-color-{COLOR} where {COLOR} is the color name</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>39</td>
|
||||
<td>Restore default foreground color</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>40-47</td>
|
||||
<td>Set background color to color from the color table below</td>
|
||||
<td>`jqconsole-ansi-background-color-{COLOR}` where {COLOR} is the color name</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>49</td>
|
||||
<td>Restore default background color</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>51</td>
|
||||
<td>Adds a frame around the text</td>
|
||||
<td>`jqconsole-ansi-framed`</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>53</td>
|
||||
<td>Line above text</td>
|
||||
<td>jqconsole-ansi-overline</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>54</td>
|
||||
<td>Remove frame effect</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>55</td>
|
||||
<td>Remove over-line effect</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
###Colors
|
||||
[Reference](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors).
|
||||
<table>
|
||||
<tr>
|
||||
<th>Code offset</th>
|
||||
<th>Color</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>0</td>
|
||||
<td>Black</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>Red</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>Green</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td>Yellow</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>4</td>
|
||||
<td>Blue</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>5</td>
|
||||
<td>Magenta</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>6</td>
|
||||
<td>Cyan</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>7</td>
|
||||
<td>White</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
##CSS Classes
|
||||
|
||||
Several CSS classes are provided to help stylize the console:
|
||||
|
||||
* `jqconsole`: The main console container.
|
||||
* `jqconsole, jqconsole-blurred`: The main console container, when not in focus.
|
||||
* `jqconsole-cursor`: The cursor.
|
||||
* `jqconsole-header`: The welcome message at the top of the console.
|
||||
* `jqconsole-input`: The prompt area during input. May have multiple lines.
|
||||
* `jqconsole-old-input`: Previously-entered inputs.
|
||||
* `jqconsole-prompt`: The prompt area during prompting. May have multiple lines.
|
||||
* `jqconsole-old-prompt`: Previously-entered prompts.
|
||||
* `jqconsole-composition`: The div encapsulating the composition of multi-byte
|
||||
characters.
|
||||
|
||||
|
||||
Of course, custom classes may be specified when using `jqconsole.Write()` for
|
||||
further customization.
|
||||
|
||||
|
||||
##Contributors
|
||||
|
||||
[Max Shawabkeh](http://max99x.com/)
|
||||
[Amjad Masad](http://twitter.com/amjad_masad)
|
||||
172
lib/client/terminal/ansi.css
Normal file
172
lib/client/terminal/ansi.css
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
.jqconsole-ansi-bold {
|
||||
font-weight: bold!important;
|
||||
}
|
||||
|
||||
.jqconsole-ansi-lighter {
|
||||
font-weight: lighter!important;
|
||||
}
|
||||
|
||||
.jqconsole-ansi-italic {
|
||||
font-style: italic!important;
|
||||
}
|
||||
|
||||
.jqconsole-ansi-underline {
|
||||
text-decoration: underline!important;
|
||||
}
|
||||
|
||||
@-webkit-keyframes blinker {
|
||||
from { opacity: 1.0; }
|
||||
to { opacity: 0.0; }
|
||||
}
|
||||
|
||||
@-moz-keyframes blinker {
|
||||
from { opacity: 1.0; }
|
||||
to { opacity: 0.0; }
|
||||
}
|
||||
|
||||
@-ms-keyframes blinker {
|
||||
from { opacity: 1.0; }
|
||||
to { opacity: 0.0; }
|
||||
}
|
||||
|
||||
@-o-keyframes blinker {
|
||||
from { opacity: 1.0; }
|
||||
to { opacity: 0.0; }
|
||||
}
|
||||
|
||||
.jqconsole-ansi-blink {
|
||||
-webkit-animation-name: blinker;
|
||||
-moz-animation-name: blinker;
|
||||
-ms-animation-name: blinker;
|
||||
-o-animation-name: blinker;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
-moz-animation-iteration-count: infinite;
|
||||
-ms-animation-iteration-count: infinite;
|
||||
-o-animation-iteration-count: infinite;
|
||||
-webkit-animation-timing-function: cubic-bezier(1.0,0,0,1.0);
|
||||
-ms-animation-timing-function: cubic-bezier(1.0,0,0,1.0);
|
||||
-o-animation-timing-function: cubic-bezier(1.0,0,0,1.0);
|
||||
-moz-animation-timing-function: cubic-bezier(1.0,0,0,1.0);
|
||||
-webkit-animation-duration: 1s;
|
||||
-moz-animation-duration: 1s;
|
||||
-o-animation-duration: 1s;
|
||||
-ms-animation-duration: 1s;
|
||||
}
|
||||
|
||||
.jqconsole-ansi-blink-rapid {
|
||||
-webkit-animation-name: blinker;
|
||||
-moz-animation-name: blinker;
|
||||
-ms-animation-name: blinker;
|
||||
-o-animation-name: blinker;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
-moz-animation-iteration-count: infinite;
|
||||
-ms-animation-iteration-count: infinite;
|
||||
-o-animation-iteration-count: infinite;
|
||||
-webkit-animation-timing-function: cubic-bezier(1.0,0,0,1.0);
|
||||
-ms-animation-timing-function: cubic-bezier(1.0,0,0,1.0);
|
||||
-o-animation-timing-function: cubic-bezier(1.0,0,0,1.0);
|
||||
-moz-animation-timing-function: cubic-bezier(1.0,0,0,1.0);
|
||||
-webkit-animation-duration: 0.5s;
|
||||
-moz-animation-duration: 0.5s;
|
||||
-o-animation-duration: 0.5s;
|
||||
-ms-animation-duration: 0.5s;
|
||||
}
|
||||
|
||||
|
||||
.jqconsole-ansi-hidden {
|
||||
visibility:hidden!important;
|
||||
}
|
||||
|
||||
.jqconsole-ansi-line-through {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.jqconsole-ansi-fonts-1 {
|
||||
|
||||
}
|
||||
.jqconsole-ansi-fonts-2 {
|
||||
|
||||
}
|
||||
.jqconsole-ansi-fonts-3 {
|
||||
|
||||
}
|
||||
.jqconsole-ansi-fonts-4 {
|
||||
|
||||
}
|
||||
.jqconsole-ansi-fonts-5 {
|
||||
|
||||
}
|
||||
.jqconsole-ansi-fonts-6 {
|
||||
|
||||
}
|
||||
.jqconsole-ansi-fonts-7 {
|
||||
|
||||
}
|
||||
.jqconsole-ansi-fonts-8 {
|
||||
|
||||
}
|
||||
.jqconsole-ansi-fonts-9 {
|
||||
|
||||
}
|
||||
|
||||
.jqconsole-ansi-fraktur {
|
||||
|
||||
}
|
||||
|
||||
.jqconsole-ansi-color-black {
|
||||
color: black!important;
|
||||
}
|
||||
.jqconsole-ansi-color-red {
|
||||
color: red!important;
|
||||
}
|
||||
.jqconsole-ansi-color-green {
|
||||
color: green!important;
|
||||
}
|
||||
.jqconsole-ansi-color-yellow {
|
||||
color: yellow!important;
|
||||
}
|
||||
.jqconsole-ansi-color-blue {
|
||||
color: blue!important;
|
||||
}
|
||||
.jqconsole-ansi-color-magenta {
|
||||
color: magenta!important;
|
||||
}
|
||||
.jqconsole-ansi-color-cyan {
|
||||
color: cyan!important;
|
||||
}
|
||||
.jqconsole-ansi-color-white {
|
||||
color: white!important;
|
||||
}
|
||||
|
||||
.jqconsole-ansi-background-color-black {
|
||||
background-color: black!important;
|
||||
}
|
||||
.jqconsole-ansi-background-color-red {
|
||||
background-color: red!important;
|
||||
}
|
||||
.jqconsole-ansi-background-color-green {
|
||||
background-color: green!important;
|
||||
}
|
||||
.jqconsole-ansi-background-color-yellow {
|
||||
background-color: yellow!important;
|
||||
}
|
||||
.jqconsole-ansi-background-color-blue {
|
||||
background-color: blue!important;
|
||||
}
|
||||
.jqconsole-ansi-background-color-magenta {
|
||||
background-color: magenta!important;
|
||||
}
|
||||
.jqconsole-ansi-background-color-cyan {
|
||||
background-color: cyan!important;
|
||||
}
|
||||
.jqconsole-ansi-background-color-white {
|
||||
background-color: white!important;
|
||||
}
|
||||
|
||||
.jqconsole-ansi-framed {
|
||||
border: 1px solid!important;
|
||||
}
|
||||
.jqconsole-ansi-overline {
|
||||
text-decoration: overline!important;
|
||||
}
|
||||
|
||||
1279
lib/client/terminal/jqconsole.coffee
Normal file
1279
lib/client/terminal/jqconsole.coffee
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,149 +1,134 @@
|
|||
var CloudCommander, CloudFunc, $;
|
||||
/* object contains viewer FancyBox
|
||||
* https://github.com/fancyapps/fancyBox
|
||||
*/
|
||||
CloudCommander.Viewer = {
|
||||
dir : CloudCommander.LIBDIRCLIENT + 'viewer/',
|
||||
|
||||
getByClass : function(pClass){
|
||||
return document.getElementsByClassName(pClass);
|
||||
}
|
||||
};
|
||||
CloudCommander.Viewer.FancyBox = {
|
||||
dir : CloudCommander.Viewer.dir + 'fancybox/',
|
||||
/* function return configureation
|
||||
* for FancyBox open and
|
||||
* onclick (it shoud be
|
||||
* different objects)
|
||||
*/
|
||||
getConfig: (function(){
|
||||
return{
|
||||
beforeShow : (function(){
|
||||
CloudCommander.keyBinded = false;
|
||||
}),
|
||||
beforeClose: (function(){
|
||||
CloudCommander.keyBinded = true;
|
||||
}),
|
||||
|
||||
openEffect : 'none',
|
||||
closeEffect : 'none',
|
||||
|
||||
helpers : {
|
||||
overlay : {
|
||||
opacity: 0.1,
|
||||
css : {
|
||||
'background-color' : '#fff'
|
||||
}
|
||||
}
|
||||
},
|
||||
padding : 0
|
||||
};
|
||||
}),
|
||||
/* function loads css and js of FancyBox
|
||||
* @pParent - this
|
||||
* @pCallBack - executes, when everything loaded
|
||||
*/
|
||||
load: (function(pParent, pCallBack){
|
||||
return function(){
|
||||
var ljsLoad_f = function(){
|
||||
var lSrc = 'http://fancyapps.com/fancybox/source/jquery.fancybox.pack.js';
|
||||
|
||||
CloudCommander.jsload(lSrc,{
|
||||
onload: pCallBack,
|
||||
|
||||
onerror: (function() {
|
||||
CloudCommander.jsload(pParent.dir +
|
||||
'jquery.fancybox.pack.js', {onload: pCallBack});
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
var lSrc = 'http://fancyapps.com/fancybox/source/jquery.fancybox.css';
|
||||
|
||||
CloudCommander.cssLoad({
|
||||
src : lSrc,
|
||||
func : {
|
||||
onload: ljsLoad_f,
|
||||
onerror: (function() {
|
||||
CloudCommander.cssLoad({
|
||||
src : pParent.dir +'jquery.fancybox.css',
|
||||
func : ljsLoad_f
|
||||
});
|
||||
})
|
||||
}
|
||||
});
|
||||
}();
|
||||
}),
|
||||
set: function(){
|
||||
if(this.getByClass('fancybox').length)
|
||||
return;
|
||||
try{
|
||||
/* get current panel (left or right) */
|
||||
var lPanel = this.getByClass('current-file');
|
||||
|
||||
lPanel.length &&
|
||||
(lPanel = lPanel[0].parentElement);
|
||||
|
||||
/* get all file links */
|
||||
var lA = lPanel.getElementsByTagName('a');
|
||||
|
||||
var lName;
|
||||
/* first two is not files nor folders*/
|
||||
for (var i=2; i < lA.length; i++) {
|
||||
lName = lA[i].title || lA[i].textContent;
|
||||
|
||||
CloudFunc.checkExtension(lName,['png','jpg']) &&
|
||||
(lA[i].className = 'fancybox') &&
|
||||
(lA[i].rel = 'gallery');
|
||||
}
|
||||
|
||||
$('.fancybox').fancybox(this.getConfig());
|
||||
}catch(pError){
|
||||
console.log(pError);
|
||||
}
|
||||
},
|
||||
getById : function(pId){return document.getElementById(pId);},
|
||||
|
||||
getByClass : function(pClass){
|
||||
return document.getElementsByClassName(pClass);
|
||||
}
|
||||
};
|
||||
CloudCommander.Viewer.Keys = (function(){
|
||||
"use strict";
|
||||
|
||||
var lCallBack_f = (function(pParent){
|
||||
var key_event = function(){
|
||||
return function(event){
|
||||
/* если клавиши можно обрабатывать */
|
||||
if(CloudCommander.keyBinded){
|
||||
/* if f3 pressed */
|
||||
if(event.keyCode===114){
|
||||
CloudCommander.Viewer.FancyBox.set();
|
||||
var lCurrent = pParent.getByClass('current-file');
|
||||
lCurrent.length &&
|
||||
(lCurrent = lCurrent[0]);
|
||||
|
||||
var lA = lCurrent.getElementsByClassName('fancybox');
|
||||
|
||||
var lConfig = pParent.FancyBox.getConfig();
|
||||
|
||||
lA.length &&
|
||||
$.fancybox.open({ href : lA[0].href },
|
||||
lConfig);
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/* добавляем обработчик клавишь */
|
||||
if (document.addEventListener)
|
||||
document.addEventListener('keydown', key_event(pParent),false);
|
||||
|
||||
else
|
||||
document.onkeypress=key_event;
|
||||
});
|
||||
|
||||
CloudCommander.Viewer.FancyBox.load(this.FancyBox, lCallBack_f(this));
|
||||
var CloudCommander, CloudFunc, $;
|
||||
/* object contains viewer FancyBox
|
||||
* https://github.com/fancyapps/fancyBox
|
||||
*/
|
||||
CloudCommander.Viewer = {
|
||||
dir : './lib/client/viewer/'
|
||||
};
|
||||
|
||||
CloudCommander.Viewer.FancyBox = new CloudCommander.Util();
|
||||
|
||||
CloudCommander.Viewer.FancyBox.dir = CloudCommander.Viewer.dir +
|
||||
'fancybox/';
|
||||
|
||||
/* function return configureation
|
||||
* for FancyBox open and
|
||||
* onclick (it shoud be
|
||||
* different objects)
|
||||
*/
|
||||
CloudCommander.Viewer.FancyBox.getConfig = (function(){
|
||||
return{
|
||||
beforeShow : (function(){
|
||||
CloudCommander.keyBinded = false;
|
||||
}),
|
||||
beforeClose: (function(){
|
||||
CloudCommander.keyBinded = true;
|
||||
}),
|
||||
|
||||
openEffect : 'none',
|
||||
closeEffect : 'none',
|
||||
|
||||
helpers : {
|
||||
overlay : {
|
||||
opacity: 0.1,
|
||||
css : {
|
||||
'background-color' : '#fff'
|
||||
}
|
||||
}
|
||||
},
|
||||
padding : 0
|
||||
};
|
||||
});
|
||||
|
||||
/* function loads css and js of FancyBox
|
||||
* @pParent - this
|
||||
* @pCallBack - executes, when everything loaded
|
||||
*/
|
||||
CloudCommander.Viewer.FancyBox.load = (function(pParent, pCallBack){
|
||||
return function(){
|
||||
var ljsLoad_f = function(){
|
||||
var lSrc = pParent.dir + 'jquery.fancybox.pack.js';
|
||||
pParent.jsload(lSrc,{
|
||||
onload: pCallBack
|
||||
});
|
||||
};
|
||||
|
||||
var lSrc = pParent.dir +'jquery.fancybox.pack.css';
|
||||
pParent.cssLoad({
|
||||
src : lSrc,
|
||||
func : {
|
||||
onload: ljsLoad_f
|
||||
}
|
||||
});
|
||||
}();
|
||||
});
|
||||
|
||||
CloudCommander.Viewer.FancyBox.set = (function(){
|
||||
if(this.getByClass('fancybox').length)
|
||||
return;
|
||||
try{
|
||||
/* get current panel (left or right) */
|
||||
var lPanel = this.getPanel();
|
||||
|
||||
/* get all file links */
|
||||
var lA = lPanel.getElementsByTagName('a');
|
||||
|
||||
var lName;
|
||||
/* first two is not files nor folders*/
|
||||
for (var i=2; i < lA.length; i++) {
|
||||
lName = lA[i].title || lA[i].textContent;
|
||||
|
||||
CloudFunc.checkExtension(lName,
|
||||
['png','jpg', 'gif','ico']) &&
|
||||
(lA[i].className = 'fancybox') &&
|
||||
(lA[i].rel = 'gallery');
|
||||
}
|
||||
|
||||
$('.fancybox').fancybox(this.getConfig());
|
||||
}catch(pError){
|
||||
console.log(pError);
|
||||
}
|
||||
});
|
||||
|
||||
CloudCommander.Viewer.FancyBox.show = (function(pParent){
|
||||
CloudCommander.Viewer.FancyBox.set();
|
||||
|
||||
var lCurrent = this.getCurrentFile();
|
||||
var lA = lCurrent.getElementsByClassName('fancybox');
|
||||
var lConfig = this.getConfig();
|
||||
|
||||
lA.length &&
|
||||
$.fancybox.open({ href : lA[0].href },
|
||||
lConfig);
|
||||
});
|
||||
|
||||
CloudCommander.Viewer.Keys = (function(){
|
||||
"use strict";
|
||||
|
||||
var lCallBack_f = (function(){
|
||||
var key_event = (function(){
|
||||
return function(event){
|
||||
/* если клавиши можно обрабатывать */
|
||||
if(CloudCommander.keyBinded)
|
||||
/* if f3 pressed */
|
||||
if(event.keyCode===114){
|
||||
CloudCommander.Viewer.FancyBox.show();
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
/* добавляем обработчик клавишь */
|
||||
if (document.addEventListener)
|
||||
document.addEventListener('keydown', key_event(),false);
|
||||
|
||||
else
|
||||
document.onkeypress=key_event;
|
||||
|
||||
/* showing images preview*/
|
||||
CloudCommander.Viewer.FancyBox.show();
|
||||
});
|
||||
|
||||
CloudCommander.Viewer.FancyBox.load(this.FancyBox, lCallBack_f);
|
||||
});
|
||||
1
lib/client/viewer/fancybox/jquery.fancybox.pack.css
Normal file
1
lib/client/viewer/fancybox/jquery.fancybox.pack.css
Normal file
|
|
@ -0,0 +1 @@
|
|||
/*! fancyBox v2.0.6 fancyapps.com | fancyapps.com/fancybox/#license */.fancybox-wrap,.fancybox-skin,.fancybox-outer,.fancybox-inner,.fancybox-image,.fancybox-wrap iframe,.fancybox-wrap object,.fancybox-nav,.fancybox-nav span,.fancybox-tmp{padding:0;margin:0;border:0;outline:0;vertical-align:top}.fancybox-wrap{position:absolute;top:0;left:0;z-index:8020}.fancybox-skin{position:relative;background:#f9f9f9;color:#444;text-shadow:none;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.fancybox-opened{z-index:8030}.fancybox-opened .fancybox-skin{-webkit-box-shadow:0 10px 25px rgba(0,0,0,.5);-moz-box-shadow:0 10px 25px rgba(0,0,0,.5);box-shadow:0 10px 25px rgba(0,0,0,.5)}.fancybox-outer,.fancybox-inner{position:relative}.fancybox-inner{overflow:hidden}.fancybox-type-iframe .fancybox-inner{-webkit-overflow-scrolling:touch}.fancybox-error{color:#444;font:14px/20px "Helvetica Neue",Helvetica,Arial,sans-serif;margin:0;padding:15px;white-space:nowrap}.fancybox-image,.fancybox-iframe{display:block;width:100%;height:100%}.fancybox-image{max-width:100%;max-height:100%}#fancybox-loading,.fancybox-close,.fancybox-prev span,.fancybox-next span{background-image:url('fancybox_sprite.png')}#fancybox-loading{position:fixed;top:50%;left:50%;margin-top:-22px;margin-left:-22px;background-position:0 -108px;opacity:.8;cursor:pointer;z-index:8060}#fancybox-loading div{width:44px;height:44px;background:url('fancybox_loading.gif') center center no-repeat}.fancybox-close{position:absolute;top:-18px;right:-18px;width:36px;height:36px;cursor:pointer;z-index:8040}.fancybox-nav{position:absolute;top:0;width:40%;height:100%;cursor:pointer;text-decoration:none;background:transparent url('blank.gif');-webkit-tap-highlight-color:rgba(0,0,0,0);z-index:8040}.fancybox-prev{left:0}.fancybox-next{right:0}.fancybox-nav span{position:absolute;top:50%;width:36px;height:34px;margin-top:-18px;cursor:pointer;z-index:8040;visibility:hidden}.fancybox-prev span{left:10px;background-position:0 -36px}.fancybox-next span{right:10px;background-position:0 -72px}.fancybox-nav:hover span{visibility:visible}.fancybox-tmp{position:absolute;top:-9999px;left:-9999px;visibility:hidden}#fancybox-overlay{position:absolute;top:0;left:0;overflow:hidden;display:none;z-index:8010;background:#000}#fancybox-overlay.overlay-fixed{position:fixed;bottom:0;right:0}.fancybox-title{visibility:hidden;font:normal 13px/20px "Helvetica Neue",Helvetica,Arial,sans-serif;position:relative;text-shadow:none;z-index:8050}.fancybox-opened .fancybox-title{visibility:visible}.fancybox-title-float-wrap{position:absolute;bottom:0;right:50%;margin-bottom:-35px;z-index:8050;text-align:center}.fancybox-title-float-wrap .child{display:inline-block;margin-right:-100%;padding:2px 20px;background:transparent;background:rgba(0,0,0,.8);-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;text-shadow:0 1px 2px #222;color:#FFF;font-weight:700;line-height:24px;white-space:nowrap}.fancybox-title-outside-wrap{position:relative;margin-top:10px;color:#fff}.fancybox-title-inside-wrap{padding-top:10px}.fancybox-title-over-wrap{position:absolute;bottom:0;left:0;color:#fff;padding:10px;background:#000;background:rgba(0,0,0,.8)}
|
||||
|
|
@ -100,7 +100,7 @@ exports.Minify={
|
|||
}catch(pError){
|
||||
this._allowed={js:false,css:false,html:false};
|
||||
return console.log('Could not minify ' +
|
||||
'withou minify module\n' +
|
||||
'without minify module\n' +
|
||||
'npm i minify');
|
||||
}
|
||||
|
||||
|
|
|
|||
3
lib/server/win.js
Normal file
3
lib/server/win.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
/* Library contain windows specific functions
|
||||
* like getting information about volumes
|
||||
*/
|
||||
1
lib/server/win/getvolumes.bat
Normal file
1
lib/server/win/getvolumes.bat
Normal file
|
|
@ -0,0 +1 @@
|
|||
diskpart /s getvolumes.txt
|
||||
1
lib/server/win/getvolumes.txt
Normal file
1
lib/server/win/getvolumes.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
list volumes
|
||||
Loading…
Add table
Add a link
Reference in a new issue