This commit is contained in:
coderaiser 2012-12-12 03:09:51 -05:00
commit 5c5be0946c
77 changed files with 26745 additions and 8729 deletions

View file

@ -1,12 +1,11 @@
language: node_js
node_js:
#- 0.4 #jshint fails to install (no gzip, jshint test not working out)
- 0.6
- 0.8
- 0.9
notifications:
webhooks:
#http://requestb.in/12h5bl71
urls:
# http://webhooks.jit.su/deploy ##
- http://webhooks.nodejitsu.com/1/deploy
language: node_js
node_js:
- 0.6
- 0.8
- 0.9
notifications:
#webhooks:
#http://requestb.in/12h5bl71
email:
on_success: never
on_failure: change

202
ChangeLog
View file

@ -1,3 +1,196 @@
2012.10.*, Version 0.1.8
* Added ability to shutdown Cloud Commander
thru terminal command: "cloudcmd exit"
* Fixed bug with showing terminal and viewer
at the same time.
* Fixed bug with appcache config. Windows reads
file not just like Linux.
* Added ability to update thru git.
* Added ability to change charset on terminal only if
it's build in command on win32.
* Fixed bug with testing mode. If was same arguments,
Cloud Commander works in testing mode and server do not
started.
* Added more demo mirrors to readme (appfog, cloudfoundry).
* Removed resize event from jquery-terminal.
* Fixed bug with error code of program execution in terminal.
* Fixed bug with menu itmes edit and view.
* Added function jsLoadOnLoad, thet loads js files
one-by-one, and then calls callback if needed.
* Added function anyloadOnLoad thet loads any files or
elements one-by-one.
* Added function loadModule thet make it easier to connect
new module.
* Fixed bug with context menu hiding (on Esc).
Keys was unbinded.
* Added ability to disable web sockets.
* Moved cloudRequire to srvfunc.js file.
* Fixed bug with directory pathes detection
(used document.location.href, now using
document.location.origin)
* Fixed bug with keybinding in FireFox
(removed KeyBinding call on window load).
* Fixed bug with getting current url in Firefox.
* Util cleaned up (addClass and removeClass
moved to ie.js).
* Index processing function moved out from
server.js to cloudcmd.js.
* Added ability of authorithation in GitHub.
* Rewrited sizing of names if they are to
long thru css, from JavaScript.
* Fixed bug with application cahe forming on windows
and linux.
* Removed unneeded windows check.
* Fixed bug with downloading directory
listing in json from menu.
* Changed default port to 80.
* Util object moved from client.js to modules
dom.js and util.js.
* Fixed bug with navigation thru path panel.
* Added functions DOM.addKeyListener and Util.loadOnLoad
* Added buttons panel.
* Fixed bug with config file, when no need to minification.
* Added ability to hide keys panel thru config option.
* CodeMirror upgraded to version 2.35.0.
* Fixed bug with keys panel and fm bottom margin,
when CodeMirror is open on the right panel.
* Fixed bug with positioning of CodeMirror on the
right panel.
* Function generateHeaders moved to main module.
* Setted up auth on GitHub thru rest.
* Fixed bug with context menu. Now it disabled
before load menu module to.
* Throw out jquery from github module, moved Cache
object from client to DOM module,
refactored Cache object and added polyfill.
* Added ability to connect github development id,
to Cloud Commander instance working in any host,
for this all the needed to be done is:
set enveronment varibles "oauth_client_id" and
"oauth_client_secret" values from github profile.
* Changed funcyBox version to 2.1.3.
* Improved reading file with streams.
* Improved download speed of js and css files by
adding DOM.anyLoadInParallel function that is
loading files in parallel and then execute
callback function.
* Added ability to load a couple scripts after one main,
in any position in anyLoadOnLoad function.
* Added chainable to Cache object in DOM.
* Changed default ip to null so IP would be geted from
config.json only if it setted up.
* Fixed bug with starting node from other then projects dir.
* Fixed bug with slashes on win32.
* Fixed bug with editor close, when started from menu.
* Added url change on folder changing.
* Fixed bug with traveling in directories with
Javascript dissabled.
* client.js and server.js moved to lib directory.
* Improved work with browsers history api.
* Fixed bug with setting path of index.html.
* Added dropbox module.
* Renamed config options ouath_client_id and
oauth_client_secre to github_id and github_secret.
Added dropbox_id option.
* Fixed bug in github show function.
* Added ability to call dropbox chooser on <ctr> + <d>.
* Added ability to read dropbox key from config.
* Fixed bug with menu load, when dir other then root.
* Added confirmation before (not real) deleting file.
* Added ability to upload file to gists on github thru menu.
* Added getCurrentPath function to Util module.
* GitHub and DropBox secret's moved out from
config.json to env.
* Fixed bug with auth in github.
* Fixed bug with opening empty files in CodeMiror.
Editor window could not be cloused.
* Added windows support on terminal command: "cloudcmd exit".
* Fixed IP on windows.
* Added ability to delete files (not for real for now)
thru keys panel (F8).
* Changed "Upload" menu item to "Upload to".
* Fixed bug with minified styles.
* If git not installed do not show error.
Just propose install git and clone from github repo.
* Fixed bug with client.js minifying.
* Fixed bug with appcache.
2012.10.01, Version 0.1.7
* Changed name of menu files, fixed npm and jitsu
@ -5,6 +198,7 @@ problem with menu showing.
* Fixed bug with empty directorys. If directory
was empty, we could not go in.
* Removed packed versions of files, uglifing
would be doing on-a-fly
@ -17,7 +211,7 @@ loaded any time by any extension.
* Fixed bug with getByClass IE version.
* Fixed bug with keyboard not responding when go deeper
in file tree thrue mouse double click.
in file tree thru mouse double click.
* Removed property keyBinded. From not it's private
member of KeyBinding class.
@ -27,11 +221,11 @@ Function scrollIntoViewIfNeeded was polyfilled.
* Added classic borders to panels.
* Added ability to download files thrue drag'n'drop.
* Added ability to download files thru drag'n'drop.
* Fixed bug with setting current file when mouse down.
* Improved borders over CodeMirror and panels thrue css.
* Improved borders over CodeMirror and panels thru css.
* Added ability to watch is file changed wile server is
running
@ -58,7 +252,7 @@ disabled in browsers.
* Added plagin terminal ( ` button under TAB).
* Added ability to execute commands on server thrue terminal.
* Added ability to execute commands on server thru terminal.
* Added appcache paramter to config.json

156
README.md
View file

@ -1,156 +0,0 @@
Cloud Commander [![Build Status](https://secure.travis-ci.org/coderaiser/cloudcmd.png?branch=master)](http://travis-ci.org/coderaiser/cloudcmd)
===============
**Cloud Commander** - two-panels file manager, totally writed on js.
View [demo](http://cloudcmd.cloudfoundry.com/ "demo").
Google PageSpeed Score : [100](https://developers.google.com/speed/pagespeed/insights#url=http_3A_2F_2Fdemo-cloudcmd.cloudfoundry.com_2F&mobile=false "score") (out of 100).
Benefits
---------------
- full browser compatibility *(ie6+,chrome,safari,opera,firefox)*;
- responsible design
- one full page loading, *and then just one-time json-dir-listings loading
(with refresh opportunity).*
- caching readed directories *to localStorage (for now)
(so if network will disconnected or something heppen with a signal, we
definitely will can work with cached copy of directory listings)*;
- key binding
- disabled js support *(working in limited mode)*.
- automated minification *client js-files and onstart-reading Cloud manager files on server starting.*
**Cloud Commander** uses all benefits of js, so if js is disabled,
we moves to *limited mode*.
Limited-mode features
---------------
- only 1 panel available
- no keybinding
- no local caching
- full loading of all web page(with styles, js-scripts, html-page etc).
Hot keys
---------------
In all modern web browsers (but not in IE, becouse he special) hot keys works.
There is a short list:
- Ctrl + r - reload dir content
- Ctrl + d - clear local cache (wich contains dir contents)
- Alt + q - disable key bindings
- Alt + s - get all key bindings back
- up, down, enter - filesystem navigation
Viewer's hot keys
---------------
- Shift + F3 - open viewer window
- Esc - close viewer window
Editor's hot keys
---------------
- F3 - open CodeMirror editor in read only mode
- F4 - open CodeMirror editor
- Esc - close CodeMirror editor
Installing
---------------
**Cloud Commander** installing is very easy. All you need it's just clone
repository from github. Just 2 commands:
git clone git://github.com/coderaiser/cloudcmd.git
cd cloudcmd
or
npm i cloudcmd
mv node_modules/cloudcmd ./
Configuration
---------------
All main configuration could be done thrue config.json.
```js
{
"cache" : {"allowed" : true}, /* cashing of js and css files in memory */
"minification" : { /* minification of js,css,html and img */
"js" : false, /* minify module neaded */
"css" : false, /* npm i minify */
"html" : true,
"img" : false
},
"server" : true, /* server mode or testing mode */
"logs" : false, /* logs or console ouput */
"port" : 31337, /* Cloud Commander port */
"ip" : "127.0.0.1" /* Cloud Commander IP */
}
```
Starting
---------------
To start **Cloud Commander** only one command neaded:
node cloudcmd
or on win platform just
cloudcmd
After thet Cloud Commander reads config file **config.json** and start server
on 31337 port, if none of port varibles(*cloud9*, *cloudfoundry* and *nodester*)
isn't exist.
Then type in browser
http://127.0.0.1:31337
or
http://localhost:31337
Updating
---------------
**Cloud Commander** is very buggy and alfa so it's very often updated. For update
you can just type in cloudcmd directory:
git pull
or check new version on npm
npm info cloudcmd
and then, if there is new version
npm r cloudcmd
npm i cloudcmd
Additional modules
---------------
**Cloud Commander's Server Side** not using additional modules for main functionality.
But for minification and optimization tricks optional can be
assingned (and installed) modules: [Minify] (https://github.com/coderaiser/minify "Minify")
and [socket.io] (https://github.com/LearnBoost/socket.io "Socket.IO").
Install addtitional modules:
npm i
**Cloud Commander's Client Side** use module jquery for ajaxing.
We could not use this module, but this way is fast:
- google cdn
- gzip
- cache
Perhaps in the future, it will not be used, but so far it has no effect on
start loading of Cloud Commander Client Side and do things fast and stable
it is using now.
Extensions
---------------
**Cloud Commander** desinged to easily porting extensions.
For extend main functionality Cloud Commander use next modules:
- [CodeMirror] (http://codemirror.net "CodeMirror")
- [FancyBox] (https://github.com/fancyapps/fancyBox "FancyBox")
- [jQuery-contextMenu] (https://github.com/medialize/jQuery-contextMenu "jQuery-contextMenu")
- [jquery.terminal] (https://github.com/jcubic/jquery.terminal "jquery.terminal")
Contributing
---------------
If you would like to contribute - send pull request to dev branch.
Getting dev version of **Cloud Commander**:
git clone git://github.com/coderaiser/cloudcmd.git
git checkout dev
It is possible thet dev version Cloud Commander will needed dev version of Minify,
so to get it you should type a couple more commands:
cd node_modules
rm -rf minify
git clone git://github.com/coderaiser/minify
git checkout dev

1566
client.js

File diff suppressed because it is too large Load diff

View file

@ -1 +0,0 @@
node cloudcmd || chcp 866

View file

@ -1,75 +1,147 @@
"use strict";
var Server = cloudRequire('./server'),
path = cloudRequire('path'),
fs = cloudRequire('fs');
var Config = readConfig();
Config ? Server.start(Config) : Server.start();
function readConfig(){
/* Determining server.js directory
* and chang current process directory
* (usually /) to it.
* argv[1] - is always script name
*/
var lServerDir = path.dirname(process.argv[1]),
lProcessDir = process.cwd();
lConfig = {};
if(lProcessDir !== lServerDir){
console.log('current dir: ' + lProcessDir);
process.chdir(lServerDir);
}
console.log('server dir: ' + lServerDir);
console.log('reading configuretion file config.json...');
var lConfig = cloudRequire('./config');
if(lConfig){
console.log('config.json readed');
/* if command line parameter testing resolved
* setting config to testing, so server
* not created, just init and
* all logs writed to screen
*/
var lArg = process.argv[process.argv.length - 1];
if ( lArg === 'test' || lArg === 'test\r') {
console.log(process.argv);
lConfig.server = false;
lConfig.logs = false;
}
if (lConfig.logs) {
console.log('log param setted up in config.json\n' +
'from now all logs will be writed to log.txt');
writeLogsToFile();
}
return lConfig;
}
else return false;
}
/* function sets stdout to file log.txt */
function writeLogsToFile(){
var stdo = fs.createWriteStream('./log.txt');
process.stdout.write = (function(write) {
return function(string, encoding, fd) {
stdo.write(string);
};
})(process.stdout.write);
}
/* function do safe require of needed module */
function cloudRequire(pModule){
try{
return require(pModule);
}
catch(pError){
return false;
}
}
(function(){
"use strict";
var DIR = __dirname + '/',
main = require(DIR + 'lib/server/main'),
LIBDIR = main.LIBDIR,
SRVDIR = main.SRVDIR,
path = main.path,
fs = main.fs,
CloudFunc = main.cloudfunc,
Util = main.util,
update = main.update,
Server = main.require(LIBDIR + 'server'),
srv = Server.CloudServer,
Config = main.config;
/* reinit main dir os if we on
* Win32 should be backslashes */
DIR = main.DIR;
readConfig();
Server.start(Config, {
index : indexProcessing,
appcache : appCacheProcessing,
rest : rest
});
if(update)
update.get();
/**
* additional processing of index file
*/
function indexProcessing(pData){
var lReplace_s,
lData = pData.data,
lAdditional = pData.additional;
/*
* если выбрана опция минимизировать скрипты
* меняем в index.html обычные css на
* минифицированый
*/
if(srv.Minify._allowed.css){
var lPath = '/' + srv.Minify.MinFolder.replace(DIR, '');
lReplace_s = '<link rel=stylesheet href="/css/reset.css">';
lData = Util.removeStr(lData, lReplace_s)
.replace('/css/style.css', lPath + 'all.min.css');
}
lReplace_s = '<div id=fm class=no-js>';
/* меняем title */
lData = lData.replace(lReplace_s, lReplace_s + lAdditional)
.replace('<title>Cloud Commander</title>',
'<title>' + CloudFunc.getTitle() + '</title>');
if(!srv.Config.appcache)
lData = Util.removeStr(lData, ' manifest="/cloudcmd.appcache"');
if(!srv.Config.show_keys_panel){
var lKeysPanel = '<div id=keyspanel';
lData = lData.replace(lKeysPanel, lKeysPanel +' class=hidden');
}
return lData;
}
function appCacheProcessing(){
var lAppCache = srv.AppCache,
lFiles = [
{'//themes.googleusercontent.com/static/fonts/droidsansmono/v4/ns-m2xQYezAtqh7ai59hJUYuTAAIFFn5GTWtryCmBQ4.woff' : './font/DroidSansMono.woff'},
{'//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js' : './lib/client/jquery.js'}];
if(srv.Minify._allowed.css)
lFiles.push('node_modules/minify/min/all.min.css');
lAppCache.addFiles(lFiles);
lAppCache.createManifest();
}
/**
* rest interface
* @pConnectionData {request, responce}
*/
function rest(pConnectionData){
return Util.exec(main.rest, pConnectionData);
}
function readConfig(){
/* Determining server.js directory
* and chang current process directory
* (usually /) to it.
* argv[1] - is always script name
*/
var lServerDir = path.dirname(process.argv[1]) + '/';
if( DIR !== lServerDir ){
console.log('current dir: ' + DIR);
process.chdir(lServerDir);
}
console.log('server dir: ' + lServerDir + '\n' +
'reading configuretion file config.json...');
if(Config){
console.log('config.json readed');
/* if command line parameter testing resolved
* setting config to testing, so server
* not created, just init and
* all logs writed to screen
*/
var lArg = process.argv;
lArg = lArg[lArg.length - 1];
if ( lArg === 'test' || lArg === 'test\r') {
console.log(process.argv);
Config.server =
Config.logs = false;
}
if (Config.logs) {
console.log('log param setted up in config.json\n' +
'from now all logs will be writed to log.txt');
writeLogsToFile();
}
}
}
/* function sets stdout to file log.txt */
function writeLogsToFile(){
var stdo = fs.createWriteStream('./log.txt');
process.stdout.write = (function(write) {
return function(string, encoding, fd) {
stdo.write(string);
};
})(process.stdout.write);
}
})();

View file

@ -1,14 +1,22 @@
{
"cache" : {"allowed" : false},
"appcache" : true,
"minification" : {
"js" : true,
"css" : true,
"html" : true,
"img" : true
},
"server" : true,
"logs" : false,
"port" : 31337,
"ip" : "127.0.0.1"
{
"api_url" : "/api/v1",
"appcache" : false,
"cache" : {"allowed" : false},
"minification" : {
"js" : true,
"css" : true,
"html" : true,
"img" : true
},
"github_key" : "891c251b925e4e967fa9",
"github_secret" : "afe9bed1e810c5dc44c4c2a953fc6efb1e5b0545",
"dropbox_key" : "0nd3ssnp5fp7tqs",
"dropbox_chooser_key" : "o7d6llji052vijk",
"logs" : false,
"show_keys_panel" : true,
"server" : true,
"socket" : true,
"port" : 80,
"ip" : null,
"rest" : true
}

View file

@ -7,7 +7,13 @@
* 2. Prevent iOS text size adjust on device orientation change, without disabling user zoom: h5bp.com/g
*/
html { font-size: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; font-family: sans-serif; color: #222;}
html{
font-family: sans-serif;
font-size: 100%;
color: #222;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
}
body { margin: 0; font-size: 1em; line-height: 1.4; }
/*
@ -26,7 +32,10 @@ body { margin: 0; font-size: 1em; line-height: 1.4; }
Links
========================================================================== */
a {text-decoration:none; color: #00e; }
a{
color: #00e;
text-decoration:none;
}
a:visited { color: #551a8b; }
a:hover { color: #06e; }
a:focus { outline: thin dotted; }
@ -37,11 +46,11 @@ a:hover, a:active { outline: 0; }
/* changed ul to panel, it using only in this case for now, and will css
* processing cost 33% with name of tag, so it should be match faster
*/
.panel{
.panel{
/* removed default margins */
margin: 0;
padding: 20px;
}
margin: 0;
}
/*
* 1. Display hand cursor for clickable form elements

View file

@ -36,7 +36,8 @@
}
body{
font:16px "Droid Sans Mono";
font:16px "Droid Sans Mono";
background-color:white;
}
.menu{
@ -79,9 +80,9 @@ body{
.error::before{
position: relative;
bottom : 4px;
content:'\f026';
color:rgb(222, 41, 41);
cursor:default;
color:rgb(222, 41, 41);
content:'\f026';
}
.loading{
position:relative;
@ -89,15 +90,36 @@ body{
background:url(/img/spinner.gif);
}
.error:hover{
color:rgba(222, 41, 41, 0.81);
color:rgb(222, 41, 41);
color:rgba(222, 41, 41, 0.81);
}
.refresh-icon{
background:url(/img/panel_refresh.png) no-repeat;
}
.refresh-icon:active{
/*background-position-y: -15px;*/
background:url(/img/panel_refresh.png) 0 -15px no-repeat;
background:url(/img/panel_refresh.png) 0 -15px no-repeat;
}
.cmd-button{
width: 10%;
margin: 20px 2px 0 2px;
overflow: hidden;
color: rgb(49,123,249);
text-overflow: ellipsis;
white-space: nowrap;
background-color: white;
border: 1.5px solid rgba(49,123,249,.40);
}
.cmd-button:hover{
border: 1.5px solid rgb(0,0,0);
}
.cmd-button:active{
color: white;
background-color: rgb(49,123,249);
}
.clear-cache{
margin-right: 6px;
margin-left: 7px;
@ -153,16 +175,16 @@ background:url(/img/panel_refresh.png) 0 -15px no-repeat;
}
#fm{
height: 90%;
margin: 26px;
margin: 26px 26px 0 26px;
}
.fm_header{
font-weight: bold;
}
#path{
margin-left:1.5%;
margin-left:1.5%;
}
#left{
float:left;
.left, #left{
float:left;
}
/* фон файла, на котором курсор*/
.current-file{
@ -170,9 +192,11 @@ background:url(/img/panel_refresh.png) 0 -15px no-repeat;
}
.selected-file{
color:white;
background-color: rgb(49, 123, 249);
background-color: rgba(49, 123, 249, .40);
}
#right{
.right, #right{
float:right;
}
.panel{
@ -185,8 +209,15 @@ background:url(/img/panel_refresh.png) 0 -15px no-repeat;
}
/* информация о файлах и папках*/
.name{
float: left;
width: 37%;
float: left;
width: 37%;
/* если длина имени файла больше 16 символов
* отрезаем лишнее, оставляя лишь 16,
* и добавляем две точки и тайтл
*/
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.size{
float:left;

BIN
img/logo/cloudcmd.cdr Normal file

Binary file not shown.

BIN
img/logo/cloudcmd.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

View file

@ -1,19 +1,13 @@
<!DOCTYPE html>
<html manifest=/cloudcmd.appcache>
<html manifest="/cloudcmd.appcache">
<head>
<meta charset="utf-8">
<!-- mobile first design -->
<meta content="width=device-width,initial-scale=1" name="viewport" />
<title>Cloud Commander</title>
<link rel=stylesheet href=/css/reset.css>
<link rel=stylesheet href=/css/style.css>
<!--[if lt IE 9]>
<script src="//ie7-js.googlecode.com/svn/version/2.1(beta4)/IE9.js"></script>
<script src="//code.jquery.com/jquery-1.8.0.min.js" id=jquery-1_8_0_min_js ></script>
<![endif]-->
<link rel=stylesheet href="/css/reset.css">
<link rel=stylesheet href="/css/style.css">
</head>
<body>
@ -30,19 +24,23 @@
</ul>
<div id=fm class=no-js>
</div>
<div id=keyspanel class=hidden>
<button>F1 - help</button>
<button>F2 - rename</button>
<button>F3 - view</button>
<button>F4 - edit</button>
<button>F5 - copy</button>
<button>F6 - move</button>
<button>F7 - make dir</button>
<button>F8 - remove</button>
<div id=keyspanel>
<button id=f1 class=cmd-button>F1 - help</button>
<button id=f2 class=cmd-button>F2 - rename</button>
<button id=f3 class=cmd-button>F3 - view</button>
<button id=f4 class=cmd-button>F4 - edit</button>
<button id=f5 class=cmd-button>F5 - copy</button>
<button id=f6 class=cmd-button>F6 - move</button>
<button id=f7 class=cmd-button>F7 - make dir</button>
<button id=f8 class=cmd-button>F8 - remove</button>
</div>
<script src=client.js></script>
<script src=/lib/util.js></script>
<script src=/lib/client/dom.js></script>
<script src=/lib/client.js></script>
<!--[if lt IE 9]>
<script src=lib/client/ie.js id=ie_js ></script>
<script src="//ie7-js.googlecode.com/svn/version/2.1(beta4)/IE9.js" async></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js" id=jquery_min_js ></script>
<script src=/lib/client/ie.js id=ie_js ></script>
<![endif]-->
</body>
</html>

862
lib/client.js Normal file
View file

@ -0,0 +1,862 @@
/* Функция которая возвратит обьект CloudCommander
* @CloudFunc - обьект содержащий общий функционал
* клиентский и серверный
*/
var Util, DOM, CloudFunc, CloudCommander = (function(){
"use strict";
/* Клиентский обьект, содержащий функциональную часть*/
var CloudClient = {
/* Конструктор CloudClient, который выполняет
* весь функционал по инициализации
*/
init : null, /* start initialization */
KeyBinding : null, /* обьект обработки нажатий клавишь */
KeysPanel : null, /* panel with key buttons f1-f8 */
Config : null, /* function loads and shows config */
Editor : null, /* function loads and shows editor */
Storage : null, /* function loads storage */
Viewer : null, /* function loads and shows viewer */
Terminal : null, /* function loads and shows terminal*/
Menu : null, /* function loads and shows menu */
GoogleAnalytics : null,
_loadDir : null, /* Функция привязываеться ко всем
* ссылкам и
* загружает содержимое каталогов */
/* ОБЬЕКТЫ */
/* ПРИВАТНЫЕ ФУНКЦИИ */
/* функция загружает json-данные о файловой системе */
_ajaxLoad : null,
/* Функция генерирует JSON из html-таблицы файлов */
_getJSONfromFileTable : null,
/* функция меняет ссыки на ajax-овые */
_changeLinks : null,
/* КОНСТАНТЫ*/
/* название css-класа текущего файла*/
CURRENT_FILE : 'current-file',
LIBDIR : '/lib/',
LIBDIRCLIENT : '/lib/client/',
/* height of Cloud Commander
* seting up in init()
*/
HEIGHT : 0,
MIN_ONE_PANEL_WIDTH : 1155,
OLD_BROWSER : false,
HOST : (function(){
var lLocation = document.location;
return lLocation.protocol + '//' + lLocation.host;
})()
};
var cloudcmd = CloudClient,
/* глобальные переменные */
$, KeyBinding,
/* short names used all the time functions */
getByClass, getById;
/**
* function load modules
* @pParams = {name, path, func, dobefore, arg}
*/
var loadModule = function(pParams){
if(!pParams) return;
var lName = pParams.name,
lPath = pParams.path,
lFunc = pParams.func,
lDoBefore = pParams.dobefore;
if( Util.isString(pParams) )
lPath = pParams;
if(lPath && !lName){
lName = lPath[0].toUpperCase() + lPath.substring(1);
lName = Util.removeStr(lName, '.js');
var lSlash = lName.indexOf('/');
if(lSlash > 0){
var lAfterSlash = lName.substr(lSlash);
lName = Util.removeStr(lName, lAfterSlash);
}
}
if( !Util.isContainStr(lPath, '.js') )
lPath += '.js';
if(!cloudcmd[lName])
cloudcmd[lName] = function(pArg){
Util.exec(lDoBefore);
return DOM.jsload(cloudcmd.LIBDIRCLIENT + lPath, lFunc ||
function(){
Util.exec(cloudcmd[lName].init, pArg);
});
};
};
CloudClient.GoogleAnalytics = function(){
/* google analytics */
var lFunc = document.onmousemove;
document.onmousemove = function(){
setTimeout(function(){
DOM.jsload(cloudcmd.LIBDIRCLIENT + 'google_analytics.js');
},5000);
Util.exec(lFunc);
document.onmousemove = lFunc;
};
};
/**
* Функция привязываеться ко всем ссылкам и
* загружает содержимое каталогов
*/
CloudClient._loadDir = function(pLink,pNeedRefresh){
/* @pElem - элемент,
* для которого нужно
* выполнить загрузку
*/
return function(pEvent){
var lRet = true;
/* показываем гиф загрузки возле пути папки сверху*/
/* ctrl+r нажата? */
DOM.Images.showLoad(pNeedRefresh ? {top:true} : null);
var lPanel = DOM.getPanel(),
/* получаем имя каталога в котором находимся*/
lHref = DOM.getByClass('path', lPanel);
lHref = lHref[0].textContent;
lHref = CloudFunc.removeLastSlash(lHref);
var lSubstr = lHref.substr(lHref,lHref.lastIndexOf('/'));
lHref = lHref.replace(lSubstr+'/','');
/* загружаем содержимое каталога */
CloudClient._ajaxLoad(pLink, { refresh: pNeedRefresh });
/* получаем все элементы выделенной папки*/
/* при этом, если мы нажали обновить
* или <Ctrl>+R - ссылок мы ненайдём
* и заходить не будем
*/
var lA = DOM.getCurrentLink(this);
/* если нажали на ссылку на верхний каталог*/
if(lA && lA.textContent==='..' && lHref!=='/'){
/* функция устанавливает курсор на каталог
* с которого мы пришли, если мы поднялись
* в верх по файловой структуре
*/
CloudClient._currentToParent(lHref);
}
/* что бы не переходить по ссылкам
* а грузить всё ajax'ом,
* возвращаем false на событие
* onclick
*/
Util.setValue({
object : pEvent,
property: 'returnValue',
value : false
});
return lRet;
};
};
/**
* Function edits file name
*
* @param pParent - parent element
* @param pEvent
*/
CloudClient._editFileName = function(pParent){
var lA = DOM.getCurrentLink(pParent);
if (lA && lA.textContent !== '..'){
lA.contentEditable = true;
KeyBinding.unSet();
var lDocumentOnclick = document.onclick;
/* setting event handler onclick
* if user clicks somewhere keyBinded
* backs
*/
document.onclick = (function(){
var lA = DOM.getCurrentLink(pParent);
if (lA && lA.textContent !== '..')
lA.contentEditable = false;
KeyBinding.set();
/* backs old document.onclick
* and call it if it was
* setted up earlier
*/
document.onclick = lDocumentOnclick;
Util.exec(lDocumentOnclick);
});
}
};
/**
* Функция устанавливает текущим файлом, тот
* на который кликнули единожды
*/
CloudClient._setCurrent = function(){
/*
* @pFromEnter - если мы сюда попали
* из события нажатия на энтер -
* вызоветься _loadDir
*/
return function(pFromEnter){
var lCurrentFile = DOM.getCurrentFile();
if(lCurrentFile){/* устанавливаем курсор на файл, на который нажали */
DOM.setCurrentFile(this);
//if (DOM.isCurrentFile(this) &&
// !Util.isBoolean(pFromEnter)){
//var lParent = this;
//setTimeout(function(){
/* waiting a few seconds
* and if classes still equal
* make file name editable
* in other case
* double click event happend
*/
// if(DOM.getCurrentFile() === lParent)
// CloudClient._editFileName(lParent);
// },1000);
//}
}
/* если мы попали сюда с энтера */
if(pFromEnter===true){
var lResult = Util.exec( Util.bind(this.ondblclick, this) );
/* enter pressed on file */
if(!lResult){
var lA = DOM.getCurrentLink(this);
Util.exec( Util.bind(lA.ondblclick, this) );
}
}/* если мы попали сюда от клика мышки */
else
pFromEnter.returnValue = false;
/* что бы не переходить по ссылкам
* а грузить всё ajax'ом,
* возвращаем false на событие
* onclick
*/
return true;
};
};
/** функция устанавливает курсор на каталог
* с которого мы пришли, если мы поднялись
* в верх по файловой структуре
* @param pDirName - имя каталога с которого мы пришли
*/
CloudClient._currentToParent = function(pDirName){
/* опредиляем в какой мы панели:
* правой или левой
*/
var lPanel = DOM.getPanel();
/* убираем слэш с имени каталога*/
pDirName = pDirName.replace('/','');
var lRootDir = getById(pDirName + '(' + lPanel.id + ')');
/* if found li element with ID directory name
* set it to current file
*/
if(lRootDir){
DOM.setCurrentFile(lRootDir);
DOM.scrollIntoViewIfNeeded(lRootDir, true);
}
};
/** Конструктор CloudClient, который
* выполняет весь функционал по
* инициализации
*/
CloudClient.init = function(){
var lFunc = function(){
Util.loadOnLoad([
initKeysPanel,
initModules,
baseInit
]);
};
getByClass = DOM.getByClass;
getById = DOM.getById;
//Util.socketLoad();
if(!document.body.scrollIntoViewIfNeeded){
this.OLD_BROWSER = true;
DOM.jsload(CloudClient.LIBDIRCLIENT + 'ie.js',
function(){
DOM.jqueryLoad( lFunc );
});
}else
lFunc();
};
function initModules(pCallBack){
loadModule({
/* привязываем клавиши к функциям */
path : 'keyBinding.js',
func : function(){
KeyBinding = cloudcmd.KeyBinding;
KeyBinding.init();
}
});
DOM.ajax({
url:'/modules.json',
success: function(pModules){
var lStorage = 'storage/',
lShowLoadFunc = Util.retFunc( DOM.Images.showLoad ),
lDisableMenuFunc = function(){
var lFunc = document.oncontextmenu;
document.oncontextmenu = function(){
Util.exec(lFunc);
return cloudcmd.Menu.ENABLED || false;
};
},
lDoBefore = {
'editor/_codemirror' : lShowLoadFunc,
'menu' : lDisableMenuFunc,
'viewer' : lShowLoadFunc
},
lNames = {};
lNames[lStorage + '_dropbox'] = 'DropBox',
lNames[lStorage + '_github' ] = 'GitHub',
lDisableMenuFunc();
if( Util.isArray(pModules) )
for(var i = 0, n = pModules.length; i < n ; i++){
var lModule = pModules[i];
loadModule({
path : lModule,
dobefore : lDoBefore[lModule],
name : lNames[lModule]
});
}
Util.exec(pCallBack);
}
});
}
function initKeysPanel(pCallBack){
var lKeysPanel = {},
lFuncs =[
null,
null, /* f1 */
null, /* f2 */
cloudcmd.Viewer, /* f3 */
cloudcmd.Editor, /* f4 */
null, /* f5 */
null, /* f6 */
null, /* f7 */
DOM.promptRemoveCurrent,/* f8 */
];
for(var i = 1; i <= 8; i++){
var lButton = 'f' + i,
lEl = getById('f' + i);
lEl.onclick = lFuncs[i];
lKeysPanel[lButton] = lEl;
}
cloudcmd.KeysPanel = lKeysPanel;
Util.exec(pCallBack);
}
function baseInit(pCallBack){
if(applicationCache){
var lFunc = applicationCache.onupdateready;
applicationCache.onupdateready = function(){
console.log('app cacheed');
location.reload();
Util.exec(lFunc);
};
}
/* загружаем общие функции для клиента и сервера */
DOM.jsload(cloudcmd.LIBDIR + 'cloudfunc.js',function(){
DOM.addListener("popstate", function(pEvent) {
var lPath = pEvent.state;
if(lPath)
cloudcmd._ajaxLoad(lPath, {nohistory: true});
return true;
});
/* берём из обьекта window общий с сервером функционал */
CloudFunc = window.CloudFunc;
/* меняем ссылки на ajax'овые */
cloudcmd._changeLinks(CloudFunc.LEFTPANEL);
cloudcmd._changeLinks(CloudFunc.RIGHTPANEL);
/* устанавливаем переменную доступности кэша */
DOM.Cache.isAllowed();
/* Устанавливаем кэш корневого каталога */
if( !DOM.Cache.get('/') )
DOM.Cache.set('/', cloudcmd._getJSONfromFileTable());
});
/* устанавливаем размер высоты таблицы файлов
* исходя из размеров разрешения экрана
*/
/* выделяем строку с первым файлом */
var lFmHeader = getByClass('fm_header');
if(lFmHeader && lFmHeader[0].nextSibling)
DOM.setCurrentFile(lFmHeader[0].nextSibling);
/* показываем элементы, которые будут работать только, если есть js */
var lFM = getById('fm');
if(lFM)
lFM.className='localstorage';
/* формируем и округляем высоту экрана
* при разрешениии 1024x1280:
* 658 -> 700
*/
var lHeight = window.screen.height;
lHeight = lHeight - (lHeight/3).toFixed();
lHeight = (lHeight / 100).toFixed() * 100;
cloudcmd.HEIGHT = lHeight;
DOM.cssSet({id:'cloudcmd',
inner:
'.panel{' +
'height:' + lHeight +'px;' +
'}'
});
Util.exec(pCallBack);
cloudcmd.KeyBinding();
}
CloudClient.getConfig = function(pCallBack){
if(!cloudcmd.Config)
return DOM.ajax({
url:'/config.json',
success: function(pConfig){
cloudcmd.Config = pConfig;
Util.exec(pCallBack, pConfig);
}
});
else
Util.exec(pCallBack, cloudcmd.Config);
};
/* функция меняет ссыки на ajax-овые */
CloudClient._changeLinks = function(pPanelID){
/* назначаем кнопку очистить кэш и показываем её */
var lClearcache = getById('clear-cache');
if(lClearcache)
lClearcache.onclick = DOM.Cache.clear;
/* меняем ссылки на ajax-запросы */
var lPanel = getById(pPanelID),
a = lPanel.getElementsByTagName('a'),
/* номер ссылки иконки обновления страницы */
lREFRESHICON = 0,
/* путь в ссылке, который говорит
* что js отключен
*/
lNoJS_s = CloudFunc.NOJS,
/* right mouse click function varible */
lOnContextMenu_f = function(pEvent){
var lReturn_b = true;
KeyBinding.unSet();
/* getting html element
* currentTarget - DOM event
* target - jquery event
*/
var lTarget = pEvent.currentTarget || pEvent.target;
DOM.setCurrentFile(lTarget);
if(Util.isFunction(cloudcmd.Menu) ){
cloudcmd.Menu({
x: pEvent.x,
y: pEvent.y
});
/* disabling browsers menu*/
lReturn_b = false;
DOM.Images.showLoad();
}
return lReturn_b;
},
/* drag and drop function varible
* download file from browser to descktop
* in Chrome (HTML5)
*/
lOnDragStart_f = function(pEvent){
var lElement = pEvent.target,
lLink = lElement.href,
lName = lElement.textContent,
/* if it's directory - adding json extension */
lType = lElement.parentElement.nextSibling;
if(lType && lType.textContent === '<dir>'){
lLink = lLink.replace(lNoJS_s,'');
lName += '.json';
}
pEvent.dataTransfer.setData("DownloadURL",
'application/octet-stream' + ':' +
lName + ':' +
lLink);
},
lSetCurrentFile_f = function(pEvent){
var pElement = pEvent.target,
lTag = pElement.tagName;
if(lTag !== 'LI')
do{
pElement = pElement.parentElement;
lTag = pElement.tagName;
}while(lTag !== 'LI');
DOM.setCurrentFile(pElement);
},
lUrl = cloudcmd.HOST;
for(var i = 0, n = a.length; i < n ; i++)
{
/* убираем адрес хоста*/
var link = a[i].href.replace(lUrl,'');
/* ставим загрузку гифа на клик*/
if(i === lREFRESHICON){
a[i].onclick = CloudClient._loadDir(link, true);
a[i].parentElement.onclick = a[i].onclick;
}
/* устанавливаем обработчики на строку на одинарное и *
* двойное нажатие на левую кнопку мышки */
else{
var lLi = a[i].parentElement.parentElement;
/* if we in path changing onclick events */
if (lLi.className === 'path') {
a[i].onclick = CloudClient._loadDir(link);
}
else {
lLi.onclick = CloudClient._setCurrent();
lLi.onmousedown = lSetCurrentFile_f;
a[i].ondragstart = lOnDragStart_f;
/* if right button clicked menu will
* loads and shows
*/
lLi.oncontextmenu = lOnContextMenu_f;
/* если ссылка на папку, а не файл */
if(a[i].target !== '_blank'){
lLi.ondblclick = CloudClient._loadDir(link);
DOM.addListener('touchend',
CloudClient._loadDir(link),
false,
lLi
);
}
lLi.id = (a[i].title ? a[i].title : a[i].textContent) +
'(' + pPanelID + ')';
}
}
}
};
/**
* Функция загружает json-данные о Файловой Системе
* через ajax-запрос.
* @param path - каталог для чтения
* @param pOptions
* { refresh, nohistory } - необходимость обновить данные о каталоге
*/
CloudClient._ajaxLoad = function(pFullPath, pOptions){
if(!pOptions)
pOptions = {};
/* Отображаем красивые пути */
/* added supporting of russian language */
pFullPath = decodeURI(pFullPath);
var lPath = pFullPath,
lFSPath = pFullPath,
lFS_s = CloudFunc.FS,
lNoJS_s = CloudFunc.NOJS;
/*
* убираем значения, которые,
* говорят об отсутствии js
*/
if(lPath.indexOf(lNoJS_s) === lFS_s.length){
lPath = lFSPath = Util.removeStr(lPath, lNoJS_s);
}
if(lPath.indexOf(lFS_s) === 0){
lPath = lPath.replace(lFS_s,'');
if(lPath === '/')
pFullPath = '/';
}
console.log ('reading dir: "' + lPath + '";');
if(!pOptions.nohistory)
DOM.setHistory(pFullPath, null, pFullPath);
DOM.setTitle( CloudFunc.getTitle(lPath) );
/* если доступен localStorage и
* в нём есть нужная нам директория -
* читаем данные с него и
* выходим
* если стоит поле обязательной перезагрузки -
* перезагружаемся
*/
/* опредиляем в какой мы панели:
* правой или левой
*/
var lPanel = DOM.getPanel().id,
lError;
if(!pOptions.refresh && lPanel){
var lJSON = DOM.Cache.get(lPath);
if (lJSON){
/* переводим из текста в JSON */
if(window && !window.JSON){
lError = Util.tryCatchLog(function(){
lJSON = eval('('+lJSON+')');
});
}else
lJSON = JSON.parse(lJSON);
CloudClient._createFileTable(lPanel, lJSON);
CloudClient._changeLinks(lPanel);
return;
}
}
DOM.ajax({
url: lFSPath,
error: DOM.Images.showError,
success:function(data, textStatus, jqXHR){
/* если такой папки (или файла) нет
* прячем загрузку и показываем ошибку
*/
if(!jqXHR.responseText.indexOf('Error:'))
return DOM.showError(jqXHR);
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 )
DOM.Cache.set(lPath, lJSON_s);
}
});
};
/**
* Функция строит файловую таблицу
* @param pEleme - родительский элемент
* @param pJSON - данные о файлах
*/
CloudClient._createFileTable = function(pElem, pJSON){
var lElem = getById(pElem);
/* getting current element if was refresh */
var lPath = getByClass('path', lElem);
var lWasRefresh_b = lPath[0].textContent === pJSON[0].path;
var lCurrent;
if(lWasRefresh_b)
lCurrent = DOM.getCurrentFile();
/* говорим построителю,
* что бы он в нужный момент
* выделил строку с первым файлом
*/
/* очищаем панель */
var i = lElem.childNodes.length;
while(i--)
lElem.removeChild(lElem.lastChild);
/* заполняем панель новыми элементами */
lElem.innerHTML = CloudFunc.buildFromJSON(pJSON,true);
/* searching current file */
if(lWasRefresh_b && lCurrent){
for(i = 0; i < lElem.childNodes.length; i++)
if(lElem.childNodes[i].textContent === lCurrent.textContent){
lCurrent = lElem.childNodes[i];
break;
}
DOM.setCurrentFile(lCurrent);
//lCurrent.parentElement.focus();
}
};
/**
* Функция генерирует JSON из html-таблицы файлов и
* используеться при первом заходе в корень
*/
CloudClient._getJSONfromFileTable = function(){
var lLeft = getById('left'),
lPath = getByClass('path')[0].textContent,
lFileTable = [{
path:lPath,
size:'dir'
}],
lLI = lLeft.getElementsByTagName('li'),
j = 1; /* счётчик реальных файлов */
/* счётчик элементов файлов в DOM */
/* Если путь отличный от корневного
* второй элемент li - это ссылка на верхний
* каталог '..'
*/
/* пропускам Path и Header*/
for(var i = 2, n = lLI.length; i < n; i++){
var lChildren = lLI[i].children,
/* file attributes */
lAttr = {};
/* getting all elements to lAttr object */
for(var l = 0; l < lChildren.length; l++)
lAttr[lChildren[l].className] = lChildren[l];
/* mini-icon */
var lIsDir = lAttr['mini-icon directory'] ? true : false,
lName = lAttr.name;
if(lName)
lName = DOM.getByTag('a', lName);
/* if found link to folder
* cheking is it a full name
* or short
*/
/* if short we got title
* if full - getting textConent
*/
if(lName.length)
lName = lName[0];
lName = lName.title || lName.textContent;
/* если это папка - выводим слово dir вместо размера*/
var lSize = lIsDir ? 'dir' : lAttr.size.textContent,
lMode = lAttr.mode.textContent;
/* переводим права доступа в цыфровой вид
* для хранения в localStorage
*/
lMode = CloudFunc.convertPermissionsToNumberic(lMode);
lFileTable[ j++ ]={
name: lName,
size: lSize,
mode: lMode
};
}
return JSON.stringify(lFileTable);
};
return CloudClient;
})();
window.onload = function(){
'use strict';
/* базовая инициализация*/
CloudCommander.init();
/* загружаем Google Analytics */
CloudCommander.GoogleAnalytics();
};

1115
lib/client/dom.js Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,8 @@
var CloudCommander, CloudFunc, CodeMirror;
/* object contains editors CodeMirror
* and later will be Ace
*/
var CloudCommander, Util, DOM, CodeMirror;
/* object contains editors CodeMirror */
(function(){
"use strict";
var cloudcmd = CloudCommander,
Util = CloudCommander.Util,
KeyBinding = CloudCommander.KeyBinding,
CodeMirrorEditor = {},
FM,
@ -13,7 +10,14 @@ var CloudCommander, CloudFunc, CodeMirror;
CodeMirrorLoaded = false,
/* indicator says CodeMirror still loads */
Loading = false,
ReadOnly = false;
ReadOnly = false,
CallBacks = [
hide,
initCodeMirror,
show,
load
];
cloudcmd.Editor = {
get : (function(){
@ -21,13 +25,41 @@ var CloudCommander, CloudFunc, CodeMirror;
})
};
/* private functions */
function initCodeMirror(pValue){
if(!FM)
FM = Util.getById('fm');
function setCSS(){
var lPosition = DOM.getPanel().id,
lRet = DOM.cssSet({
id : 'editor',
inner : '.CodeMirror{' +
'font-family' + ': \'Droid Sans Mono\';' +
'font-size' + ': 15px;' +
'}' +
'.CodeMirror-scroll{' +
'height' + ':' + cloudcmd.HEIGHT + 'px' +
'}' +
'#CodeMirrorEditor{' +
'float' + ':' + lPosition +
'}'
});
CodeMirrorElement = Util.anyload({
return lRet;
}
/**
* function initialize CodeMirror
* @param {value, callback}
*/
function initCodeMirror(pData){
if(!pData)
pData = {};
if(!FM)
FM = DOM.getFM();
var lCSS = setCSS();
CodeMirrorElement = DOM.anyload({
name : 'div',
id : 'CodeMirrorEditor',
className : 'panel',
@ -36,65 +68,51 @@ var CloudCommander, CloudFunc, CodeMirror;
CodeMirrorEditor.CodeMirror = new CodeMirror(CodeMirrorElement,{
mode : 'javascript',
value : pValue,
value : pData.data,
theme : 'night',
lineNumbers : true,
//переносим длинные строки
//переносим длинные строки
lineWrapping: false,
autofocus : true,
extraKeys: {
//Сохранение
"Esc": CodeMirrorEditor.hide
'Esc': function(){
Util.exec(pData.callback);
DOM.remove(lCSS, document.head);
}
},
readOnly : ReadOnly
});
}
cloudcmd.Editor.dir = 'lib/client/editor/';
CodeMirrorEditor.dir = cloudcmd.Editor.dir + 'codemirror/';
/**
* function loads CodeMirror js and css files
*/
function load(pCallBack){
console.time('codemirror load');
var lDir = cloudcmd.LIBDIRCLIENT + 'editor/codemirror/',
lFiles =
[
[
lDir + 'codemirror.css',
lDir + 'theme/night.css',
lDir + 'mode/javascript.js',
],
lDir + 'codemirror.js'
];
DOM.anyLoadOnLoad(lFiles, function(){
console.timeEnd('codemirror load');
CodeMirrorLoaded = true;
Util.exec(pCallBack);
});
}
/* function loads CodeMirror js and css files */
CodeMirrorEditor.load = (function(){
/* function loads css files of CodeMirror */
var loadAll = function() {
Util.cssLoad([
{ src : CodeMirrorEditor.dir + 'codemirror.css'},
{ src : CodeMirrorEditor.dir + 'theme/night.css'}
]);
Util.cssSet({id:'editor',
inner : '.CodeMirror{' +
'font-family :\'Droid Sans Mono\';' +
'font-size :15px;' +
/* codemirror v3 */
//'height : ' + cloudcmd.HEIGHT + 'px' +
'}' +
'.CodeMirror-scroll{' +
'height : ' + (cloudcmd.HEIGHT) + 'px' +
'}' //+
/* codemirror v3 */
//'#CodeMirrorEditor{' +
// 'padding :20px 20px 20px 20px;' +
// '}'
});
Util.jsload(CodeMirrorEditor.dir +
'mode/javascript.js', function(){
CodeMirrorLoaded = true;
CodeMirrorEditor.show();
});
};
/* load CodeMirror main module */
Util.jsload(CodeMirrorEditor.dir + 'codemirror.js', loadAll);
});
/* function shows CodeMirror editor */
CodeMirrorEditor.show = (function(){
/* if CodeMirrorEditor is not loaded - loading him */
if(!CodeMirrorLoaded)
return this.load();
/**
* function shows CodeMirror editor
*/
function show(pCallBack){
/* if CodeMirror function show already
* called do not call it again
@ -104,32 +122,21 @@ var CloudCommander, CloudFunc, CodeMirror;
return;
/* getting link */
var lCurrentFile = Util.getCurrentFile(),
lA = Util.getCurrentLink(lCurrentFile);
lA = lA.href;
/* убираем адрес хоста*/
lA = '/' + lA.replace(document.location.href,'');
var lCurrentFile = DOM.getCurrentFile(),
lPath = DOM.getCurrentPath(lCurrentFile);
/* checking is this link is to directory */
var lSize = Util.getByClass('size', lCurrentFile);
var lSize = DOM.getByClass('size', lCurrentFile);
if(lSize){
lSize = lSize[0].textContent;
/* if directory - load json
* not html data
/* when folder view
* is no need to edit
* data
*/
if (lSize === '<dir>'){
var lIndexOfNOJS = lA.indexOf(CloudFunc.NOJS);
if (lIndexOfNOJS === CloudFunc.FS.length){
lA = lA.replace(CloudFunc.NOJS, '');
/* when folder view
* is no need to edit
* data
*/
ReadOnly = true;
}
}
if (lSize === '<dir>')
ReadOnly = true;
}
Loading = true;
@ -139,92 +146,95 @@ var CloudCommander, CloudFunc, CodeMirror;
400);
/* reading data from current file */
Util.ajax({
url:lA,
DOM.ajax({
url:lPath,
error: function(jqXHR, textStatus, errorThrown){
Loading = false;
return Util.Images.showError(jqXHR);
return DOM.Images.showError(jqXHR);
},
success:function(data, textStatus, jqXHR){
success:function(data, textStatus, jqXHR){
/* if we got json - show it */
if(typeof data === 'object')
data = JSON.stringify(data, null, 4);
initCodeMirror(data);
var lHided = DOM.hidePanel();
if(lHided){
Util.exec(pCallBack, data);
/* removing keyBinding if set */
KeyBinding.unSet();
}
/* removing keyBinding if set */
KeyBinding.unSet();
Util.hidePanel();
Util.Images.hideLoad();
DOM.Images.hideLoad();
Loading = false;
}
});
});
}
/* function hides CodeMirror editor */
CodeMirrorEditor.hide = (function() {
/**
* function hides CodeMirror editor
*/
function hide() {
var lElem = CodeMirrorElement;
KeyBinding.set();
if(lElem && FM)
FM.removeChild(lElem);
Util.showPanel();
});
DOM.showPanel();
}
cloudcmd.Editor.Keys = (function(pIsReadOnly){
ReadOnly = pIsReadOnly;
var lThis = this.CodeMirror;
/* loading js and css of CodeMirror */
lThis.show();
var key_event = function(pEvent){
/**
* function calls all CodeMirror editor functions
*/
CodeMirrorEditor.show = function(){
DOM.Images.showLoad();
Util.loadOnLoad( CallBacks );
};
/**
* function hides CodeMirror editor
*/
CodeMirrorEditor.hide = hide;
/**
* function bind keys
*/
cloudcmd.Editor.init = function(pReadOnly){
ReadOnly = pReadOnly;
CodeMirrorEditor.show();
CallBacks.pop();
var lKeyListener = function(pEvent){
/* если клавиши можно обрабатывать */
if( KeyBinding.get() ){
/* if f4 or f3 pressed */
var lF3 = cloudcmd.KEY.F3;
var lF4 = cloudcmd.KEY.F4;
//var lShow = lThis.show.bind(lThis);
var lShow = Util.bind(lThis.show, lThis);
var lF3 = cloudcmd.KEY.F3,
lF4 = cloudcmd.KEY.F4;
if(!pEvent.shiftKey){
if(!pEvent.shiftKey)
switch(pEvent.keyCode)
{
case lF4:
ReadOnly = false;
lShow();
CodeMirrorEditor.show();
break;
case lF3:
ReadOnly = true;
lShow();
CodeMirrorEditor.show();
break;
}
}
}
}
};
/* добавляем обработчик клавишь */
if (document.addEventListener)
document.addEventListener('keydown', key_event, false);
else{
var lFunc;
if(typeof document.onkeydown === 'function')
lFunc = document.onkeydown;
document.onkeydown = function(){
if(lFunc)
lFunc();
key_event();
};
}
});
DOM.addKeyListener( lKeyListener );
DOM.setButtonKey('f4', CodeMirrorEditor.show);
};
cloudcmd.Editor.CodeMirror = CodeMirrorEditor;
})();

View file

@ -1,169 +1,174 @@
.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;}
.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: auto;
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 {
position: absolute;
right: 0; top: 0;
overflow-x: hidden;
overflow-y: scroll;
z-index: 5;
}
.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 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;
overflow: visible;
}
.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: #997;}
.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;}
span.cm-invalidchar {color: #f00;}
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
@media print {
/* Hide the cursor when printing */
.CodeMirror pre.CodeMirror-cursor {
visibility: hidden;
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,361 +1,409 @@
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});
// TODO actually recognize syntax of TypeScript constructs
CodeMirror.defineMode("javascript", function(config, parserConfig) {
var indentUnit = config.indentUnit;
var jsonMode = parserConfig.json;
var isTS = parserConfig.typescript;
// 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"};
var jsKeywords = {
"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
};
// Extend the 'normal' keywords with the TypeScript language extensions
if (isTS) {
var type = {type: "variable", style: "variable-3"};
var tsKeywords = {
// object-like things
"interface": kw("interface"),
"class": kw("class"),
"extends": kw("extends"),
"constructor": kw("constructor"),
// scope modifiers
"public": kw("public"),
"private": kw("private"),
"protected": kw("protected"),
"static": kw("static"),
"super": kw("super"),
// types
"string": type, "number": type, "bool": type, "any": type
};
for (var attr in tsKeywords) {
jsKeywords[attr] = tsKeywords[attr];
}
}
return jsKeywords;
}();
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.lastType == "operator" || state.lastType == "keyword c" ||
/^[\[{}\(,;:]$/.test(state.lastType)) {
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.lastType != ".") ? 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() {
cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
cx.state.localVars = defaultVars;
}
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" && value == "?") return cont(expression, expect(":"), 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 maybetype(type) {
if (type == ":") return cont(typedef);
return pass();
}
function typedef(type) {
if (type == "variable"){cx.marked = "variable-3"; return cont();}
return pass();
}
function vardef1(type, value) {
if (type == "variable") {
register(value);
return isTS ? cont(maybetype, vardef2) : cont(vardef2);
}
return pass();
}
function vardef2(type, value) {
if (value == "=") return cont(expression, vardef2);
if (type == ",") return cont(vardef1);
}
function forspec1(type) {
if (type == "var") return cont(vardef1, expect(";"), forspec2);
if (type == ";") return cont(forspec2);
if (type == "variable") return cont(formaybein);
return cont(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 isTS ? cont(maybetype) : cont();}
}
// Interface
return {
startState: function(basecolumn) {
return {
tokenize: jsTokenBase,
lastType: null,
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.lastType = type;
return parseJS(state, style, type, content, stream);
},
indent: function(state, textAfter) {
if (state.tokenize == jsTokenComment) return CodeMirror.Pass;
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 + (state.lastType == "operator" || state.lastType == "," ? 4 : 0);
else if (type == "form" && firstChar == "{") return lexical.indented;
else if (type == "form") return lexical.indented + indentUnit;
else if (type == "stat")
return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? indentUnit : 0);
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});
CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });

View file

@ -1,326 +1,318 @@
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});
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: true
} : {
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;
},
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});

View file

@ -0,0 +1,21 @@
{
"name": "codemirror",
"version":"2.35.0",
"main": "codemirror.js",
"description": "In-browser code editing made bearable",
"licenses": [{"type": "MIT",
"url": "http://codemirror.net/LICENSE"}],
"directories": {"lib": "./lib"},
"scripts": {"test": "node ./test/run.js"},
"devDependencies": {"node-static": "0.6.0"},
"bugs": "http://github.com/marijnh/CodeMirror/issues",
"keywords": ["JavaScript", "CodeMirror", "Editor"],
"homepage": "http://codemirror.net",
"maintainers":[{"name": "Marijn Haverbeke",
"email": "marijnh@gmail.com",
"web": "http://marijnhaverbeke.nl"}],
"repositories": [{"type": "git",
"url": "http://marijnhaverbeke.nl/git/codemirror"},
{"type": "git",
"url": "https://github.com/marijnh/CodeMirror.git"}]
}

View file

@ -1,21 +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; }
/* 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; }

View file

@ -0,0 +1,23 @@
Copyright (C) 2012 by Marijn Haverbeke <marijnh@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Please note that some subdirectories of the CodeMirror distribution
include their own LICENSE files, and are released under different
licences.

View file

@ -0,0 +1,8 @@
# CodeMirror [![Build Status](https://secure.travis-ci.org/marijnh/CodeMirror.png?branch=master)](http://travis-ci.org/marijnh/CodeMirror)
CodeMirror is a JavaScript component that provides a code editor in
the browser. When a mode is available for the language you are coding
in, it will color your code, and optionally help with indentation.
The project page is http://codemirror.net
The manual is at http://codemirror.net/doc/manual.html

View file

@ -0,0 +1,231 @@
/* BASICS */
.CodeMirror {
/* Set height, width, borders, and global font properties here */
font-family: monospace;
height: 300px;
}
.CodeMirror-scroll {
/* Set scrolling behaviour here */
overflow: auto;
}
/* PADDING */
.CodeMirror-lines {
padding: 4px 0; /* Vertical padding around content */
}
.CodeMirror pre {
padding: 0 4px; /* Horizontal padding of content */
}
.CodeMirror-scrollbar-filler {
background-color: white; /* The little square between H and V scrollbars */
}
/* GUTTER */
.CodeMirror-gutters {
border-right: 1px solid #ddd;
background-color: #f7f7f7;
}
.CodeMirror-linenumbers {}
.CodeMirror-linenumber {
padding: 0 3px 0 5px;
min-width: 20px;
text-align: right;
color: #999;
}
/* CURSOR */
.CodeMirror pre.CodeMirror-cursor {
border-left: 1px solid black;
}
/* Shown when moving in bi-directional text */
.CodeMirror pre.CodeMirror-secondarycursor {
border-left: 1px solid silver;
}
.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);
}
/* Can style cursor different in overwrite (non-insert) mode */
.CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {}
/* DEFAULT THEME */
.cm-s-default .cm-keyword {color: #708;}
.cm-s-default .cm-atom {color: #219;}
.cm-s-default .cm-number {color: #164;}
.cm-s-default .cm-def {color: #00f;}
.cm-s-default .cm-variable {color: black;}
.cm-s-default .cm-variable-2 {color: #05a;}
.cm-s-default .cm-variable-3 {color: #085;}
.cm-s-default .cm-property {color: black;}
.cm-s-default .cm-operator {color: black;}
.cm-s-default .cm-comment {color: #a50;}
.cm-s-default .cm-string {color: #a11;}
.cm-s-default .cm-string-2 {color: #f50;}
.cm-s-default .cm-meta {color: #555;}
.cm-s-default .cm-error {color: #f00;}
.cm-s-default .cm-qualifier {color: #555;}
.cm-s-default .cm-builtin {color: #30a;}
.cm-s-default .cm-bracket {color: #997;}
.cm-s-default .cm-tag {color: #170;}
.cm-s-default .cm-attribute {color: #00c;}
.cm-s-default .cm-header {color: blue;}
.cm-s-default .cm-quote {color: #090;}
.cm-s-default .cm-hr {color: #999;}
.cm-s-default .cm-link {color: #00c;}
.cm-header, .cm-strong {font-weight: bold;}
.cm-em {font-style: italic;}
.cm-emstrong {font-style: italic; font-weight: bold;}
.cm-link {text-decoration: underline;}
.cm-invalidchar {color: #f00;}
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
/* STOP */
/* The rest of this file contains styles related to the mechanics of
the editor. You probably shouldn't touch them. */
.CodeMirror {
line-height: 1;
position: relative;
overflow: hidden;
}
.CodeMirror-scroll {
/* 30px is the magic margin used to hide the element's real scrollbars */
/* See overflow: hidden in .CodeMirror, and the paddings in .CodeMirror-sizer */
margin-bottom: -30px; margin-right: -30px;
padding-bottom: 30px; padding-right: 30px;
height: 100%;
outline: none; /* Prevent dragging from highlighting the element */
position: relative;
}
.CodeMirror-sizer {
position: relative;
}
/* The fake, visible scrollbars. Used to force redraw during scrolling
before actuall scrolling happens, thus preventing shaking and
flickering artifacts. */
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler {
position: absolute;
z-index: 6;
display: none;
}
.CodeMirror-vscrollbar {
right: 0; top: 0;
overflow-x: hidden;
overflow-y: scroll;
}
.CodeMirror-hscrollbar {
bottom: 0; left: 0;
overflow-y: hidden;
overflow-x: scroll;
}
.CodeMirror-scrollbar-filler {
right: 0; bottom: 0;
z-index: 6;
}
.CodeMirror-gutters {
position: absolute; left: 0; top: 0;
height: 100%;
z-index: 3;
}
.CodeMirror-gutter {
height: 100%;
float: left;
}
.CodeMirror-gutter-elt {
position: absolute;
cursor: default;
z-index: 4;
}
.CodeMirror-lines {
cursor: text;
}
.CodeMirror pre {
/* Reset some styles that the rest of the page might have set */
-moz-border-radius: 0; -webkit-border-radius: 0; -o-border-radius: 0; border-radius: 0;
border-width: 0;
background: transparent;
font-family: inherit;
font-size: inherit;
margin: 0;
white-space: pre;
word-wrap: normal;
line-height: inherit;
color: inherit;
z-index: 2;
position: relative;
overflow: visible;
}
.CodeMirror-wrap pre {
word-wrap: break-word;
white-space: pre-wrap;
word-break: normal;
}
.CodeMirror-linebackground {
position: absolute;
left: 0; right: 0; top: 0; bottom: 0;
z-index: 0;
}
.CodeMirror-linewidget {
position: relative;
z-index: 2;
}
.CodeMirror-wrap .CodeMirror-scroll {
overflow-x: hidden;
}
.CodeMirror-measure {
position: absolute;
width: 100%; height: 0px;
overflow: hidden;
visibility: hidden;
}
.CodeMirror-measure pre { position: static; }
.CodeMirror pre.CodeMirror-cursor {
position: absolute;
visibility: hidden;
border-right: none;
width: 0;
}
.CodeMirror-focused pre.CodeMirror-cursor {
visibility: visible;
}
.CodeMirror-selected { background: #d9d9d9; }
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
.CodeMirror-searching {
background: #ffa;
background: rgba(255, 255, 0, .4);
}
@media print {
/* Hide the cursor when printing */
.CodeMirror pre.CodeMirror-cursor {
visibility: hidden;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,409 @@
// TODO actually recognize syntax of TypeScript constructs
CodeMirror.defineMode("javascript", function(config, parserConfig) {
var indentUnit = config.indentUnit;
var jsonMode = parserConfig.json;
var isTS = parserConfig.typescript;
// 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"};
var jsKeywords = {
"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
};
// Extend the 'normal' keywords with the TypeScript language extensions
if (isTS) {
var type = {type: "variable", style: "variable-3"};
var tsKeywords = {
// object-like things
"interface": kw("interface"),
"class": kw("class"),
"extends": kw("extends"),
"constructor": kw("constructor"),
// scope modifiers
"public": kw("public"),
"private": kw("private"),
"protected": kw("protected"),
"static": kw("static"),
"super": kw("super"),
// types
"string": type, "number": type, "bool": type, "any": type
};
for (var attr in tsKeywords) {
jsKeywords[attr] = tsKeywords[attr];
}
}
return jsKeywords;
}();
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.lastType == "operator" || state.lastType == "keyword c" ||
/^[\[{}\(,;:]$/.test(state.lastType)) {
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.lastType != ".") ? 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() {
cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
cx.state.localVars = defaultVars;
}
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" && value == "?") return cont(expression, expect(":"), 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 maybetype(type) {
if (type == ":") return cont(typedef);
return pass();
}
function typedef(type) {
if (type == "variable"){cx.marked = "variable-3"; return cont();}
return pass();
}
function vardef1(type, value) {
if (type == "variable") {
register(value);
return isTS ? cont(maybetype, vardef2) : cont(vardef2);
}
return pass();
}
function vardef2(type, value) {
if (value == "=") return cont(expression, vardef2);
if (type == ",") return cont(vardef1);
}
function forspec1(type) {
if (type == "var") return cont(vardef1, expect(";"), forspec2);
if (type == ";") return cont(forspec2);
if (type == "variable") return cont(formaybein);
return cont(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 isTS ? cont(maybetype) : cont();}
}
// Interface
return {
startState: function(basecolumn) {
return {
tokenize: jsTokenBase,
lastType: null,
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.lastType = type;
return parseJS(state, style, type, content, stream);
},
indent: function(state, textAfter) {
if (state.tokenize == jsTokenComment) return CodeMirror.Pass;
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 + (state.lastType == "operator" || state.lastType == "," ? 4 : 0);
else if (type == "form" && firstChar == "{") return lexical.indented;
else if (type == "form") return lexical.indented + indentUnit;
else if (type == "stat")
return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? indentUnit : 0);
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});
CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });

View file

@ -0,0 +1,318 @@
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: true
} : {
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;
},
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});

View file

@ -0,0 +1,21 @@
{
"name": "codemirror",
"version":"3.0.2",
"main": "codemirror.js",
"description": "In-browser code editing made bearable",
"licenses": [{"type": "MIT",
"url": "http://codemirror.net/LICENSE"}],
"directories": {"lib": "./lib"},
"scripts": {"test": "node ./test/run.js"},
"devDependencies": {"node-static": "0.6.0"},
"bugs": "http://github.com/marijnh/CodeMirror/issues",
"keywords": ["JavaScript", "CodeMirror", "Editor"],
"homepage": "http://codemirror.net",
"maintainers":[{"name": "Marijn Haverbeke",
"email": "marijnh@gmail.com",
"web": "http://marijnhaverbeke.nl"}],
"repositories": [{"type": "git",
"url": "http://marijnhaverbeke.nl/git/codemirror"},
{"type": "git",
"url": "https://github.com/marijnh/CodeMirror.git"}]
}

View 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-gutters { background: #0a001f; border-right: 1px solid #aaa; }
.cm-s-night .CodeMirror-linenumber { 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; }

View file

@ -1,4 +1,4 @@
var CloudCommander, _gaq;
var DOM, _gaq;
(function(){
"use strict";
@ -19,5 +19,5 @@ var CloudCommander, _gaq;
return preventErrorAlert;
};
CloudCommander.Util.jsload('http://google-analytics.com/ga.js');
DOM.jsload('http://google-analytics.com/ga.js');
})();

View file

@ -1,5 +1,5 @@
/* script, fixes ie */
var CloudCommander, $;
var Util, DOM, $;
(function(){
"use strict";
@ -12,7 +12,7 @@ var CloudCommander, $;
{name: '', src: ' ',func: '', style: '', id: '', parent: '',
async: false, inner: 'id{color:red, }, class:'', not_append: false}
*/
lUtil.cssSet = function(pParams_o){
DOM.cssSet = function(pParams_o){
var lElement = '<style ';
if (pParams_o.id) lElement += 'id=' + pParams_o.id + ' ';
@ -26,18 +26,42 @@ var CloudCommander, $;
.appendTo(pParams_o.parent || document.head);
};
}
var lUtil = CloudCommander.Util;
/* setting function context (this) */
lUtil.bind = function(pFunction, pContext){
Util.bind = function(pFunction, pContext){
return $.proxy(pFunction, pContext);
};
/*
* typeof callback === "function" should not be used,
* as older browsers may report objects to be a function,
* which they are not
*/
Util.isFunction = $.isFunction;
if (!document.addEventListener)
/**
* safe add event listener on ie
* @param pType
* @param pListener
*/
DOM.addListener = function(pType, pListener){
var lRet = this,
lType = 'on' + pType,
lFunc = document[lType];
document[lType] = function(){
Util.exec(lFunc);
pListener();
};
return lRet;
};
if(!document.getElementsByClassName){
lUtil.getByClass = function(pClass, pElement){
var lClass = '.' + pClass;
var lResult;
DOM.getByClass = function(pClass, pElement){
var lClass = '.' + pClass,
lResult;
if(pElement)
lResult = $(pElement).find(lClass);
@ -48,7 +72,7 @@ var CloudCommander, $;
}
/* function polyfill webkit standart function */
lUtil.scrollIntoViewIfNeeded = function(pElement, centerIfNeeded){
DOM.scrollIntoViewIfNeeded = function(pElement, centerIfNeeded){
/*
https://gist.github.com/2581101
*/
@ -82,31 +106,116 @@ var CloudCommander, $;
alignWithTop = overTop && !overBottom;
if ((overTop || overBottom) && centerIfNeeded) {
if ((overTop || overBottom) && centerIfNeeded)
parent.scrollTop =
pElement.offsetTop -
parent.offsetTop -
parent.clientHeight / 2 -
parentBorderTopWidth +
pElement.clientHeight / 2;
}
if ((overLeft || overRight) && centerIfNeeded) {
if ((overLeft || overRight) && centerIfNeeded)
parent.scrollLeft =
pElement.offsetLeft -
parent.offsetLeft -
parent.clientWidth / 2 -
parentBorderLeftWidth +
pElement.clientWidth / 2;
}
if (( overTop ||
overBottom ||
overLeft ||
overRight) &&
!centerIfNeeded) {
if ( (overTop || overBottom || overLeft || overRight) &&
!centerIfNeeded)
pElement.scrollIntoView(alignWithTop);
}
};
if(!document.body.classList){
DOM.addClass = function(pElement, pClass){
var lSpaceChar = '',
lClassName = pElement.className,
lRet_b = true;
if(lClassName) lSpaceChar = ' ';
if( lClassName.indexOf(pClass) < 0 )
pElement.className += lSpaceChar + pClass;
else
lRet_b = false;
};
DOM.removeClass = function(pElement, pClass){
var lClassName = pElement.className;
if(lClassName.length > pClass.length)
pElement.className = lClassName.replace(pClass, '');
};
}
if(!window.XMLHttpRequest)
DOM.ajax = $.ajax;
if(!window.localStorage){
DOM.Cache = function(){
/* приватный переключатель возможности работы с кэшем */
var CacheAllowed,
Data = {};
/* функция проверяет возможно ли работать с кэшем каким-либо образом */
this.isAllowed = function(){
return CacheAllowed;
};
this.setAllowed = function(){
CacheAllowed = true;
};
this.UnSetAllowed = function(){
CacheAllowed = false;
};
/** remove element */
this.remove = function(pItem){
var lRet = this;
if(CacheAllowed)
delete Data[pItem];
return lRet;
};
/** если доступен localStorage и
* в нём есть нужная нам директория -
* записываем данные в него
*/
this.set = function(pName, pData){
var lRet = this;
if(CacheAllowed && pName && pData)
Data[pName] = pData;
return lRet;
},
/** Если доступен Cache принимаем из него данные*/
this.get = function(pName){
var lRet = false;
if(CacheAllowed)
lRet = Data[pName];
return lRet;
},
/** функция чистит весь кэш для всех каталогов*/
this.clear = function(){
var lRet = this;
if(CacheAllowed)
Data = {};
return lRet;
};
};
DOM.Cache = new DOM.Cache();
}
})();

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,12 @@
var CloudCommander;
var CloudCommander, Util, DOM;
(function(){
"use strict";
var cloudcmd = CloudCommander;
DOM.Images.hideLoad();
var cloudcmd = CloudCommander,
/* private property set or set key binding */
var keyBinded;
keyBinded;
/* Key constants*/
cloudcmd.KEY = {
@ -24,6 +25,8 @@ var CloudCommander;
D : 68,
G : 71,
O : 79,
Q : 81,
R : 82,
@ -50,9 +53,7 @@ var CloudCommander;
KeyBinding.unSet = function(){keyBinded = false;};
KeyBinding.init = (function(){
var Util = cloudcmd.Util;
KeyBinding.init = function(){
/* saving state of tabs varibles */
var lTabPanel = {
left : 0,
@ -60,20 +61,29 @@ var CloudCommander;
};
var key_event = function(event){
var key_event = function(event){
/* получаем выдленный файл*/
var lCurrentFile = Util.getCurrentFile(),
var lCurrentFile = DOM.getCurrentFile(),
lName, i;
/* если клавиши можно обрабатывать*/
if(keyBinded && event){
var lKeyCode = event.keyCode;
/* open configuration window */
if(event.keyCode === KEY.O && event.altKey){
if(lKeyCode === KEY.O && event.altKey){
console.log('openning config window...');
Util.Images.showLoad();
if (typeof cloudcmd.Config === 'function')
cloudcmd.Config();
DOM.Images.showLoad({top: true});
Util.exec(cloudcmd.Config);
}
else if(lKeyCode === KEY.G && event.altKey)
Util.exec(cloudcmd.GitHub);
else if(lKeyCode === KEY.D && event.altKey){
Util.exec(cloudcmd.DropBox);
event.preventDefault();
}
/* если нажали таб:
@ -81,81 +91,65 @@ var CloudCommander;
* правую панель, если
* мы были на левой и
* наоборот
*/
else if(event.keyCode === KEY.TAB){
*/
else if(lKeyCode === KEY.TAB){
console.log('Tab pressed');
try{
Util.tryCatchLog(function(){
/* changing parent panel of curent-file */
var lPanel = Util.getPanel();
var lId = lPanel.id;
var lPanel = DOM.getPanel(),
lId = lPanel.id;
lTabPanel[lId] = lCurrentFile;
lPanel = Util.getPanel({active:false});
lPanel = DOM.getPanel({active:false});
lId = lPanel.id;
if(lTabPanel[lId])
Util.setCurrentFile(lTabPanel[lId]);
DOM.setCurrentFile(lTabPanel[lId]);
else{
var lFirstFileOnList = Util.getByTag('li', lPanel)[2];
var lFirstFileOnList = DOM.getByTag('li', lPanel)[2];
Util.setCurrentFile(lFirstFileOnList);
DOM.setCurrentFile(lFirstFileOnList);
}
}catch(error){console.log(error);}
});
event.preventDefault();//запрет на дальнейшее действие
}
/* if f2 pressed */
else if(event.keyCode === KEY.F2){
}
else if(event.keyCode === KEY.Delete)
Util.removeCurrent(lCurrentFile);
else if(lKeyCode === KEY.Delete)
DOM.promptRemoveCurrent(lCurrentFile);
/* if f3 or shift+f3 or alt+f3 pressed */
else if(event.keyCode === KEY.F3){
Util.Images.showLoad();
var lViewer = cloudcmd.Viewer;
var lEditor = cloudcmd.Editor;
if(event.shiftKey && typeof lViewer === 'function')
lViewer(lCurrentFile);
else if (typeof lEditor === 'function')
lEditor(true);
else if(lKeyCode === KEY.F3){
var lEditor = cloudcmd[event.shiftKey ?
'Viewer' : 'Editor'];
Util.exec(lEditor, true);
event.preventDefault();//запрет на дальнейшее действие
}
/* if f4 pressed */
else if(event.keyCode === KEY.F4) {
Util.Images.showLoad();
else if(lKeyCode === KEY.F4) {
DOM.Images.showLoad();
if (typeof cloudcmd.Editor === 'function')
cloudcmd.Editor();
Util.exec(cloudcmd.Editor);
event.preventDefault();//запрет на дальнейшее действие
}
else if(event.keyCode === KEY.F10 &&
event.shiftKey){
if (typeof cloudcmd.Menu === 'function')
cloudcmd.Menu();
event.preventDefault();//запрет на дальнейшее действие
else if(lKeyCode === KEY.F10 && event.shiftKey){
Util.exec(cloudcmd.Menu);
event.preventDefault();//запрет на дальнейшее действие
}
else if (event.keyCode === KEY.TRA){
Util.Images.showLoad({top: true});
if(typeof cloudcmd.Terminal === 'function')
cloudcmd.Terminal();
else if (lKeyCode === KEY.TRA){
DOM.Images.showLoad({top: true});
Util.exec(cloudcmd.Terminal);
}
/* навигация по таблице файлов*/
/* если нажали клавишу вверх*/
else if(event.keyCode === KEY.UP){
else if(lKeyCode === KEY.UP){
/* если ненайдены выделенные файлы - выходим*/
if(!lCurrentFile) return;
@ -166,14 +160,14 @@ var CloudCommander;
lCurrentFile = lCurrentFile.previousSibling;
if(lCurrentFile){
/* выделяем предыдущую строку*/
Util.setCurrentFile(lCurrentFile);
DOM.setCurrentFile(lCurrentFile);
}
event.preventDefault();//запрет на дальнейшее действие
}
/* если нажали клавишу в низ*/
else if(event.keyCode === KEY.DOWN){
else if(lKeyCode === KEY.DOWN){
/* если ненайдены выделенные файлы - выходим*/
if(!lCurrentFile)return;
@ -181,7 +175,7 @@ var CloudCommander;
/* если это не последняя строка */
if(lCurrentFile){
/* выделяем следующую строку*/
Util.setCurrentFile(lCurrentFile);
DOM.setCurrentFile(lCurrentFile);
event.preventDefault();//запрет на дальнейшее действие
}
@ -191,7 +185,7 @@ var CloudCommander;
* переходим к самому верхнему
* элементу
*/
else if(event.keyCode === KEY.HOME){
else if(lKeyCode === KEY.HOME){
/* получаем первый элемент
* пропускаем путь и заголовки столбиков
* выделяем верхий файл
@ -202,7 +196,7 @@ var CloudCommander;
/* set current file and
* move scrollbar to top
*/
Util.setCurrentFile(lCurrentFile);
DOM.setCurrentFile(lCurrentFile);
event.preventDefault();//запрет на дальнейшее действие
}
@ -210,7 +204,7 @@ var CloudCommander;
/* если нажали клавишу End
* выделяем последний элемент
*/
else if( event.keyCode === KEY.END){
else if(lKeyCode === KEY.END){
/* выделяем самый нижний файл */
lCurrentFile = lCurrentFile.
parentElement.lastElementChild;
@ -218,22 +212,22 @@ var CloudCommander;
/* set current file and
* move scrollbar to bottom
*/
Util.setCurrentFile(lCurrentFile);
DOM.setCurrentFile(lCurrentFile);
event.preventDefault();//запрет на дальнейшее действие
}
/* если нажали клавишу page down
* проматываем экран
*/
else if(event.keyCode === KEY.PAGE_DOWN){
Util.getPanel().scrollByPages(1);
else if(lKeyCode === KEY.PAGE_DOWN){
DOM.getPanel().scrollByPages(1);
for(i=0; i<30; i++){
if(!lCurrentFile.nextSibling) break;
lCurrentFile = lCurrentFile.nextSibling;
}
Util.setCurrentFile(lCurrentFile);
DOM.setCurrentFile(lCurrentFile);
event.preventDefault();//запрет на дальнейшее действие
}
@ -241,39 +235,44 @@ var CloudCommander;
/* если нажали клавишу page up
* проматываем экран
*/
else if(event.keyCode === KEY.PAGE_UP){
Util.getPanel().scrollByPages(-1);
else if(lKeyCode === KEY.PAGE_UP){
DOM.getPanel().scrollByPages(-1);
for(i=0; i<30; i++){
if(!lCurrentFile.previousSibling) break;
else try{
lCurrentFile
.previousSibling
.previousSibling
var lC = lCurrentFile,
tryCatch = function(pCurrentFile){
Util.tryCatch(function(){
return pCurrentFile
.previousSibling
.previousSibling;
}
catch(pError){
break;
}
lCurrentFile = lCurrentFile.previousSibling;
.previousSibling
.previousSibling
.previousSibling;
});
};
for(i = 0; i < 30; i++){
if(!lC.previousSibling || tryCatch(lC) ) break;
lC = lC.previousSibling;
}
Util.setCurrentFile(lCurrentFile);
DOM.setCurrentFile(lC);
event.preventDefault();//запрет на дальнейшее действие
}
/* если нажали Enter - открываем папку*/
else if(event.keyCode === KEY.ENTER){
else if(lKeyCode === KEY.ENTER){
/* если ненайдены выделенные файлы - выходим*/
if(!lCurrentFile)return;
/* из него достаём спан с именем файла*/
lName = Util.getByClass('name', lCurrentFile);
lName = DOM.getByClass('name', lCurrentFile);
/* если нету (что вряд ли) - выходим*/
if(!lName)return false;
/* достаём все ссылки*/
var lATag = Util.getByTag('a', lName[0]);
var lATag = DOM.getByTag('a', lName[0]);
/* если нету - выходим */
if(!lATag)return false;
@ -282,7 +281,8 @@ var CloudCommander;
* (opera, ie), вызываем событие onclick,
*/
if(lCurrentFile.onclick)lCurrentFile.onclick(true);
if(lCurrentFile.onclick)
lCurrentFile.onclick(true);
else try{
lATag[0].click();
}
@ -300,44 +300,42 @@ var CloudCommander;
* сервера, а не из кэша
* (обновляем кэш)
*/
else if(event.keyCode === KEY.R &&
else if(lKeyCode === KEY.R &&
event.ctrlKey){
console.log('<ctrl>+r pressed');
console.log('reloading page...');
console.log('press <alt>+q to remove all key-handlers');
console.log('<ctrl>+r pressed\n' +
'reloading page...\n' +
'press <alt>+q to remove all key-handlers');
/* Программно нажимаем на кнопку перезагрузки
* содержимого каталога
*/
var lRefreshIcon = Util.getRefreshButton();
var lRefreshIcon = DOM.getRefreshButton();
if(lRefreshIcon){
/* получаем название файла*/
var lSelectedName = Util
.getByTag('a', lCurrentFile)[0].textContent;
var lSelectedName = DOM.getCurrentName();
/* если нашли элемент нажимаем него
* а если не можем - нажимаем на
* ссылку, на которую повешен eventHandler
* onclick
*/
lRefreshIcon.onclick();
lRefreshIcon.onclick();
cloudcmd._currentToParent(lSelectedName);
event.preventDefault();//запрет на дальнейшее действие
}
}
/* если нажали <ctrl>+d чистим кэш */
else if(event.keyCode === KEY.D &&
else if(lKeyCode === KEY.D &&
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');
console.log('<ctrl>+d pressed\n' +
'clearing cache...\n' +
'press <alt>+q to remove all key-handlers');
var lClearCache = DOM.getById('clear-cache');
if(lClearCache && lClearCache.onclick)
lClearCache.onclick();
event.preventDefault();//запрет на дальнейшее действие
}
@ -345,18 +343,16 @@ var CloudCommander;
* убираем все обработчики
* нажатий клавиш
*/
else if(event.keyCode === KEY.Q &&
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');
/* обработчик нажатий клавиш снят*/
keyBinded = false;
event.preventDefault();//запрет на дальнейшее действие
else if(lKeyCode === KEY.Q && event.altKey){
console.log('<alt>+q pressed\n' +
'<ctrl>+r reload key-handerl - removed' +
'<ctrl>+s clear cache key-handler - removed'+
'press <alt>+s to to set them');
/* обработчик нажатий клавиш снят*/
keyBinded = false;
event.preventDefault();//запрет на дальнейшее действие
}
}
@ -364,28 +360,23 @@ var CloudCommander;
* устанавливаем все обработчики
* нажатий клавиш
*/
else if(event.keyCode === KEY.S &&
event.altKey){
/* обрабатываем нажатия на клавиши*/
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');
event.preventDefault();//запрет на дальнейшее действие
else if(event.keyCode === KEY.S && event.altKey){
/* обрабатываем нажатия на клавиши*/
keyBinded = true;
console.log('<alt>+s pressed\n' +
'<ctrl>+r reload key-handerl - set\n' +
'<ctrl>+s clear cache key-handler - set\n' +
'press <alt>+q to remove them');
event.preventDefault();//запрет на дальнейшее действие
}
return false;
};
/* добавляем обработчик клавишь */
if(document.addEventListener)
document.addEventListener('keydown', key_event, false);
else document.onkeydown = key_event;
DOM.addKeyListener(key_event);
/* клавиши назначены*/
keyBinded = true;
});
};
})();

View file

@ -1,40 +1,41 @@
/* object contains jQuery-contextMenu
* https://github.com/medialize/jQuery-contextMenu
*/
var CloudCommander, $;
var CloudCommander, Util, DOM, CloudFunc, $;
(function(){
"use strict";
var cloudcmd = CloudCommander;
var KeyBinding = cloudcmd.KeyBinding;
cloudcmd.Menu = {};
var Util = CloudCommander.Util;
CloudCommander.Menu.dir = './lib/client/menu/';
var cloudcmd = CloudCommander,
KeyBinding = cloudcmd.KeyBinding,
MenuSeted = false,
Menu = {},
Position;
Menu.dir = '/lib/client/menu/';
/* enable and disable menu constant */
CloudCommander.Menu.ENABLED = false;
Menu.ENABLED = false;
/* PRIVATE FUNCTIONS */
/** function shows editor
* @param pReadOnly
*/
function showEditor(pReadOnly){
DOM.Images.showLoad();
var lEditor = cloudcmd[pReadOnly ? 'Viewer' : 'Editor'],
lResult = Util.exec(lEditor, pReadOnly);
if(!lResult){
lEditor = lEditor.get();
if(lEditor)
Util.exec(lEditor.show);
}
}
CloudCommander.Menu.showEditor = (function(pReadOnly){
Util.Images.showLoad();
var lEditor = pReadOnly ? cloudcmd.Viewer : cloudcmd.Editor;
var lCurrent = Util.getCurrentFile();
if(lCurrent){
if(typeof lEditor === 'function')
lEditor(lCurrent);
else{
lEditor = lEditor.get();
if(lEditor && lEditor.show)
lEditor.show(lCurrent);
}
}
});
/* function return configureation for menu */
CloudCommander.Menu.getConfig = (function(){
/** function return configureation for menu */
function getConfig (){
return{
// define which elements trigger this menu
selector: 'li',
@ -45,87 +46,114 @@ var CloudCommander, $;
KeyBinding.set();
},
// define the elements of the menu
items: {
view: {name: 'View', callback: function(key, opt){
CloudCommander.showEditor(true);
showEditor(true);
}},
edit: {name: 'Edit', callback: function(key, opt){
CloudCommander.showEditor(false);
showEditor();
}},
'delete': {name: 'Delete',
delete: {name: 'Delete',
callback: function(key, opt){
console.log('delete menu item choosen');
DOM.promptRemoveCurrent();
}},
upload: {
name: "Upload to",
items: {
'upload_to_gist': {name: 'Gist',
callback: function(key, opt){
var lCurrent = DOM.getCurrentFile(),
lPath = DOM.getCurrentPath(),
lName = DOM.getCurrentName(lCurrent);
DOM.ajax({
url : lPath,
error : DOM.Images.showError,
success : function(data, textStatus, jqXHR){
if( Util.isObject(data) )
data = JSON.stringify(data, null, 4);
var lGitHub = cloudcmd.GitHub;
if('init' in lGitHub)
lGitHub.createGist(data, lName);
else
Util.exec(cloudcmd.GitHub,
function(){
lGitHub.createGist(data, lName);
});
}
});
Util.log('Uploading to gist...');
}
},
},
},
download: {name: 'Download',callback: function(key, opt){
Util.Images.showLoad();
DOM.Images.showLoad();
var lCurrent = Util.getCurrentFile();
var lLink = Util.getByTag('a', lCurrent)[0].href;
var lCurrent = DOM.getCurrentFile(),
lLink = DOM.getByTag('a', lCurrent)[0].href;
console.log('downloading file ' + lLink +'...');
lLink = lLink + '?download';
var lId = Util.getIdBySrc(lLink);
lLink = lLink + '?download';
lLink = Util.removeStr( lLink, CloudFunc.NOJS );
var lId = DOM.getIdBySrc(lLink);
if(!Util.getById(lId)){
var lDownload = Util.anyload({
if(!DOM.getById(lId)){
var lDownload = DOM.anyload({
name : 'iframe',
async : false,
className : 'hidden',
src : lLink,
func : function(){
Util.Images.hideLoad();
DOM.Images.hideLoad();
}
});
Util.Images.hideLoad();
DOM.Images.hideLoad();
setTimeout(function() {
document.body.removeChild(lDownload);
}, 10000);
}
else
Util.Images.showError({
DOM.Images.showError({
responseText: 'Error: You trying to' +
'download same file to often'});
}}
}
};
});
}
/* function loads css and js of Menu
* @pParent - this
* @pPosition - position of menu
/** function loads css and js of Menu
* @param pCallBack
*/
CloudCommander.Menu.load = (function(pPosition){
var lThis = this;
var ljsLoad_f = function(){
var lUISrc = lThis.dir + 'ui.position.js';
var lMenuSrc = lThis.dir + 'contextMenu.js';
Util.jsload(lUISrc, function(){
Util.jsload(lMenuSrc, lThis.show(lThis, pPosition));
});
};
function load(pCallBack){
console.time('menu load');
var lSrc = this.dir + 'contextMenu.css';
var lDir = Menu.dir,
lFiles = [
lDir + 'contextMenu.js',
lDir + 'contextMenu.css'
];
Util.cssLoad({
src : lSrc,
func : {
onload: function(){
Util.jqueryLoad(ljsLoad_f);
}
}
DOM.anyLoadInParallel(lFiles, function(){
console.timeEnd('menu load');
Util.exec(pCallBack);
});
});
}
CloudCommander.Menu.set = (function(){
if(!this.seted){
$.contextMenu(this.getConfig());
function set(){
if(!MenuSeted){
$.contextMenu(getConfig());
var lFunc_f = document.onclick;
/*
@ -142,7 +170,7 @@ var CloudCommander, $;
*/
document.onclick = function(pEvent){
if(pEvent && pEvent.x && pEvent.y){
var lLayer = Util.getById('context-menu-layer');
var lLayer = DOM.getById('context-menu-layer');
if(lLayer){
var lStyle;
@ -154,9 +182,9 @@ var CloudCommander, $;
.replace('z-index: 1', 'z-index:-1');
/* get element by point */
var lElement = document.elementFromPoint(pEvent.x, pEvent.y);
var lTag = lElement.tagName;
var lParent;
var lElement = document.elementFromPoint(pEvent.x, pEvent.y),
lTag = lElement.tagName,
lParent;
if(lTag === 'A' || lTag === 'SPAN'){
if (lElement.tagName === 'A')
@ -165,7 +193,7 @@ var CloudCommander, $;
lParent = lElement.parentElement;
if(lParent.className === '')
Util.setCurrentFile(lParent);
DOM.setCurrentFile(lParent);
}
/* show invisible menu layer */
@ -183,73 +211,53 @@ var CloudCommander, $;
}
};
this.seted = true;
MenuSeted = true;
}
});
}
CloudCommander.Menu.seted = false;
/* function shows menu for the first time
/** function shows menu for the first time
* right away after loading
*/
CloudCommander.Menu.show = (function(pThis, pPosition){
return function(){
pThis.set();
Util.Images.hideLoad();
if(pPosition && pPosition.x && pPosition.y)
$('li').contextMenu(pPosition);
else
$('li').contextMenu();
};
});
Menu.show = function(){
set();
DOM.Images.hideLoad();
if(Position && Position.x && Position.y)
$('li').contextMenu(Position);
else
$('li').contextMenu();
};
/* key binding function */
CloudCommander.Menu.Keys = (function(pPosition){
"use strict";
var lFunc = document.oncontextmenu;
document.oncontextmenu = function(){
if(typeof lFunc === 'function')
lFunc();
return CloudCommander.Menu.ENABLED;
};
Menu.init = function(pPosition){
Position = pPosition;
var key_event = (function(pEvent){
DOM.jqueryLoad( Util.retLoadOnLoad([
Menu.show,
load
]));
var key_event = function(pEvent){
/* если клавиши можно обрабатывать */
if( KeyBinding.get() )
if( KeyBinding.get() ){
/* if shift + F10 pressed */
if(pEvent.keyCode === CloudCommander.KEY.F10 &&
pEvent.shiftKey){
var lCurrent = Util.getCurrentFile();
if(lCurrent)
$(lCurrent).contextMenu();
pEvent.preventDefault();
if(pEvent.keyCode === cloudcmd.KEY.F10 && pEvent.shiftKey){
var lCurrent = DOM.getCurrentFile();
if(lCurrent)
$(lCurrent).contextMenu();
pEvent.preventDefault();
}
}
});
else if (pEvent.keyCode === cloudcmd.KEY.ESC)
KeyBinding.set();
};
/* добавляем обработчик клавишь */
if (document.addEventListener)
document.addEventListener('keydown', key_event.bind(this),false);
else{
lFunc;
if(typeof document.onkeydown === 'function')
lFunc = document.onkeydown;
document.onkeydown = function(){
if(lFunc)
lFunc();
key_event();
};
/* showing context menu preview*/
CloudCommander.Menu.show();
}
CloudCommander.Menu.load(pPosition);
});
DOM.addKeyListener( key_event );
};
cloudcmd.Menu = Menu;
})();

View file

@ -1,24 +1,31 @@
/* module make possible connectoin thrue socket.io on a client */
var CloudCommander, io;
var CloudCommander, DOM, Util, io;
(function(){
"use strict";
var cloudcmd = CloudCommander,
Util = cloudcmd.Util,
Messages = [],
socket,
JqueryTerminal = cloudcmd.Terminal.JqueryTerminal;
JqueryTerminal;
Util.jsload("/socket.io/lib/socket.io.js", {
function getJqueryTerminal(){
return cloudcmd.Terminal.JqueryTerminal;
}
DOM.jsload('/socket.io/lib/socket.io.js', {
onload : function(){
socket = io.connect(document.location.hostname);
cloudcmd.Socket = socket;
socket.on('connect', function () {
outToTerminal({stdout: 'socket connected'});
JqueryTerminal = getJqueryTerminal();
JqueryTerminal.Term.resume();
if(JqueryTerminal){
outToTerminal({stdout: 'socket connected'});
JqueryTerminal.Term.resume();
}
});
socket.on('message', function (msg) {
@ -29,9 +36,13 @@ var CloudCommander, io;
});
socket.on('disconnect', function () {
outToTerminal({stderr: 'socket disconected'});
JqueryTerminal = getJqueryTerminal();
JqueryTerminal.Term.pause();
if(JqueryTerminal){
outToTerminal({stderr: 'socket disconected'});
JqueryTerminal.Term.pause();
}
});
},
@ -42,35 +53,44 @@ var CloudCommander, io;
});
function outToTerminal(pMsg){
var lTerm = JqueryTerminal.Term,
var lTerm,
lResult = true;
JqueryTerminal = getJqueryTerminal();
if(JqueryTerminal)
lTerm = JqueryTerminal.Term;
if(lTerm){
var lStdout,
lStderr;
if(Messages.length){
/* show oll msg from buffer */
for(var i=0; i < Messages.length; i++){
for(var i = 0, n = Messages.length; i < n; i++){
lStdout = Messages[i].stdout;
lStderr = Messages[i].stderr;
if(lStdout)
lTerm.echo(lStdout);
if(lStderr)
if(lStderr){
/* if it's object - convert is to string' */
if( Util.isObject(lStderr) )
lStderr = JSON.Stringify(lStderr);
lTerm.error(lStderr);
}
}
Messages = [];
}
lStdout = pMsg.stdout;
lStderr = pMsg.stderr;
if(lStdout)
lResult = lTerm.echo(lStdout);
if(lStderr)
lResult = lTerm.error(lStderr);
if(lStderr && lStderr.code !== 1)
lResult = lTerm.error(lStderr.toString());
}
else{
/* if term not accesable

View file

@ -0,0 +1,55 @@
var CloudCommander, DOM, Dropbox;
/* module for work with github */
(function(){
"use strict";
var cloudcmd = CloudCommander,
CHOOSER_API = 'https://www.dropbox.com/static/api/1/dropbox.js',
CLIENT_ID,
DropBoxStore = {},
options = {
linkType: "direct",
success: function(files) {
console.log("Here's the file link:" + files[0].link);
},
cancel: function() {
console.log('Chose something');
}
};
/* PRIVATE FUNCTIONS */
/**
* function loads dropbox.js
*/
function load(){
console.time('dropbox load');
cloudcmd.getConfig(function(pConfig){
var lElement = DOM.anyload({
src : CHOOSER_API,
not_append : true,
id : 'dropboxjs',
func : DropBoxStore.choose
});
var lDropBoxId = pConfig.dropbox_chooser_key;
lElement.setAttribute('data-app-key', lDropBoxId);
document.body.appendChild(lElement);
console.timeEnd('dropbox load');
});
}
DropBoxStore.choose = function(){
Dropbox.choose(options);
};
DropBoxStore.init = function(){
load();
};
cloudcmd.DropBox = DropBoxStore;
})();

View file

@ -0,0 +1,170 @@
var CloudCommander, Util, DOM, $, Github, cb;
/* module for work with github */
(function(){
"use strict";
var cloudcmd = CloudCommander,
Cache = DOM.Cache,
APIURL = '/api/v1',
AuthURL = APIURL + '/auth',
GitHubIdURL = APIURL + '/github_key',
GitHub_ID,
GithubLocal,
User,
GithubStore = {};
/* temporary callback function for work with github */
cb = function (err, data){ console.log(err || data);};
/* PRIVATE FUNCTIONS */
/**
* function loads github.js
*/
function load(pCallBack){
console.time('github load');
var lDir = '/lib/client/storage/github/',
lFiles = [
lDir + 'github.js',
lDir + 'lib/base64.js',
lDir + 'lib/underscore.js'
];
DOM.anyLoadInParallel(lFiles, function(){
console.timeEnd('github load');
DOM.Images.hideLoad();
Util.exec(pCallBack);
});
}
function setConfig(pCallBack){
DOM.ajax({
url : GitHubIdURL,
success : function(pData){
GitHub_ID = pData;
Util.exec(pCallBack);
}
});
}
function init(pCallBack){
var lToken = Cache.get('token');
if(lToken){
GithubStore.Login(lToken);
Util.exec(pCallBack);
}
else{
var lCode = window.location.search;
if ( Util.isContainStr(lCode, '?code=') ){
lCode = lCode.replace('?code=','');
DOM.ajax({
type : 'put',
url : AuthURL,
data: lCode,
success: function(pData){
if(pData && pData.token){
lToken = pData.token;
GithubStore.Login(lToken);
Cache.set('token', lToken);
Util.exec(pCallBack);
}
else
Util.log("Worning: token not getted...");
}
});
}
else
//window.open('welcome.html', 'welcome','width=300,height=200,menubar=yes,status=yes')">
window.location =
'https://github.com/login/oauth/authorize?client_id=' +
GitHub_ID + '&&scope=repo,user,gist';
}
}
function getUserData(pCallBack){
var lName,
lShowUserInfo = function(pError, pData){
if(!pError){
lName = pData.name;
console.log('Hello ' + lName + ' :)!');
}
else
DOM.Cache.remove('token');
};
User.show(null, lShowUserInfo);
Util.exec(pCallBack);
}
/* PUBLICK FUNCTIONS */
GithubStore.basicLogin = function(pUser, pPasswd){
GithubLocal = new Github({
username: pUser,
password: pPasswd,
auth : 'basic'
});
};
GithubStore.Login = function(pToken){
Github = GithubLocal = new Github({
token : pToken,
auth : 'oauth'
});
User = GithubLocal.getUser();
};
/**
* function creates gist
*/
cloudcmd.GitHub.createGist = function(pContent, pFileName){
if(pContent){
if(!pFileName)
pFileName = Util.getDate();
var lGist = GithubLocal.getGist(),
lFiles = {},
lHost = CloudCommander.HOST,
lOptions = {
description: "Uplouded by Cloud Commander from " + lHost,
public: true
};
lFiles[pFileName] ={
content: pContent
};
lOptions.files = lFiles;
lGist.create(lOptions, cb);
}
return pContent;
};
cloudcmd.GitHub.init = function(pCallBack){
Util.loadOnLoad([
Util.retExec(pCallBack),
getUserData,
init,
setConfig,
load
]);
cloudcmd.GitHub.init = null;
};
cloudcmd.Github = GithubStore;
})();

View file

@ -0,0 +1,25 @@
Copyright (c) 2012 Michael Aufreiter, Development Seed
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
- Neither the name "Development Seed" nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1,272 @@
# Github.js
Github.js provides a minimal higher-level wrapper around git's [plumbing commands](http://git-scm.com/book/en/Git-Internals-Plumbing-and-Porcelain), exposing an API for manipulating GitHub repositories on the file level. It is being developed in the context of [Prose](http://prose.io), a content editor for GitHub.
## Usage
Create a Github instance.
```js
var github = new Github({
username: "YOU_USER",
password: "YOUR_PASSWORD",
auth: "basic"
});
```
Or if you prefer OAuth, it looks like this:
```js
var github = new Github({
token: "OAUTH_TOKEN"
auth: "oauth"
});
```
## Repository API
```js
var repo = github.getRepo(username, reponame);
```
Show repository information
```js
repo.show(function(err, repo) {});
```
Get contents at a particular path.
```js
repo.contents("path/to/dir", function(err, contents) {});
```
Fork repository. This operation runs asynchronously. You may want to poll for `repo.contents` until the forked repo is ready.
```js
repo.fork(function(err) {});
```
Create Pull Request.
```js
var pull = {
title: message,
body: "This pull request has been automatically generated by Prose.io.",
base: "gh-pages",
head: "michael" + ":" + "prose-patch",
};
repo.createPullRequest(pull, function(err, pullRequest) {});
```
Retrieve all available branches (aka heads) of a repository.
```js
repo.listBranches(function(err, branches) {});
```
Store contents at a certain path, where files that don't yet exist are created on the fly.
```js
repo.write('master', 'path/to/file', 'YOUR_NEW_CONTENTS', 'YOUR_COMMIT_MESSAGE', function(err) {});
```
Not only can you can write files, you can of course read them.
```js
repo.read('master', 'path/to/file', function(err, data) {});
```
Move a file from A to B.
```js
repo.move('master', 'path/to/file', 'path/to/new_file', function(err) {});
```
Remove a file.
```js
repo.remove('master', 'path/to/file', function(err) {});
```
Exploring files of a repository is easy too by accessing the top level tree object.
```js
repo.getTree('master', function(err, tree) {});
```
If you want to access all blobs and trees recursively, you can add `?recursive=true`.
```js
repo.getTree('master?recursive=true', function(err, tree) {});
```
Given a filepath, retrieve the reference blob or tree sha.
```js
repo.getSha('master', '/path/to/file', function(err, sha) {});
```
For a given reference, get the corresponding commit sha.
```js
repo.getRef('heads/master', function(err, sha) {});
```
Create a new reference.
```js
var refSpec = {
"ref": "refs/heads/my-new-branch-name",
"sha": "827efc6d56897b048c772eb4087f854f46256132"
};
repo.createRef(refSpec, function(err) {});
```
Delete a reference.
```js
repo.deleteRef('heads/gh-pages', function(err) {});
```
## User API
```js
var user = github.getUser();
```
List all repositories of the authenticated user.
```js
user.repos(username, function(err, repos) {});
```
List organizations the autenticated user belongs to.
```js
user.orgs(function(err, orgs) {});
```
List authenticated user's gists.
```js
user.gists(username, function(err, gists) {});
```
Show user information for a particular username. Also works for organizations.
```js
user.show(username, function(err, user) {});
```
List public repositories for a particular user.
```js
user.userRepos(username, function(err, repos) {});
```
List repositories for a particular organization. Includes private repositories if you are authorized.
```js
user.orgRepos(orgname, function(err, repos) {});
```
List all gists of a particular user. If username is ommitted gists of the current authenticated user are returned.
```js
user.userGists(username, function(err, gists) {});
```
## Gist API
```js
var gist = github.getGist(3165654);
```
Read the contents of a Gist.
```js
gist.read(function(err, gist) {
});
```
Updating the contents of a Git. Please consult the documentation on [GitHub](http://developer.github.com/v3/gists/).
```js
var delta = {
"description": "the description for this gist",
"files": {
"file1.txt": {
"content": "updated file contents"
},
"old_name.txt": {
"filename": "new_name.txt",
"content": "modified contents"
},
"new_file.txt": {
"content": "a new file"
},
"delete_this_file.txt": null
}
};
gist.update(delta, function(err, gist) {
});
```
## Tests
Github.js is automatically™ tested by the users of [Prose](http://prose.io). Because of that, we decided to save some time by not maintaining a test suite. Yes, you heard right. :) However, you can still consider it stable since it is used in production.
##Setup
Github.js has the following dependencies:
- Underscore
- Base64 (for basic auth). You can leave this if you are not using basic auth.
Include these before github.js :
```
<script src="lib/underscore-min.js">
<script src="lib/base64.js">
<script src="github.js">
```
## Change Log
### 0.7.X
Switched to a native `request` implementation (thanks @mattpass). Adds support for GitHub gists, forks and pull requests.
### 0.6.X
Adds support for organizations and fixes an encoding issue.
### 0.5.X
Smart caching of latest commit sha.
### 0.4.X
Added support for [OAuth](http://developer.github.com/v3/oauth/).
### 0.3.X
Support for Moving and removing files.
### 0.2.X
Consider commit messages.
### 0.1.X
Initial version.

View file

@ -0,0 +1,499 @@
// Github.js 0.7.0
// (c) 2012 Michael Aufreiter, Development Seed
// Github.js is freely distributable under the MIT license.
// For all details and documentation:
// http://substance.io/michael/github
(function() {
var Github;
var API_URL = 'https://api.github.com';
Github = window.Github = function(options) {
// HTTP Request Abstraction
// =======
//
// I'm not proud of this and neither should you be if you were responsible for the XMLHttpRequest spec.
function _request(method, path, data, cb, raw) {
function getURL() {
var url = API_URL + path;
return url + ((/\?/).test(url) ? "&" : "?") + (new Date()).getTime();
}
var xhr = new XMLHttpRequest();
if (!raw) {xhr.dataType = "json";}
xhr.open(method, getURL());
xhr.onreadystatechange = function () {
if (this.readyState == 4) {
if (this.status >= 200 && this.status < 300 || this.status === 304) {
cb(null, raw ? this.responseText : this.responseText ? JSON.parse(this.responseText) : true);
} else {
cb({request: this, error: this.status});
}
}
};
xhr.setRequestHeader('Accept','application/vnd.github.raw');
xhr.setRequestHeader('Content-Type','application/json');
if (
(options.auth == 'oauth' && options.token) ||
(options.auth == 'basic' && options.username && options.password)
) {
xhr.setRequestHeader('Authorization',options.auth == 'oauth'
? 'token '+ options.token
: 'Basic ' + Base64.encode(options.username + ':' + options.password)
);
}
data ? xhr.send(JSON.stringify(data)) : xhr.send();
}
// User API
// =======
Github.User = function() {
this.repos = function(cb) {
_request("GET", "/user/repos?type=all&per_page=1000&sort=updated", null, function(err, res) {
cb(err, res);
});
};
// List user organizations
// -------
this.orgs = function(cb) {
_request("GET", "/user/orgs", null, function(err, res) {
cb(err, res);
});
};
// List authenticated user's gists
// -------
this.gists = function(cb) {
_request("GET", "/gists", null, function(err, res) {
cb(err,res);
});
};
// Show user information
// -------
this.show = function(username, cb) {
var command = username ? "/users/"+username : "/user";
_request("GET", command, null, function(err, res) {
cb(err, res);
});
};
// List user repositories
// -------
this.userRepos = function(username, cb) {
_request("GET", "/users/"+username+"/repos?type=all&per_page=1000&sort=updated", null, function(err, res) {
cb(err, res);
});
};
// List a user's gists
// -------
this.userGists = function(username, cb) {
_request("GET", "/users/"+username+"/gists", null, function(err, res) {
cb(err,res);
});
};
// List organization repositories
// -------
this.orgRepos = function(orgname, cb) {
_request("GET", "/orgs/"+orgname+"/repos?type=all&per_page=1000&sort=updated&direction=desc", null, function(err, res) {
cb(err, res);
});
};
// Follow user
// -------
this.follow = function(username, cb) {
_request("PUT", "/user/following/"+username, null, function(err, res) {
cb(err, res);
});
};
// Unfollow user
// -------
this.unfollow = function(username, cb) {
_request("DELETE", "/user/following/"+username, null, function(err, res) {
cb(err, res);
});
};
};
// Repository API
// =======
Github.Repository = function(options) {
var repo = options.name;
var user = options.user;
var that = this;
var repoPath = "/repos/" + user + "/" + repo;
var currentTree = {
"branch": null,
"sha": null
};
// Uses the cache if branch has not been changed
// -------
function updateTree(branch, cb) {
if (branch === currentTree.branch && currentTree.sha) return cb(null, currentTree.sha);
that.getRef("heads/"+branch, function(err, sha) {
currentTree.branch = branch;
currentTree.sha = sha;
cb(err, sha);
});
}
// Get a particular reference
// -------
this.getRef = function(ref, cb) {
_request("GET", repoPath + "/git/refs/" + ref, null, function(err, res) {
if (err) return cb(err);
cb(null, res.object.sha);
});
};
// Create a new reference
// --------
//
// {
// "ref": "refs/heads/my-new-branch-name",
// "sha": "827efc6d56897b048c772eb4087f854f46256132"
// }
this.createRef = function(options, cb) {
_request("POST", repoPath + "/git/refs", options, cb);
};
// Delete a reference
// --------
//
// repo.deleteRef('heads/gh-pages')
// repo.deleteRef('tags/v1.0')
this.deleteRef = function(ref, cb) {
_request("DELETE", repoPath + "/git/refs/"+ref, options, cb);
};
// List all branches of a repository
// -------
this.listBranches = function(cb) {
_request("GET", repoPath + "/git/refs/heads", null, function(err, heads) {
if (err) return cb(err);
cb(null, _.map(heads, function(head) { return _.last(head.ref.split('/')); }));
});
};
// Retrieve the contents of a blob
// -------
this.getBlob = function(sha, cb) {
_request("GET", repoPath + "/git/blobs/" + sha, null, cb, 'raw');
};
// For a given file path, get the corresponding sha (blob for files, tree for dirs)
// -------
this.getSha = function(branch, path, cb) {
// Just use head if path is empty
if (path === "") return that.getRef("heads/"+branch, cb);
that.getTree(branch+"?recursive=true", function(err, tree) {
var file = _.select(tree, function(file) {
return file.path === path;
})[0];
cb(null, file ? file.sha : null);
});
};
// Retrieve the tree a commit points to
// -------
this.getTree = function(tree, cb) {
_request("GET", repoPath + "/git/trees/"+tree, null, function(err, res) {
if (err) return cb(err);
cb(null, res.tree);
});
};
// Post a new blob object, getting a blob SHA back
// -------
this.postBlob = function(content, cb) {
if (typeof(content) === "string") {
content = {
"content": content,
"encoding": "utf-8"
};
}
_request("POST", repoPath + "/git/blobs", content, function(err, res) {
if (err) return cb(err);
cb(null, res.sha);
});
};
// Update an existing tree adding a new blob object getting a tree SHA back
// -------
this.updateTree = function(baseTree, path, blob, cb) {
var data = {
"base_tree": baseTree,
"tree": [
{
"path": path,
"mode": "100644",
"type": "blob",
"sha": blob
}
]
};
_request("POST", repoPath + "/git/trees", data, function(err, res) {
if (err) return cb(err);
cb(null, res.sha);
});
};
// Post a new tree object having a file path pointer replaced
// with a new blob SHA getting a tree SHA back
// -------
this.postTree = function(tree, cb) {
_request("POST", repoPath + "/git/trees", { "tree": tree }, function(err, res) {
if (err) return cb(err);
cb(null, res.sha);
});
};
// Create a new commit object with the current commit SHA as the parent
// and the new tree SHA, getting a commit SHA back
// -------
this.commit = function(parent, tree, message, cb) {
var data = {
"message": message,
"author": {
"name": options.username
},
"parents": [
parent
],
"tree": tree
};
_request("POST", repoPath + "/git/commits", data, function(err, res) {
currentTree.sha = res.sha; // update latest commit
if (err) return cb(err);
cb(null, res.sha);
});
};
// Update the reference of your head to point to the new commit SHA
// -------
this.updateHead = function(head, commit, cb) {
_request("PATCH", repoPath + "/git/refs/heads/" + head, { "sha": commit }, function(err, res) {
cb(err);
});
};
// Show repository information
// -------
this.show = function(cb) {
_request("GET", repoPath, null, cb);
};
// Get contents
// --------
this.contents = function(path, cb) {
_request("GET", repoPath + "/contents", { path: path }, cb);
};
// Fork repository
// -------
this.fork = function(cb) {
_request("POST", repoPath + "/forks", null, cb);
};
// Create pull request
// --------
this.createPullRequest = function(options, cb) {
_request("POST", repoPath + "/pulls", options, cb);
};
// Read file at given path
// -------
this.read = function(branch, path, cb) {
that.getSha(branch, path, function(err, sha) {
if (!sha) return cb("not found", null);
that.getBlob(sha, function(err, content) {
cb(err, content, sha);
});
});
};
// Remove a file from the tree
// -------
this.remove = function(branch, path, cb) {
updateTree(branch, function(err, latestCommit) {
that.getTree(latestCommit+"?recursive=true", function(err, tree) {
// Update Tree
var newTree = _.reject(tree, function(ref) { return ref.path === path; });
_.each(newTree, function(ref) {
if (ref.type === "tree") delete ref.sha;
});
that.postTree(newTree, function(err, rootTree) {
that.commit(latestCommit, rootTree, 'Deleted '+path , function(err, commit) {
that.updateHead(branch, commit, function(err) {
cb(err);
});
});
});
});
});
};
// Move a file to a new location
// -------
this.move = function(branch, path, newPath, cb) {
updateTree(branch, function(err, latestCommit) {
that.getTree(latestCommit+"?recursive=true", function(err, tree) {
// Update Tree
_.each(tree, function(ref) {
if (ref.path === path) ref.path = newPath;
if (ref.type === "tree") delete ref.sha;
});
that.postTree(tree, function(err, rootTree) {
that.commit(latestCommit, rootTree, 'Deleted '+path , function(err, commit) {
that.updateHead(branch, commit, function(err) {
cb(err);
});
});
});
});
});
};
// Write file contents to a given branch and path
// -------
this.write = function(branch, path, content, message, cb) {
updateTree(branch, function(err, latestCommit) {
if (err) return cb(err);
that.postBlob(content, function(err, blob) {
if (err) return cb(err);
that.updateTree(latestCommit, path, blob, function(err, tree) {
if (err) return cb(err);
that.commit(latestCommit, tree, message, function(err, commit) {
if (err) return cb(err);
that.updateHead(branch, commit, cb);
});
});
});
});
};
};
// Gists API
// =======
Github.Gist = function(options) {
var id = options.id;
var gistPath = "/gists/"+id;
// Read the gist
// --------
this.read = function(cb) {
_request("GET", gistPath, null, function(err, gist) {
cb(err, gist);
});
};
// Create the gist
// --------
// {
// "description": "the description for this gist",
// "public": true,
// "files": {
// "file1.txt": {
// "content": "String file contents"
// }
// }
// }
this.create = function(options, cb){
_request("POST", "/gists", options, cb);
};
// Delete the gist
// --------
this.delete = function(cb) {
_request("DELETE", gistPath, null, function(err,res) {
cb(err,res);
});
};
// Fork a gist
// --------
this.fork = function(cb) {
_request("POST", gistPath+"/fork", null, function(err,res) {
cb(err,res);
});
};
// Update a gist with the new stuff
// --------
this.update = function(options, cb) {
_request("PATCH", gistPath, options, function(err,res) {
cb(err,res);
});
};
};
// Top Level API
// -------
this.getRepo = function(user, repo) {
return new Github.Repository({user: user, name: repo});
};
this.getUser = function() {
return new Github.User();
};
this.getGist = function(id) {
return new Github.Gist({id: id});
};
};
}).call(this);

View file

@ -0,0 +1,82 @@
// This code was written by Tyler Akins and has been placed in the
// public domain. It would be nice if you left this header intact.
// Base64 code from Tyler Akins -- http://rumkin.com
var Base64 = (function () {
var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var obj = {
/**
* Encodes a string in base64
* @param {String} input The string to encode in base64.
*/
encode: function (input) {
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
do {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) +
keyStr.charAt(enc3) + keyStr.charAt(enc4);
} while (i < input.length);
return output;
},
/**
* Decodes a base64 string.
* @param {String} input The string to decode.
*/
decode: function (input) {
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
// remove all characters that are not A-Z, a-z, 0-9, +, /, or =
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
do {
enc1 = keyStr.indexOf(input.charAt(i++));
enc2 = keyStr.indexOf(input.charAt(i++));
enc3 = keyStr.indexOf(input.charAt(i++));
enc4 = keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
} while (i < input.length);
return output;
}
};
return obj;
})();
window.Base64 = Base64

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
var CloudCommander, $;
var CloudCommander, Util, DOM, $;
/* object contains terminal jqconsole */
(function(){
@ -6,96 +6,110 @@ var CloudCommander, $;
var cloudcmd = CloudCommander,
Util = cloudcmd.Util,
KeyBinding = cloudcmd.KeyBinding,
TerminalId,
Term,
Hidden = false;
cloudcmd.Terminal = {};
var JqueryTerminal = {};
Hidden = false,
JqueryTerminal = {};
cloudcmd.Terminal = {};
/* PRIVATE FUNCTIONS */
/**
* function loads jquery-terminal
*/
function load(pCallBack){
console.time('terminal load');
var lDir = 'lib/client/terminal/jquery-terminal/jquery.',
lFiles = [
lDir + 'terminal.js',
lDir + 'mousewheel.js',
lDir + 'terminal.css'
];
DOM.anyLoadInParallel(lFiles, function(){
console.timeEnd('terminal load');
init();
JqueryTerminal.load = (function(){
Util.cssLoad({
src : 'lib/client/terminal/jquery-terminal/jquery.terminal.css'
$(function($, undefined) {
Term = JqueryTerminal.Term = $('#terminal').terminal(function(command, term){
term.echo('');
cloudcmd.Socket.send(command);
}, {
greetings : '[[;#729FCF;]Cloud Commander Terminal]',
prompt : '[[;#729FCF;]cloudcmd> ]',
color : '#729FCF;'
});
});
/* removing resize function, no need for us */
Term.resize = function(){};
$(window).unbind('resize');
Util.exec(pCallBack.callback || pCallBack);
});
Util.jsload('lib/client/socket.js');
var lLoadTerm_func = function(){
Util.jsload('lib/client/terminal/jquery-terminal/jquery.terminal.js',
function(){
JqueryTerminal.init();
$(function($, undefined) {
Term = JqueryTerminal.Term = $('#terminal').terminal(function(command, term){
term.echo('');
cloudcmd.Socket.send(command);
}, {
greetings : 'Javascript Interpreter',
prompt : 'cloudcmd> '
});
});
JqueryTerminal.show();
});
};
Util.jsload('lib/client/terminal/jquery-terminal/jquery.mousewheel.js',
lLoadTerm_func);
});
}
/**
* function do basic initialization
*/
function init(){
if(!TerminalId)
TerminalId = DOM.anyload({
name : 'div',
id : 'terminal',
className : 'panel',
parent : DOM.getFM()
});
}
JqueryTerminal.init = (function(){
if(!TerminalId){
var lFM = Util.getById('fm');
if(lFM)
TerminalId = Util.anyload({
name : 'div',
id : 'terminal',
className : 'panel',
parent : lFM
});
/* PUBLIC FUNCTIONS */
/**
* functin show jquery-terminal
*/
JqueryTerminal.show = function(){
DOM.Images.hideLoad();
/* only if panel was hided */
if( DOM.hidePanel() ){
Hidden = false;
DOM.show(TerminalId);
KeyBinding.unSet();
Term.resume();
}
else
console.log('Error. Something went wrong FM not found');
});
};
JqueryTerminal.show = (function(){
Util.Images.hideLoad();
Hidden = false;
Util.hidePanel();
Util.show(TerminalId);
KeyBinding.unSet();
Term.resume();
});
JqueryTerminal.hide = (function(){
/**
* function hide jquery-terminal
*/
JqueryTerminal.hide = function(){
Hidden = true;
Util.hide(TerminalId);
Util.showPanel();
DOM.hide(TerminalId);
DOM.showPanel();
KeyBinding.set();
Term.pause();
});
cloudcmd.Terminal.Keys = (function(){
};
/**
* function bind keys
*/
cloudcmd.Terminal.init = function(){
/* loading js and css*/
Util.jqueryLoad( JqueryTerminal.load );
var key_event = function(event){
DOM.jqueryLoad( Util.retLoadOnLoad([
JqueryTerminal.show,
load,
DOM.socketLoad
]) );
/* добавляем обработчик клавишь */
DOM.addKeyListener(function(){
/* если клавиши можно обрабатывать */
if(Hidden &&
KeyBinding.get() &&
if(Hidden && KeyBinding.get() &&
event.keyCode === cloudcmd.KEY.TRA){
JqueryTerminal.show();
event.preventDefault();
@ -103,18 +117,8 @@ var CloudCommander, $;
else if(!Hidden && event.keyCode === cloudcmd.KEY.ESC)
JqueryTerminal.hide();
};
/* добавляем обработчик клавишь */
if (document.addEventListener)
document.addEventListener('keydown', key_event, false);
else
document.onkeypress = key_event;
});
});
};
cloudcmd.Terminal.JqueryTerminal = JqueryTerminal;
})();
cloudcmd.Terminal.JqueryTerminal = JqueryTerminal;
})();

View file

@ -26,7 +26,10 @@
font-family: FreeMono, monospace;
color: #aaa;
background-color: #000;
line-height: 14px;
line-height: 16px;
/* removing breaking the lines */
white-space: nowrap;
overflow:hidden;
}
.terminal .cmd span {
float: left;

View file

@ -1656,9 +1656,7 @@ function get_stack(caller) {
div = $('<div/>').html(encodeHTML(string));
}
output.append(div);
/*
div.width('100%');
*/
div.width('100%');
scroll_to_bottom();
return div;
}
@ -1853,7 +1851,7 @@ function get_stack(caller) {
}).get().join('\n');
}
},
resize: function(width, height) {
resize: function(width, height) {
if (width && height) {
self.width(width);
self.height(height);
@ -2363,6 +2361,7 @@ function get_stack(caller) {
} else {
self.disable();
}
$(window).resize(self.resize);
self.click(function() {
self.focus();

View file

@ -1,46 +1,78 @@
var CloudCommander, CloudFunc, $;
var CloudCommander, Util, DOM, CloudFunc, $;
/* object contains viewer FancyBox
* https://github.com/fancyapps/fancyBox
*/
(function(){
var cloudcmd = CloudCommander;
var Util = CloudCommander.Util;
var KeyBinding = CloudCommander.KeyBinding;
"use strict";
cloudcmd.Viewer = {
var cloudcmd = CloudCommander,
KeyBinding = CloudCommander.KeyBinding,
FancyBox = {};
cloudcmd.Viewer = {
get: (function(){
return this.FancyBox;
})
};
var FancyBox = {};
cloudcmd.Viewer.dir = './lib/client/viewer/';
FancyBox.dir = cloudcmd.Viewer.dir + 'fancybox/';
/* function return configureation
* for FancyBox open and
* onclick (it shoud be
* different objects)
/* PRIVATE FUNCTIONS */
function set(){
if(DOM.getByClass('fancybox').length)
return;
/* get all file links */
var lA = DOM.getByTag('a', DOM.getPanel(false) ),
lActiveA = DOM.getByTag('a', DOM.getPanel(true) ),
lDblClick_f = function(pA){
return function(){
var lConfig = getConfig();
lConfig.href = pA.href;
if(pA.rel)
$.fancybox(lConfig);
else
FancyBox.loadData(pA, FancyBox.onDataLoaded);
};
},
lSetOnclick = function(pA){
/* first two is not files nor folders*/
for (var i = 2, n = pA.length; i < n; i++) {
var lA = pA[i],
lName = lA.title || lA.textContent;
lA.className = 'fancybox';
if(Util.checkExtension(lName, ['png','jpg', 'gif','ico']))
lA.rel = 'gallery';
lA.ondblclick = lDblClick_f(lA);
}
};
lSetOnclick(lA);
lSetOnclick(lActiveA);
}
/**
* function return configureation for FancyBox open and
* onclick (it shoud be different objects)
*/
FancyBox.getConfig = (function(){
function getConfig(){
return{
/* function do her work
* before FauncyBox shows
*/
beforeShow : function(){
KeyBinding.unSet();
},
beforeShow : Util.retFunc( KeyBinding.unSet ),
afterShow : function(){
var lEditor = Util.getById('CloudViewer');
var lEditor = DOM.getById('CloudViewer');
if(lEditor)
lEditor.focus();
},
beforeClose : function(){
KeyBinding.set();
},
beforeClose : Util.retFunc( KeyBinding.set ),
openEffect : 'none',
closeEffect : 'none',
@ -55,194 +87,134 @@ var CloudCommander, CloudFunc, $;
},
padding : 0
};
});
}
/* function loads css and js of FancyBox
/**
* function loads css and js of FancyBox
* @pParent - this
* @pCallBack - executes, when everything loaded
*/
FancyBox.load = (function(pThis, pCallBack){
var ljsLoad_f = function(){
var lSrc = pThis.dir + 'jquery.fancybox.js';
Util.jsload(lSrc,{
onload: pCallBack
});
};
FancyBox.load = function(pCallBack){
console.time('fancybox load');
var lDir = cloudcmd.LIBDIRCLIENT + 'viewer/fancybox/',
lFiles = [ lDir + 'jquery.fancybox.css',
lDir + 'jquery.fancybox.js' ];
Util.cssSet({id:'viewer',
inner : '#CloudViewer{' +
'font-size: 16px;' +
'white-space :pre' +
'}' +
'#CloudViewer::selection{' +
/*
'background: #fe57a1;'
'color: #fff;'
*/
'background: #b3d4fc;' +
'text-shadow: none;' +
'}'
});
var lSrc = pThis.dir +'jquery.fancybox.css';
Util.cssLoad({
src : lSrc,
func : {
onload: ljsLoad_f
}
DOM.anyLoadInParallel(lFiles, function(){
console.timeEnd('fancybox load');
pCallBack();
})
.cssSet({id:'viewer',
inner : '#CloudViewer{' +
'font-size: 16px;' +
'white-space :pre' +
'}' +
'#CloudViewer::selection{' +
/*
'background: #fe57a1;'
'color: #fff;'
*/
'background: #b3d4fc;' +
'text-shadow: none;' +
'}'
});
});
};
/* function loads data an put it to pSuccess_f
* @pA - link to data
* @pSucces_f - function, thet process data (@data)
/**
* function loads data an put it to pSuccess_f
* @param pA - link to data
* @param pSucces_f - function, thet process data (@data)
*
* Example: loadData('index.html', function(pData){console.log(pData)});
*/
FancyBox.loadData = (function(pA, pSuccess_f){
Util.Images.showLoad();
var lThis = this;
FancyBox.loadData = function(pA, pCallBack){
var lLink = pA.href;
/* убираем адрес хоста */
lLink = '/' + lLink.replace(document.location.href,'');
lLink = lLink.replace(cloudcmd.HOST, '');
if (lLink.indexOf(CloudFunc.NOJS) === CloudFunc.FS.length)
lLink = lLink.replace(CloudFunc.NOJS, '');
lLink = Util.removeStr(lLink, CloudFunc.NOJS);
Util.ajax({
DOM.ajax({
url : lLink,
error : (function(jqXHR, textStatus, errorThrown){
lThis.loading = false;
return Util.Images.showError(jqXHR, textStatus, errorThrown);
}),
success:function(data, textStatus, jqXHR){
if(typeof pSuccess_f === 'function')
pSuccess_f(data);
Util.Images.hideLoad();
error : function(jqXHR, textStatus, errorThrown){
FancyBox.loading = false;
return DOM.Images.showError(jqXHR, textStatus, errorThrown);
},
success :function(data, textStatus, jqXHR){
Util.exec(pCallBack, data);
}
});
});
};
FancyBox.onDataLoaded = (function(pData){
var lConfig = FancyBox.getConfig();
FancyBox.onDataLoaded = function(pData){
var lConfig = getConfig();
/* if we got json - show it */
if(typeof pData === 'object')
if( Util.isObject(pData) )
pData = JSON.stringify(pData, null, 4);
$.fancybox('<div id=CloudViewer tabindex=0>' + pData + '</div>', lConfig);
});
FancyBox.set = (function(){
if(Util.getByClass('fancybox').length)
return;
try{
/* get current panel (left or right) */
var lPanel = Util.getPanel();
/* get all file links */
var lA = Util.getByTag('a', lPanel);
var lThis = this;
var lDblClick_f = function(pA){
return function(){
var lConfig = lThis.getConfig();
lConfig.href = pA.href;
if(pA.rel)
$.fancybox(lConfig);
else
lThis.loadData(pA, lThis.onDataLoaded);
};
};
/* first two is not files nor folders*/
for (var i=2; i < lA.length; i++) {
var lName = lA[i].title || lA[i].textContent;
lA[i].className = 'fancybox';
if(CloudFunc.checkExtension(lName, ['png','jpg', 'gif','ico'])){
lA[i].rel = 'gallery';
}
lA[i].ondblclick = lDblClick_f(lA[i]);
}
}catch(pError){
console.log(pError);
}
});
FancyBox.show = (function(pCurrentFile){
FancyBox.set();
var lConfig = this.getConfig();
if(typeof pCurrentFile !== 'function'){
var lA = Util.getByClass('fancybox', pCurrentFile)[0];
DOM.Images.hideLoad();
};
/**
* function shows FancyBox
*/
FancyBox.show = function(pCallBack){
set();
var lConfig = getConfig(),
lResult = Util.exec(pCallBack);
if(!lResult){
var lCurrentFile = DOM.getCurrentFile(),
lA = DOM.getByClass('fancybox', lCurrentFile)[0];
if(lA){
if(lA.rel)
$.fancybox.open({ href : lA.href },
lConfig);
else this.loadData(lA, this.onDataLoaded);
else
FancyBox.loadData(lA, FancyBox.onDataLoaded);
}
}
else {
var lFunc_f = pCurrentFile;
lFunc_f();
}
Util.Images.hideLoad();
});
};
cloudcmd.Viewer.Keys = (function(pCurrentFile){
"use strict";
var lCallBack_f = (function(){
var key_event = (function(pEvent){
/* если клавиши можно обрабатывать */
if( KeyBinding.get() )
if(pEvent.keyCode === cloudcmd.KEY.F3 &&
pEvent.shiftKey){
var lCurrentFile = Util.getCurrentFile();
FancyBox.show(lCurrentFile);
pEvent.preventDefault();
}
});
/* добавляем обработчик клавишь */
if (document.addEventListener)
document.addEventListener('keydown', key_event, false);
else{
var lFunc;
if(typeof document.onkeydown === 'function')
lFunc = document.onkeydown;
document.onkeydown = function(){
if(lFunc)
lFunc();
key_event();
};
cloudcmd.Viewer.init = function(){
DOM.jqueryLoad( Util.retLoadOnLoad([
FancyBox.show,
FancyBox.load
]));
var lView = function(){
DOM.Images.showLoad();
FancyBox.show( DOM.getCurrentFile() );
};
var lKeyListener = function(){
var lF3 = cloudcmd.KEY.F3,
lKeyBinded = KeyBinding.get(),
lKey = event.keyCode,
lShift = event.shiftKey;
/* если клавиши можно обрабатывать */
if( lKeyBinded && lKey === lF3 && lShift ){
lView();
event.preventDefault();
}
/* showing images preview*/
FancyBox.show(pCurrentFile);
Util.Images.hideLoad();
});
Util.jqueryLoad(function(){
FancyBox.load(FancyBox, lCallBack_f);
});
});
};
/* добавляем обработчик клавишь */
DOM.addKeyListener(lKeyListener)
.setButtonKey('f3', lView);
};
cloudcmd.Viewer.FancyBox = FancyBox;
})();

View file

@ -0,0 +1,115 @@
fancyBox - Changelog
=========
### Version 2.1.3 - October 23, 2012
* Fixed #426 - Broken IE7
* Fixed #423 - Background flickering on iOS
* Fixed #418 - Automatically Grow/Shrink and Center
* Updated the script to work with jQuery 1.6
* Media helper supports YouTube video series
### Version 2.1.2 - October 15, 2012
* Fixed #414 - Don't allow nextClick if there is only one item
* Fixed #397 - Button helper 'Menu' not visible in IE7
* Overlay can be opened/closed manually:
* $.fancybox.helpers.overlay.open();
* $.fancybox.helpers.overlay.open({closeClick : false});
* $.fancybox.helpers.overlay.close();
* Optimized for Internet Explorer 10 (Windows 8)
### Version 2.1.1 - October 01, 2012
* Fixed #357 - Converting values like 'auto' in getScalar()
* Fixed #358 - Updated overlay background image
* New "fancybox-href" and "fancybox-title" HTML5 data-attributes (#317)
* Improved helpers:
* - now they can have a property 'defaults' that contains default settings
* - updated vimeo and youtube parsers for media helper
* Content locking now can be turned off
### Version 2.1.0 - August 20, 2012
* Fixed #103 - DOM element re-injection after closing
* Fixed #188 - navigation keys inside editable content
* New animation directions (see https://github.com/fancyapps/fancyBox/issues/233#issuecomment-5512453)
* New option "iframe" - it is now possible to separate scrolling for iframe and wrapping element; choose to preload
* New option "swf" - brings back functionality from fancyBox v1
* Improved media helper - better support for vimeo and youtube; links are now configurable
* Rewritten overlay helper:
* - new option "showEarly" - toggles if should be open before of after content is loaded
* - Facebook-style (https://github.com/fancyapps/fancyBox/issues/24) and therefore uses image for background
* Option "padding" accepts array (e.g., padding: [15, 50, 10, 5])
* One of dimensions (width or height) can now be set to "auto" (option "autoSize" needs to be "false")
* Updated callbacks:
* - "beforeClose" is now called only once
* - "afterLoad" receives current and previous object as arguments
* Method "$.fancybox.update();" recalculates content width/height
* Updated to work with jQuery v1.8
### Version 2.0.6 - April 16, 2012
* Fixed #188 - keystrokes in contenteditable
* Fixed #171 - non-images should not be preloaded
* Fixed #158 - 'closeClick: true' breaks gallery navigation
* New "media" helper - detects and displays various media types
* New option "groupAttr" - name of group selector attribute, default is "data-fancybox-group"
* New feature - selector expressions in URLs, see #170
* Improved 'overlay' helper to use "position: fixed"
* Improved autoSize, fixed wrong height in some cases
* Improved centering and iframe scrolling for iOS
* Updated markup, new element '.fancybox-skin' is now used for styling
### Version 2.0.5 - February 21, 2012
* Fixed #155 - easing for prev/next animations
* Fixed #153 - overriding "keys" options
* Fixed #147 - IE7 problem with #hash links
* Fixed #130 - changing dynamically data-fancybox-group
* Fixed #126 - obey minWidth/minHeight
* Fixed #118 - placement of loading icon and navigation arrows
* Fixed #101 - "index" option not working
* Fixed #94 - "orig" option not working
* Fixed #80 - does not work on IE6
* Fixed #72 - can't set overlay opacity to 0
* Fixed #63 - properly set gallery index
* New option "autoCenter" - toggles centering on window resize or scroll, disabled for mobile devices by default
* New option "autoResize" - toggles responsivity, disabled for mobile devices by default
* New option "preload" - number of images to preload
* New feature to target mobile/desktop browsers using CSS, see #108
* Changed ajax option defaults to "{ dataType: 'html', headers: { 'X-fancyBox': true } }", see #150 and #128
* Updated loading icon for IE7, IE8
* Calculates height of the iframe if 'autoSize' is set to 'true' and the iframe is on the same domain as the main page
### Version 2.0.4 - December 12, 2011
* Fixed #47 - fix overriding properties
* New option "position" to thumbnail and button helpers
### Version 2.0.3 - November 29, 2011
* Fixed #29 - broken elastic transitions
### Version 2.0.2 - November 28, 2011
* Fixed slideshow
* Fixed scrollbars issue when displayed a very tall image
* New option "nextClick" - navigate to next gallery item when user clicks the content
* New option "modal" - to disable navigation and closing
* Add 'metadata' plugin support
* Add ability to create groups using 'data-fancybox-group' attribute
* Updated manual usage to match earlier releases
### Version 2.0.1 - November 23, 2011
* Fixed keyboard events inside form elements
* Fixed manual usage
### Version 2.0.0 - November 21, 2011
First release - completely rewritten, many new features and updated graphics.

View file

@ -0,0 +1,217 @@
fancyBox
========
fancyBox is a tool that offers a nice and elegant way to add zooming functionality for images, html content and multi-media on your webpages.
More information and examples: http://www.fancyapps.com/fancybox/
License: http://www.fancyapps.com/fancybox/#license
Copyright (c) 2012 Janis Skarnelis - janis@fancyapps.com
How to use
----------
To get started, download the plugin, unzip it and copy files to your website/application directory.
Load files in the <head> section of your HTML document. Make sure you also add the jQuery library.
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.min.js"></script>
<link rel="stylesheet" href="/fancybox/jquery.fancybox.css" type="text/css" media="screen" />
<script type="text/javascript" src="/fancybox/jquery.fancybox.pack.js"></script>
</head>
Create your links with a `title` if you want a title to be shown, and add a class:
<a href="large_image.jpg" class="fancybox" title="Sample title"><img src="small_image.jpg" /></a>
If you have a set of related items that you would like to group,
additionally include a group name in the `rel` (or `data-fancybox-group`) attribute:
<a href="large_1.jpg" class="fancybox" rel="gallery" title="Sample title 1"><img src="small_1.jpg" /></a>
<a href="large_2.jpg" class="fancybox" rel="gallery" title="Sample title 1"><img src="small_2.jpg" /></a>
Initialise the script like this:
<script>
$(document).ready(function() {
$('.fancybox').fancybox();
});
</script>
May also be passed an optional options object which will extend the default values. Example:
<script>
$(document).ready(function() {
$('.fancybox').fancybox({
padding : 0,
openEffect : 'elastic'
});
});
</script>
Tip: Automatically group and apply fancyBox to all images:
$("a[href$='.jpg'],a[href$='.jpeg'],a[href$='.png'],a[href$='.gif']").attr('rel', 'gallery').fancybox();
Script uses the `href` attribute of the matched elements to obtain the location of the content and to figure out content type you want to display.
You can specify type directly by adding classname (fancybox.image, fancybox.iframe, etc) or `data-fancybox-type` attribute:
//Ajax:
<a href="/example.html" class="fancybox fancybox.ajax">Example</a>
//or
<a href="/example.html" class="fancybox" data-fancybox-type="ajax">Example</a>
//Iframe:
<a href="example.html" class="fancybox fancybox.iframe">Example</a>
//Inline (will display an element with `id="example"`)
<a href="#example" class="fancybox">Example</a>
//SWF:
<a href="example.swf" class="fancybox">Example</a>
//Image:
<a href="example.jpg" class="fancybox">Example</a>
Note, ajax requests are subject to the [same origin policy](http://en.wikipedia.org/wiki/Same_origin_policy).
If fancyBox will not be able to get content type, it will try to guess based on 'href' and will quit silently if would not succeed.
(this is different from previsous versions where 'ajax' was used as default type or an error message was displayed).
Advanced
--------
### Helpers
Helpers provide a simple mechanism to extend the capabilities of fancyBox. There are two built-in helpers - 'overlay' and 'title'.
You can disable them, set custom options or enable other helpers. Examples:
//Disable title helper
$(".fancybox").fancybox({
helpers: {
title: null
}
});
//Disable overlay helper
$(".fancybox").fancybox({
helpers: {
overlay : null
}
});
//Change title position and overlay color
$(".fancybox").fancybox({
helpers: {
title : {
type : 'inside'
},
overlay : {
css : {
'background' : 'rgba(255,255,255,0.5)'
}
}
}
});
//Enable thumbnail helper and set custom options
$(".fancybox").fancybox({
helpers: {
thumbs : {
width: 50,
height: 50
}
}
});
### API
Also available are event driven callback methods. The `this` keyword refers to the current or upcoming object (depends on callback method). Here is how you can change title:
$(".fancybox").fancybox({
beforeLoad : function() {
this.title = 'Image ' + (this.index + 1) + ' of ' + this.group.length + (this.title ? ' - ' + this.title : '');
/*
"this.element" refers to current element, so you can, for example, use the "alt" attribute of the image to store the title:
this.title = $(this.element).find('img').attr('alt');
*/
}
});
It`s possible to open fancyBox programmatically in various ways:
//HTML content:
$.fancybox( '<div><h1>Lorem Lipsum</h1><p>Lorem lipsum</p></div>', {
title : 'Custom Title'
});
//DOM element:
$.fancybox( $("#inline"), {
title : 'Custom Title'
});
//Custom object:
$.fancybox({
href: 'example.jpg',
title : 'Custom Title'
});
//Array of objects:
$.fancybox([
{
href: 'example1.jpg',
title : 'Custom Title 1'
},
{
href: 'example2.jpg',
title : 'Custom Title 2'
}
], {
padding: 0
});
There are several methods that allow you to interact with and manipulate fancyBox, example:
//Close fancybox:
$.fancybox.close();
There is a simply way to access wrapping elements using JS:
$.fancybox.wrap
$.fancybox.skin
$.fancybox.outer
$.fancybox.inner
You can override CSS to customize the look. For example, make navigation arrows always visible,
change width and move them outside of area (use this snippet after including fancybox.css):
.fancybox-nav span {
visibility: visible;
}
.fancybox-nav {
width: 80px;
}
.fancybox-prev {
left: -80px;
}
.fancybox-next {
right: -80px;
}
In that case, you might want to increase space around box:
$(".fancybox").fancybox({
margin : [20, 60, 20, 60]
});
Bug tracker
-----------
Have a bug? Please create an issue on GitHub at https://github.com/fancyapps/fancyBox/issues

View file

@ -1,6 +1,6 @@
/*!
* Buttons helper for fancyBox
* version: 1.0.3
* version: 1.0.5 (Mon, 15 Oct 2012)
* @requires fancyBox v2.0 or later
*
* Usage:
@ -12,10 +12,6 @@
* }
* });
*
* Options:
* tpl - HTML template
* position - 'top' or 'bottom'
*
*/
(function ($) {
//Shortcut for fancyBox object
@ -23,7 +19,12 @@
//Add helper object
F.helpers.buttons = {
tpl : '<div id="fancybox-buttons"><ul><li><a class="btnPrev" title="Previous" href="javascript:;"></a></li><li><a class="btnPlay" title="Start slideshow" href="javascript:;"></a></li><li><a class="btnNext" title="Next" href="javascript:;"></a></li><li><a class="btnToggle" title="Toggle size" href="javascript:;"></a></li><li><a class="btnClose" title="Close" href="javascript:jQuery.fancybox.close();"></a></li></ul></div>',
defaults : {
skipSingle : false, // disables if gallery contains single image
position : 'top', // 'top' or 'bottom'
tpl : '<div id="fancybox-buttons"><ul><li><a class="btnPrev" title="Previous" href="javascript:;"></a></li><li><a class="btnPlay" title="Start slideshow" href="javascript:;"></a></li><li><a class="btnNext" title="Next" href="javascript:;"></a></li><li><a class="btnToggle" title="Toggle size" href="javascript:;"></a></li><li><a class="btnClose" title="Close" href="javascript:jQuery.fancybox.close();"></a></li></ul></div>'
},
list : null,
buttons: null,
@ -57,7 +58,7 @@
var buttons = this.buttons;
if (!buttons) {
this.list = $(opts.tpl || this.tpl).addClass(opts.position || 'top').appendTo('body');
this.list = $(opts.tpl).addClass(opts.position).appendTo('body');
buttons = {
prev : this.list.find('.btnPrev').click( F.prev ),

View file

@ -1,6 +1,6 @@
/*!
* Media helper for fancyBox
* version: 1.0.3 (Mon, 13 Aug 2012)
* version: 1.0.5 (Tue, 23 Oct 2012)
* @requires fancyBox v2.0 or later
*
* Usage:
@ -37,6 +37,7 @@
*
* Youtube
* http://www.youtube.com/watch?v=opj24KnzrWo
* http://www.youtube.com/embed/opj24KnzrWo
* http://youtu.be/opj24KnzrWo
* Vimeo
* http://vimeo.com/40648169
@ -85,9 +86,9 @@
//Add helper object
F.helpers.media = {
types : {
defaults : {
youtube : {
matcher : /(youtube\.com|youtu\.be)\/(watch\?v=|v\/|u\/|embed)?([\w-]{11}|\?listType=(.*)&list=(.*)).*/i,
matcher : /(youtube\.com|youtu\.be)\/(watch\?v=|v\/|u\/|embed\/?)?(videoseries\?list=(.*)|[\w-]{11}|\?listType=(.*)&list=(.*)).*/i,
params : {
autoplay : 1,
autohide : 1,
@ -108,7 +109,6 @@
show_title : 1,
show_byline : 1,
show_portrait : 0,
color : '',
fullscreen : 1
},
type : 'iframe',
@ -170,8 +170,8 @@
rez,
params;
for (what in this.types) {
item = this.types[ what ];
for (what in opts) {
item = opts[ what ];
rez = url.match( item.matcher );
if (rez) {

View file

@ -1,6 +1,6 @@
/*!
* Thumbnail helper for fancyBox
* version: 1.0.6
* version: 1.0.7 (Mon, 01 Oct 2012)
* @requires fancyBox v2.0 or later
*
* Usage:
@ -13,12 +13,6 @@
* }
* });
*
* Options:
* width - thumbnail width
* height - thumbnail height
* source - function to obtain the URL of the thumbnail image
* position - 'top' or 'bottom'
*
*/
(function ($) {
//Shortcut for fancyBox object
@ -26,31 +20,35 @@
//Add helper object
F.helpers.thumbs = {
defaults : {
width : 50, // thumbnail width
height : 50, // thumbnail height
position : 'bottom', // 'top' or 'bottom'
source : function ( item ) { // function to obtain the URL of the thumbnail image
var href;
if (item.element) {
href = $(item.element).find('img').attr('src');
}
if (!href && item.type === 'image' && item.href) {
href = item.href;
}
return href;
}
},
wrap : null,
list : null,
width : 0,
//Default function to obtain the URL of the thumbnail image
source: function ( item ) {
var href;
if (item.element) {
href = $(item.element).find('img').attr('src');
}
if (!href && item.type === 'image' && item.href) {
href = item.href;
}
return href;
},
init: function (opts, obj) {
var that = this,
list,
thumbWidth = opts.width || 50,
thumbHeight = opts.height || 50,
thumbSource = opts.source || this.source;
thumbWidth = opts.width,
thumbHeight = opts.height,
thumbSource = opts.source;
//Build list structure
list = '';
@ -59,7 +57,7 @@
list += '<li><a style="width:' + thumbWidth + 'px;height:' + thumbHeight + 'px;" href="javascript:jQuery.fancybox.jumpto(' + n + ');"></a></li>';
}
this.wrap = $('<div id="fancybox-thumbs"></div>').addClass(opts.position || 'bottom').appendTo('body');
this.wrap = $('<div id="fancybox-thumbs"></div>').addClass(opts.position).appendTo('body');
this.list = $('<ul>' + list + '</ul>').appendTo(this.wrap);
//Load each thumbnail
@ -125,7 +123,7 @@
}
//Increase bottom margin to give space for thumbs
obj.margin[ opts.position === 'top' ? 0 : 2 ] += ((opts.height || 50) + 15);
obj.margin[ opts.position === 'top' ? 0 : 2 ] += ((opts.height) + 15);
},
afterShow: function (opts, obj) {

View file

@ -1,4 +1,4 @@
/*! fancyBox v2.1.0 fancyapps.com | fancyapps.com/fancybox/#license */
/*! fancyBox v2.1.3 fancyapps.com | fancyapps.com/fancybox/#license */
.fancybox-wrap,
.fancybox-skin,
.fancybox-outer,
@ -154,9 +154,12 @@
.fancybox-tmp {
position: absolute;
top: -9999px;
left: -9999px;
top: -99999px;
left: -99999px;
visibility: hidden;
max-width: 99999px;
max-height: 99999px;
overflow: visible !important;
}
/* Overlay helper */

View file

@ -1,6 +1,6 @@
/*!
* fancyBox - jQuery Plugin
* version: 2.1.0 (Mon, 20 Aug 2012)
* version: 2.1.3 (Tue, 23 Oct 2012)
* @requires jQuery v1.6 or later
*
* Examples at http://fancyapps.com/fancybox/
@ -33,14 +33,14 @@
isScrollable = function(el) {
return (el && !(el.style.overflow && el.style.overflow === 'hidden') && ((el.clientWidth && el.scrollWidth > el.clientWidth) || (el.clientHeight && el.scrollHeight > el.clientHeight)));
},
getScalar = function(value, dim) {
var value_ = parseInt(value, 10);
getScalar = function(orig, dim) {
var value = parseInt(orig, 10) || 0;
if (dim && isPercentage(value)) {
value_ = F.getViewport()[ dim ] / 100 * value_;
if (dim && isPercentage(orig)) {
value = F.getViewport()[ dim ] / 100 * value;
}
return Math.ceil(value_);
return Math.ceil(value);
},
getValue = function(value, dim) {
return getScalar(value, dim) + 'px';
@ -48,7 +48,7 @@
$.extend(F, {
// The current version of fancyBox
version: '2.1.0',
version: '2.1.3',
defaults: {
padding : 15,
@ -65,7 +65,7 @@
autoHeight : false,
autoWidth : false,
autoResize : !isTouch,
autoResize : true,
autoCenter : !isTouch,
fitToView : true,
aspectRatio : false,
@ -136,7 +136,7 @@
tpl: {
wrap : '<div class="fancybox-wrap" tabIndex="-1"><div class="fancybox-skin"><div class="fancybox-outer"><div class="fancybox-inner"></div></div></div></div>',
image : '<img class="fancybox-image" src="{href}" alt="" />',
iframe : '<iframe id="fancybox-frame{rnd}" name="fancybox-frame{rnd}" class="fancybox-iframe" frameborder="0" vspace="0" hspace="0"' + ($.browser.msie ? ' allowtransparency="true"' : '') + '></iframe>',
iframe : '<iframe id="fancybox-frame{rnd}" name="fancybox-frame{rnd}" class="fancybox-iframe" frameborder="0" vspace="0" hspace="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen' + ($.browser.msie ? ' allowtransparency="true"' : '') + '></iframe>',
error : '<p class="fancybox-error">The requested content cannot be loaded.<br/>Please try again later.</p>',
closeBtn : '<a title="Close" class="fancybox-item fancybox-close" href="javascript:;"></a>',
next : '<a title="Next" class="fancybox-nav fancybox-next" href="javascript:;"><span></span></a>',
@ -170,17 +170,10 @@
prevEasing : 'swing',
prevMethod : 'changeOut',
// Enabled helpers
// Enable default helpers
helpers : {
overlay : {
closeClick : true,
speedOut : 200,
showEarly : true,
css : {}
},
title : {
type : 'float' // 'float', 'inside', 'outside' or 'over'
}
overlay : true,
title : true
},
// Callbacks
@ -264,8 +257,8 @@
if (isQuery(element)) {
obj = {
href : element.attr('href'),
title : element.attr('title'),
href : element.data('fancybox-href') || element.attr('href'),
title : element.data('fancybox-title') || element.attr('title'),
isDom : true,
element : element
};
@ -383,20 +376,20 @@
F.imgPreload.onload = F.imgPreload.onerror = null;
}
// If the first item has been canceled, then clear everything
if (coming.wrap) {
coming.wrap.stop(true).trigger('onReset').remove();
}
if (!F.current) {
F.trigger('afterClose');
coming.wrap.stop(true, true).trigger('onReset').remove();
}
F.coming = null;
// If the first item has been canceled, then clear everything
if (!F.current) {
F._afterZoomOut( coming );
}
},
// Start closing animation if is open; remove immediately if opening/closing
close: function (immediately) {
close: function (event) {
F.cancel();
if (false === F.trigger('beforeClose')) {
@ -405,7 +398,11 @@
F.unbindEvents();
if (!F.isOpen || immediately === true) {
if (!F.isActive) {
return;
}
if (!F.isOpen || event === true) {
$('.fancybox-wrap').stop(true).trigger('onReset').remove();
F._afterZoomOut();
@ -418,10 +415,6 @@
F.wrap.stop(true, true).removeClass('fancybox-opened');
if (F.wrap.css('position') === 'fixed') {
F.wrap.css(F._getPosition( true ));
}
F.transitions[ F.current.closeMethod ]();
}
},
@ -529,18 +522,22 @@
// Center inside viewport and toggle position type to fixed or absolute if needed
reposition: function (e, onlyAbsolute) {
var pos;
var current = F.current,
wrap = current ? current.wrap : null,
pos;
if (F.isOpen) {
if (wrap) {
pos = F._getPosition(onlyAbsolute);
if (e && e.type === 'scroll') {
delete pos.position;
F.wrap.stop(true, true).animate(pos, 200);
wrap.stop(true, true).animate(pos, 200);
} else {
F.wrap.css(pos);
wrap.css(pos);
current.pos = $.extend({}, current.dim, pos);
}
}
},
@ -559,23 +556,16 @@
return;
}
// Help browser to restore document dimensions
if (anyway || isTouch) {
F.wrap.removeAttr('style').addClass('fancybox-tmp');
F.trigger('onUpdate');
}
didUpdate = setTimeout(function() {
var current = F.current;
if (!current) {
if (!current || F.isClosing) {
return;
}
F.wrap.removeClass('fancybox-tmp');
if (type !== 'scroll') {
if (anyway || type === 'load' || (type === 'resize' && current.autoResize)) {
F._setDimension();
}
@ -587,7 +577,7 @@
didUpdate = null;
}, (isTouch ? 500 : (anyway ? 20 : 300)));
}, (anyway && !isTouch ? 0 : 300));
},
// Shrink content to fit inside viewport or restore if resized
@ -595,12 +585,19 @@
if (F.isOpen) {
F.current.fitToView = $.type(action) === "boolean" ? action : !F.current.fitToView;
// Help browser to restore document dimensions
if (isTouch) {
F.wrap.removeAttr('style').addClass('fancybox-tmp');
F.trigger('onUpdate');
}
F.update();
}
},
hideLoading: function () {
D.unbind('keypress.fb');
D.unbind('.loading');
$('#fancybox-loading').remove();
},
@ -610,16 +607,17 @@
F.hideLoading();
el = $('<div id="fancybox-loading"><div></div></div>').click(F.cancel).appendTo('body');
// If user will press the escape-button, the request will be canceled
D.bind('keypress.fb', function(e) {
D.bind('keydown.loading', function(e) {
if ((e.which || e.keyCode) === 27) {
e.preventDefault();
F.cancel();
}
});
el = $('<div id="fancybox-loading"><div></div></div>').click(F.cancel).appendTo('body');
if (!F.defaults.fixed) {
viewport = F.getViewport();
@ -632,15 +630,15 @@
},
getViewport: function () {
var lock = F.current ? F.current.locked : false,
rez = {
var locked = (F.current && F.current.locked) || false,
rez = {
x: W.scrollLeft(),
y: W.scrollTop()
};
if (lock) {
rez.w = lock[0].clientWidth;
rez.h = lock[0].clientHeight;
if (locked) {
rez.w = locked[0].clientWidth;
rez.h = locked[0].clientHeight;
} else {
// See http://bugs.jquery.com/ticket/6724
@ -671,7 +669,7 @@
// Changing document height on iOS devices triggers a 'resize' event,
// that can change document height... repeating infinitely
W.bind('orientationchange.fb' + (isTouch ? '' : ' resize.fb' ) + (current.autoCenter && !current.locked ? ' scroll.fb' : ''), F.update);
W.bind('orientationchange.fb' + (isTouch ? '' : ' resize.fb') + (current.autoCenter && !current.locked ? ' scroll.fb' : ''), F.update);
keys = current.keys;
@ -680,6 +678,11 @@
var code = e.which || e.keyCode,
target = e.target || e.srcElement;
// Skip esc key if loading, because showLoading will cancel preloading
if (code === 27 && F.coming) {
return false;
}
// Ignore key combinations and key events within form elements
if (!e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey && !(target && (target.type || $(target).is('[contenteditable]')))) {
$.each(keys, function(i, val) {
@ -747,13 +750,11 @@
return false;
}
if (event === 'onCancel' && !F.isOpened) {
F.isActive = false;
}
if (obj.helpers) {
$.each(obj.helpers, function (helper, opts) {
if (opts && F.helpers[helper] && $.isFunction(F.helpers[helper][event])) {
opts = $.extend(true, {}, F.helpers[helper].defaults, opts);
F.helpers[helper][event](opts, obj);
}
});
@ -763,7 +764,7 @@
},
isImage: function (str) {
return isString(str) && str.match(/\.(jp(e|g|eg)|gif|png|bmp|webp)((\?|#).*)?$/i);
return isString(str) && str.match(/(^data:image\/.*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp)((\?|#).*)?$)/i);
},
isSWF: function (str) {
@ -880,7 +881,7 @@
}
// Build the neccessary markup
coming.wrap = $(coming.tpl.wrap).addClass('fancybox-' + (isTouch ? 'mobile' : 'desktop') + ' fancybox-type-' + type + ' fancybox-tmp ' + coming.wrapCSS).appendTo( coming.parent );
coming.wrap = $(coming.tpl.wrap).addClass('fancybox-' + (isTouch ? 'mobile' : 'desktop') + ' fancybox-type-' + type + ' fancybox-tmp ' + coming.wrapCSS).appendTo( coming.parent || 'body' );
$.extend(coming, {
skin : $('.fancybox-skin', coming.wrap),
@ -954,7 +955,7 @@
img.src = F.coming.href;
if (img.complete === undefined || !img.complete) {
if (img.complete !== true) {
F.showLoading();
}
},
@ -1072,10 +1073,6 @@
previous.wrap.stop(true).removeClass('fancybox-opened')
.find('.fancybox-item, .fancybox-nav')
.remove();
if (previous.wrap.css('position') === 'fixed') {
previous.wrap.css(F._getPosition( true ));
}
}
F.unbindEvents();
@ -1123,7 +1120,7 @@
break;
case 'swf':
content = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="100%" height="100%"><param name="movie" value="' + href + '"></param>';
content = '<object id="fancybox-swf" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="100%" height="100%"><param name="movie" value="' + href + '"></param>';
embed = '';
$.each(current.swf, function(name, val) {
@ -1148,9 +1145,7 @@
// Set initial dimensions and start position
F._setDimension();
current.wrap.removeClass('fancybox-tmp');
current.pos = $.extend({}, current.dim, F._getPosition( true ));
F.reposition();
F.isOpen = false;
F.coming = null;
@ -1187,8 +1182,8 @@
scrolling = current.scrolling,
scrollOut = current.scrollOutside ? current.scrollbarWidth : 0,
margin = current.margin,
wMargin = margin[1] + margin[3],
hMargin = margin[0] + margin[2],
wMargin = getScalar(margin[1] + margin[3]),
hMargin = getScalar(margin[0] + margin[2]),
wPadding,
hPadding,
wSpace,
@ -1206,10 +1201,10 @@
body;
// Reset dimensions so we could re-check actual size
wrap.add(skin).add(inner).width('auto').height('auto');
wrap.add(skin).add(inner).width('auto').height('auto').removeClass('fancybox-tmp');
wPadding = skin.outerWidth(true) - skin.width();
hPadding = skin.outerHeight(true) - skin.height();
wPadding = getScalar(skin.outerWidth(true) - skin.width());
hPadding = getScalar(skin.outerHeight(true) - skin.height());
// Any space between content and viewport (margin, padding, border, title)
wSpace = wMargin + wPadding;
@ -1277,43 +1272,52 @@
origMaxWidth = maxWidth;
origMaxHeight = maxHeight;
if (current.fitToView) {
maxWidth = Math.min(viewport.w - wSpace, maxWidth);
maxHeight = Math.min(viewport.h - hSpace, maxHeight);
}
maxWidth_ = viewport.w - wMargin;
maxHeight_ = viewport.h - hMargin;
if (current.aspectRatio) {
if (width > maxWidth) {
width = maxWidth;
height = width / ratio;
height = getScalar(width / ratio);
}
if (height > maxHeight) {
height = maxHeight;
width = height * ratio;
width = getScalar(height * ratio);
}
if (width < minWidth) {
width = minWidth;
height = width / ratio;
height = getScalar(width / ratio);
}
if (height < minHeight) {
height = minHeight;
width = height * ratio;
width = getScalar(height * ratio);
}
} else {
width = Math.max(minWidth, Math.min(width, maxWidth));
width = Math.max(minWidth, Math.min(width, maxWidth));
if (current.autoHeight && current.type !== 'iframe') {
inner.width( width );
height = inner.height();
}
height = Math.max(minHeight, Math.min(height, maxHeight));
}
// Try to fit inside viewport (including the title)
if (current.fitToView) {
maxWidth = Math.min(viewport.w - wSpace, maxWidth);
maxHeight = Math.min(viewport.h - hSpace, maxHeight);
inner.width( width ).height( height );
inner.width( getScalar( width ) ).height( getScalar( height ) );
wrap.width( getScalar( width + wPadding ) );
wrap.width( width + wPadding );
// Real wrap dimensions
width_ = wrap.width();
@ -1326,21 +1330,21 @@
}
height = Math.max(minHeight, Math.min(maxHeight, height - 10));
width = height * ratio;
width = getScalar(height * ratio);
if (width < minWidth) {
width = minWidth;
height = width / ratio;
height = getScalar(width / ratio);
}
if (width > maxWidth) {
width = maxWidth;
height = width / ratio;
height = getScalar(width / ratio);
}
inner.width( getScalar( width ) ).height( getScalar( height ) );
inner.width( width ).height( height );
wrap.width( getScalar( width + wPadding ) );
wrap.width( width + wPadding );
width_ = wrap.width();
height_ = wrap.height();
@ -1356,9 +1360,9 @@
width += scrollOut;
}
inner.width( getScalar( width ) ).height( getScalar( height ) );
inner.width( width ).height( height );
wrap.width( getScalar( width + wPadding ) );
wrap.width( width + wPadding );
width_ = wrap.width();
height_ = wrap.height();
@ -1421,14 +1425,16 @@
F.isOpen = F.isOpened = true;
F.wrap.addClass('fancybox-opened').css('overflow', 'visible');
F.wrap.css('overflow', 'visible').addClass('fancybox-opened');
F.reposition();
F.update();
// Assign a click event
if (current.closeClick || current.nextClick) {
if ( current.closeClick || (current.nextClick && F.group.length > 1) ) {
F.inner.css('cursor', 'pointer').bind('click.fb', function(e) {
if (!$(e.target).is('a') && !$(e.target).parent().is('a')) {
e.preventDefault();
F[ current.closeClick ? 'close' : 'next' ]();
}
});
@ -1436,7 +1442,11 @@
// Create a close button
if (current.closeBtn) {
$(current.tpl.closeBtn).appendTo(F.skin).bind('click.fb', F.close);
$(current.tpl.closeBtn).appendTo(F.skin).bind( isTouch ? 'touchstart.fb' : 'click.fb', function(e) {
e.preventDefault();
F.close();
});
}
// Create navigation arrows
@ -1463,10 +1473,10 @@
}
},
_afterZoomOut: function () {
var current = F.current;
_afterZoomOut: function ( obj ) {
obj = obj || F.current;
$('.fancybox-wrap').stop(true).trigger('onReset').remove();
$('.fancybox-wrap').trigger('onReset').remove();
$.extend(F, {
group : {},
@ -1483,7 +1493,7 @@
inner : null
});
F.trigger('afterClose', current);
F.trigger('afterClose', obj);
}
});
@ -1524,7 +1534,7 @@
pos.left = viewport.x + (viewport.w - width) * current.leftRatio;
}
if (current.locked) {
if (F.wrap.css('position') === 'fixed' || current.locked) {
pos.top -= viewport.y;
pos.left -= viewport.x;
}
@ -1646,7 +1656,10 @@
F.wrap.css(startPos).animate(endPos, {
duration : current.nextSpeed,
easing : current.nextEasing,
complete : F._afterZoomIn
complete : function() {
// This helps FireFox to properly render the box
setTimeout(F._afterZoomIn, 20);
}
});
}
},
@ -1677,9 +1690,90 @@
*/
F.helpers.overlay = {
overlay: null,
defaults : {
closeClick : true, // if true, fancyBox will be closed when user clicks on the overlay
speedOut : 200, // duration of fadeOut animation
showEarly : true, // indicates if should be opened immediately or wait until the content is ready
css : {}, // custom CSS properties
locked : !isTouch, // if true, the content will be locked into overlay
fixed : true // if false, the overlay CSS position property will not be set to "fixed"
},
update: function () {
overlay : null, // current handle
fixed : false, // indicates if the overlay has position "fixed"
// Public methods
create : function(opts) {
opts = $.extend({}, this.defaults, opts);
if (this.overlay) {
this.close();
}
this.overlay = $('<div class="fancybox-overlay"></div>').appendTo( 'body' );
this.fixed = false;
if (opts.fixed && F.defaults.fixed) {
this.overlay.addClass('fancybox-overlay-fixed');
this.fixed = true;
}
},
open : function(opts) {
var that = this;
opts = $.extend({}, this.defaults, opts);
if (this.overlay) {
this.overlay.unbind('.overlay').width('auto').height('auto');
} else {
this.create(opts);
}
if (!this.fixed) {
W.bind('resize.overlay', $.proxy( this.update, this) );
this.update();
}
if (opts.closeClick) {
this.overlay.bind('click.overlay', function(e) {
if ($(e.target).hasClass('fancybox-overlay')) {
if (F.isActive) {
F.close();
} else {
that.close();
}
}
});
}
this.overlay.css( opts.css ).show();
},
close : function() {
$('.fancybox-overlay').remove();
W.unbind('resize.overlay');
this.overlay = null;
if (this.margin !== false) {
$('body').css('margin-right', this.margin);
this.margin = false;
}
if (this.el) {
this.el.removeClass('fancybox-lock');
}
},
// Private, callbacks
update : function () {
var width = '100%', offsetWidth;
// Reset width/height so it will not mess
@ -1701,25 +1795,19 @@
},
// This is where we can manipulate DOM, because later it would cause iframes to reload
onReady: function (opts, obj) {
onReady : function (opts, obj) {
$('.fancybox-overlay').stop(true, true);
if (!this.overlay) {
$.extend(this, {
overlay : $('<div class="fancybox-overlay"></div>').appendTo( obj.parent ),
margin : D.height() > W.height() || $('body').css('overflow-y') === 'scroll' ? $('body').css('margin-right') : false,
el : document.all && !document.querySelector ? $('html') : $('body')
});
this.margin = D.height() > W.height() || $('body').css('overflow-y') === 'scroll' ? $('body').css('margin-right') : false;
this.el = document.all && !document.querySelector ? $('html') : $('body');
this.create(opts);
}
if (obj.fixed && !isTouch) {
this.overlay.addClass('fancybox-overlay-fixed');
if (obj.autoCenter) {
this.overlay.append( obj.wrap );
obj.locked = this.overlay;
}
if (opts.locked && this.fixed) {
obj.locked = this.overlay.append( obj.wrap );
obj.fixed = false;
}
if (opts.showEarly === true) {
@ -1728,54 +1816,28 @@
},
beforeShow : function(opts, obj) {
var overlay = this.overlay.unbind('.fb').width('auto').height('auto').css( opts.css );
if (obj.locked) {
this.el.addClass('fancybox-lock');
if (opts.closeClick) {
overlay.bind('click.fb', function(e) {
if ($(e.target).hasClass('fancybox-overlay')) {
F.close();
}
});
}
if (obj.fixed && !isTouch) {
if (obj.locked) {
this.el.addClass('fancybox-lock');
if (this.margin !== false) {
$('body').css('margin-right', getScalar( this.margin ) + obj.scrollbarWidth);
}
if (this.margin !== false) {
$('body').css('margin-right', getScalar( this.margin ) + obj.scrollbarWidth);
}
} else {
this.update();
}
overlay.show();
this.open(opts);
},
onUpdate : function(opts, obj) {
if (!obj.fixed || isTouch) {
onUpdate : function() {
if (!this.fixed) {
this.update();
}
},
afterClose: function (opts) {
var that = this,
speed = opts.speedOut || 0;
// Remove overlay if exists and fancyBox is not opening
// (e.g., it is not being open using afterClose callback)
if (that.overlay && !F.isActive) {
that.overlay.fadeOut(speed || 0, function () {
$('body').css('margin-right', that.margin);
that.el.removeClass('fancybox-lock');
that.overlay.remove();
that.overlay = null;
});
if (this.overlay && !F.isActive) {
this.overlay.fadeOut(opts.speedOut, $.proxy( this.close, this ));
}
}
};
@ -1785,12 +1847,22 @@
*/
F.helpers.title = {
defaults : {
type : 'float', // 'float', 'inside', 'outside' or 'over',
position : 'bottom' // 'top' or 'bottom'
},
beforeShow: function (opts) {
var text = F.current.title,
type = opts.type,
var current = F.current,
text = current.title,
type = opts.type,
title,
target;
if ($.isFunction(text)) {
text = text.call(current.element, current);
}
if (!isString(text) || $.trim(text) === '') {
return;
}
@ -1813,22 +1885,20 @@
default: // 'float'
target = F.skin;
title
.appendTo('body')
.width(title.width()) //This helps for some browsers
.wrapInner('<span class="child"></span>');
title.appendTo('body');
//Increase bottom margin so this title will also fit into viewport
F.current.margin[2] += Math.abs( getScalar(title.css('margin-bottom')) );
if ($.browser.msie) {
title.width( title.width() );
}
title.wrapInner('<span class="child"></span>');
//Increase bottom margin so this title will also fit into viewport
F.current.margin[2] += Math.abs( getScalar(title.css('margin-bottom')) );
break;
}
if (opts.position === 'top') {
title.prependTo(target);
} else {
title.appendTo(target);
}
title[ (opts.position === 'top' ? 'prependTo' : 'appendTo') ](target);
}
};
@ -1869,10 +1939,13 @@
if (!selector || options.live === false) {
that.unbind('click.fb-start').bind('click.fb-start', run);
} else {
D.undelegate(selector, 'click.fb-start').delegate(selector + ":not('.fancybox-item, .fancybox-nav')", 'click.fb-start', run);
}
this.filter('[data-fancybox-start=1]').trigger('click');
return this;
};

View file

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

756
lib/server.js Normal file
View file

@ -0,0 +1,756 @@
(function(){
"use strict";
var main = global.cloudcmd.main,
/*
* Обьект содержащий все функции и переменные
* серверной части Cloud Commander'а
*/
CloudServer = {
/* base configuration */
Config : {
server : true,
socket : true,
port : 80
},
/* функция, которая генерирует заголовки
* файлов, отправляемые сервером клиенту
*/
generateHeaders : function () {},
/* функция высылает
* данные клиенту
*/
sendResponse : function () {},
/* Асоциативный масив обьектов для
* работы с ответами сервера
* высылаемыми на запрос о файле и
* хранащий информацию в виде
* Responses[name]=responce;
*/
Responses : {},
/*
* Асоциативный масив статусов
* ответов сервера
* высылаемыми на запрос о файле и
* хранащий информацию в виде
* Statuses[name] = 404;
*/
Statuses : {},
/*
* queries of file params
* example: ?download
*/
Queries : {},
/* ПЕРЕМЕННЫЕ
* Поддержка браузером JS */
NoJS : true,
/* Поддержка gzip-сжатия браузером */
Gzip : false,
/* server varible */
Server : {},
/* КОНСТАНТЫ */
INDEX : main.DIR + 'index.html'
},
DirPath = '/',
OK = 200,
DIR = main.Dir,
LIBDIR = main.LIBDIR,
SRVDIR = main.SRVDIR,
/* модуль для работы с путями*/
Path = main.path,
Fs = main.fs, /* модуль для работы с файловой системой*/
Querystring = main.querystring,
/* node v0.4 not contains zlib */
Zlib = main.zlib; /* модуль для сжатия данных gzip-ом*/
if(!Zlib)
console.log('to use gzip-commpression' +
'you should use newer node version\n');
/* добавляем модуль с функциями */
var CloudFunc = main.cloudfunc,
Util = main.util;
/* Обьект для работы с кэшем */
CloudServer.Cache = main.cache,
CloudServer.Minify = main.minify,
CloudServer.AppCache = main.appcache,
CloudServer.Socket = main.socket;
/* базовая инициализация */
CloudServer.init = (function(pAppCachProcessing){
var lConfig = this.Config,
lMinify = this.Minify,
lCache = this.Cache,
lAppCache = this.AppCache;
/* Переменная в которой храниться кэш*/
lCache.setAllowed(lConfig.cache.allowed);
/* Change default parameters of
* js/css/html minification
*/
lMinify.setAllowed(lConfig.minification);
/* Если нужно минимизируем скрипты */
lMinify._allowed = lMinify.doit();
/* создаём файл app cache */
if( lConfig.appcache && lAppCache && lConfig.server )
Util.exec( pAppCachProcessing );
});
/**
* Функция создаёт сервер
* @param pConfig
*/
CloudServer.start = function (pConfig, pProcessing) {
if(!pProcessing)
pProcessing = {};
if(pConfig)
this.Config = pConfig;
else
console.log('warning: configuretion file config.json not found...\n' +
'using default values...\n' +
JSON.stringify(this.Config));
var lConfig = this.Config;
CloudServer.indexProcessing = pProcessing.index;
CloudServer.rest = pProcessing.rest;
this.init(pProcessing.appcache);
this.Port = process.env.PORT || /* c9 */
process.env.app_port || /* nodester */
process.env.VCAP_APP_PORT || /* cloudfoundry */
lConfig.port;
this.IP = process.env.IP || /* c9 */
this.Config.ip;
if(!this.IP)
this.IP = main.WIN32 ? '127.0.0.1' : '0.0.0.0';
/* server mode or testing mode */
if (lConfig.server) {
var http = main.http,
lError = Util.tryCatchLog(Util.bind(function(){
this.Server = http.createServer(this._controller);
this.Server.listen(this.Port, this.IP);
if(lConfig.socket && CloudServer.Socket){
CloudServer.Socket.listen(this.Server);
console.log('sockets running');
}
else
console.log('sockets disabled');
console.log('Cloud Commander server running at http://' +
this.IP + ':' + this.Port);
}, this));
if(lError)
console.log('Cloud Commander server could not started');
}else
console.log('Cloud Commander testing mode');
};
/**
* Главная функция, через которую проихсодит
* взаимодействие, обмен данными с клиентом
* @param req - запрос клиента (Request)
* @param res - ответ сервера (Response)
*/
CloudServer._controller = function(pReq, pRes)
{
/* Читаем содержимое папки, переданное в url */
var lConfig = CloudServer.Config,
url = main.url,
lParsedUrl = url.parse(pReq.url),
pathname = lParsedUrl.pathname,
/* varible contain one of queris:
* download - change content-type for
* make downloading process
* from client js
* json - /no-js/ will be removed, and
* if we will wont get directory
* content wi will set json
* query like this
* ?json
*/
lQuery = lParsedUrl.query;
if(lQuery)
console.log('query = ' + lQuery);
/* added supporting of Russian language in directory names */
pathname = Querystring.unescape(pathname);
console.log('pathname: ' + pathname);
/* получаем поддерживаемые браузером кодировки*/
var lAcceptEncoding = pReq.headers['accept-encoding'];
/* запоминаем поддерживает ли браузер
* gzip-сжатие при каждом обращении к серверу
* и доступен ли нам модуль zlib
*/
if (lAcceptEncoding &&
lAcceptEncoding.match(/\bgzip\b/) && Zlib)
CloudServer.Gzip = true;
/* путь в ссылке, который говорит
* что js отключен
*/
var lNoJS_s = CloudFunc.NOJS,
lFS_s = CloudFunc.FS;
console.log("request for " + pathname + " received...");
if( lConfig.rest ){
var lRestWas = Util.exec(CloudServer.rest, {
request : pReq,
response : pRes
});
if(lRestWas)
return;
}
/* если в пути нет информации ни о ФС,
* ни об отсутствии js,
* ни о том, что это корневой
* каталог - загружаем файлы проэкта
*/
if ( !Util.isContainStr(pathname, lFS_s) &&
!Util.isContainStr(pathname, lNoJS_s) &&
!Util.strCmp(pathname, '/') &&
!Util.strCmp(lQuery, 'json') ) {
/* если имена файлов проекта - загружаем их *
* убираем слеш и читаем файл с текущец директории */
/* добавляем текующий каталог к пути */
var lName = '.' + pathname;
console.log('reading ' + lName);
/* watching is file changed */
if(lConfig.appcache)
CloudServer.AppCache.watch(lName);
/* сохраняем указатель на response и имя */
CloudServer.Responses[lName] = pRes;
/* saving status OK for current file */
CloudServer.Statuses[lName] = OK;
/* Берём значение из кэша
* сжатый файл - если gzip-поддерживаеться браузером
* не сжатый - в обратном случае
*/
var lFileData = CloudServer.Cache.get(
CloudServer.Gzip?(lName+'_gzip') : lName);
console.log(Path.basename(lName));
var lMinify = CloudServer.Minify;
/* object thet contains information
* about the source of file data
*/
var lFromCache_o = {'cache': true};
/* if cache is empty and Cache allowed and Minify_allowed
* and in Minifys cache is files, so save it to
* CloudServer cache
*/
if(!lFileData && lMinify._allowed){
console.log('trying to read data from Minify.Cache');
lFromCache_o.cache = false;
lFileData = CloudServer.Minify.Cache[
Path.basename(lName)];
}
var lReadFileFunc_f = CloudServer.getReadFileFunc(lName),
/* если там что-то есть передаём данные в функцию readFile */
lResult = lFileData;
if(lResult){
/* if file readed not from cache -
* he readed from minified cache
*/
lFromCache_o.minify = !lFromCache_o.cache;
console.log(lName + ' readed from cache');
/* передаём данные с кэша,
* если gzip включен - сжатые
* в обратном случае - несжатые
*/
lReadFileFunc_f(undefined, lFileData, lFromCache_o);
}
/* if file not in one of caches
* and minimizing setted then minimize it
*/
else if(lName.indexOf('min') < 0 && CloudServer.Minify){
var lMin_o = lConfig.minification,
lCheck_f = function(pExt){
return Util.checkExtension(lName,pExt);
};
lResult = (lCheck_f('js') && lMin_o.js) ||
(lCheck_f('css') && lMin_o.css) ||
(lCheck_f('html') && lMin_o.html);
if(lResult){
lResult = CloudServer.Minify.optimize(lName, {
cache: true,
callback: function(pFileData){
lReadFileFunc_f(undefined, pFileData, false);
}
});
}
}
if(!lResult)
main.sendFile({
name: lName,
request: pReq,
response: pRes
});
}else{/* если мы имеем дело с файловой системой*/
/* если путь не начинаеться с no-js - значит
* js включен
*/
/* убираем пометку cloud, без которой c9.io
* не работает поскольку путь из двух слешей
* (/fs/no-js/) - очень короткий, нужно
* длиннее
*/
if(pathname.indexOf(lNoJS_s) !== lFS_s.length && pathname !== '/')
CloudServer.NoJS = false;
else{
CloudServer.NoJS = true;
pathname = Util.removeStr(pathname, lNoJS_s);
}
/* убираем индекс файловой системы */
if(pathname.indexOf(lFS_s) === 0){
pathname = Util.removeStr(pathname, lFS_s);
/* если посетитель только зашел на сайт
* no-js будет пустым, как и fs.
* Если в пути нету fs - посетитель только зашел на сайт
* загружаем его полностью.
*/
}
/* if query json setted up
* load json data, no-js false.
*/
if(lQuery === 'json')
CloudServer.NoJS = false;
/* если в итоге путь пустой
* делаем его корневым
*/
if (pathname === '')
pathname = '/';
DirPath = pathname;
CloudServer.Responses[DirPath] = pRes;
CloudServer.Statuses[DirPath] = OK;
/* saving query of current file */
CloudServer.Queries[DirPath] = lQuery;
Util.log(lQuery);
console.log(DirPath);
/* читаем основные данные о файле */
Fs.stat(DirPath, CloudServer._stated);
/* если установлено сжатие
* меняем название html-файла и
* загружаем сжатый html-файл в дальнейшем
*/
CloudServer.INDEX = (CloudServer.Minify._allowed.html ?
CloudServer.Minify.MinFolder + 'index.min.html'
: CloudServer.INDEX);
/*
* сохраним указатель на response
* и на статус ответа
*/
CloudServer.Responses[CloudServer.INDEX] = pRes;
CloudServer.Statuses [CloudServer.INDEX] = OK;
}
};
/**
* Function geted stat information about file
* @param pError
* @param pStat
*/
CloudServer._stated = function(pError, pStat){
if(pError){
CloudServer.Statuses[DirPath] = 404;
CloudServer.sendResponse('OK', pError.toString(), DirPath);
return;
}
/* если это каталог - читаем его содержимое */
if(pStat){
if(pStat.isDirectory())
Fs.readdir(DirPath, CloudServer._readDir);
/* отдаём файл */
else if(pStat.isFile()){
Fs.readFile(DirPath, CloudServer.getReadFileFunc(DirPath));
console.log('reading file: '+ DirPath);
}
}
};
/**
* Функция читает ссылку или выводит информацию об ошибке
* @param pError
* @param pFiles
*/
CloudServer._readDir = function (pError, pFiles)
{
if(pError){
console.log(pError);
CloudServer.Statuses[DirPath] = 404;
CloudServer.sendResponse('OK',pError.toString(),
DirPath);
return;
}
/* Если мы не в корне добавляем слеш к будующим ссылкам */
if(DirPath !== '/')
DirPath += '/';
pFiles = pFiles.sort();
var lCount = 0,
lStats = {};
/* asyn getting file states
* and putting it to lStats object
*/
var getFilesStat_f = function(pName){
return function(pError, pStat){
var fReturnFalse = function(){
return false;
};
if(pError)
lStats[pName] = {
'mode':0,
'size':0,
'isDirectory':fReturnFalse
};
else
lStats[pName] = pStat;
/* if this file is last - moving next */
if(++lCount === pFiles.length)
CloudServer._fillJSON(lStats, pFiles);
};
};
if(pFiles.length)
for(var i = 0; i < pFiles.length; i++)
/* Получаем информацию о файле */
Fs.stat(DirPath + pFiles[i], getFilesStat_f(pFiles[i]));
else
CloudServer._fillJSON(null, pFiles);
};
/**
* Function fill JSON by file stats
*
* @param pStats - object, contain file stats.
* example {'1.txt': stat}
*
* @param pFiles - array of files of current directory
*/
CloudServer._fillJSON = function(pStats, pFiles){
/* данные о файлах в формате JSON*/
var lJSON = [],
lJSONFile = {},
lHeader, /* заголовок ответа сервера */
lList;
lJSON[0] = {
path:DirPath,
size:'dir'
};
var fReturnFalse = function returnFalse(){return false;};
for(var i = 0; i < pFiles.length; i++){
/*
*Переводим права доступа в 8-ричную систему
*/
var lName = pFiles[i],
lMode = (pStats[lName].mode-0).toString(8),
lStats = pStats[lName],
lIsDir = lStats.isDirectory();
/* Если папка - выводим пиктограмму папки *
* В противоположном случае - файла */
lJSONFile = {
'name':pFiles[i],
'size' : (lIsDir ? 'dir' : lStats.size),
'uid' : lStats.uid,
'mode' : lMode};
lJSON[i+1] = lJSONFile;
}
/* если js недоступен
* или отключен выcылаем html-код
* и прописываем соответствующие заголовки
*/
if(CloudServer.NoJS){
var lPanel = CloudFunc.buildFromJSON(lJSON);
lList = '<ul id=left class=panel>' + lPanel + '</ul>' +
'<ul id=right class=panel>' + lPanel + '</ul>';
/* пробуем достать данные из кэша
* с жатием или без, взависимости
* от настроек
*/
var lFileData = CloudServer.Cache.get(CloudServer.INDEX);
/* если их нет там - вычитываем из файла*/
if(!lFileData) {
Fs.readFile(CloudServer.INDEX,
CloudServer.indexReaded(lList));
}else {
var lReaded_f = CloudServer.indexReaded(lList);
lReaded_f(false, lFileData);
}
}else{
DirPath = DirPath.substr(DirPath, DirPath.lastIndexOf('/') );
var lQuyery = CloudServer.Queries[DirPath];
DirPath += '.json';
CloudServer.Queries[DirPath] = lQuyery;
/* в обычном режиме(когда js включен
* высылаем json-структуру файлов
* с соответствующими заголовками
*/
lList = JSON.stringify(lJSON);
lHeader = main.generateHeaders(DirPath, CloudServer.Gzip, lQuyery);
/* если браузер поддерживает gzip-сжатие - сжимаем данные*/
if(CloudServer.Gzip){
var lGzipCB = CloudServer.getGzipDataFunc(lHeader, CloudServer.INDEX);
Zlib.gzip(lList, lGzipCB);
}
/* если не поддерживаеться - отсылаем данные без сжатия*/
else
CloudServer.sendResponse(lHeader, lList, CloudServer.INDEX);
}
};
/**
*@param pList
*/
CloudServer.indexReaded = function(pList){
return function(pError, pIndex){
if(pError){
return console.log(pError);
}
var lSrv = CloudServer,
lIndexName = lSrv.INDEX;
/* и сохраняем в кэш */
lSrv.Cache.set(lIndexName, pIndex);
pIndex = pIndex.toString();
var lProccessed,
lIndexProccessing = lSrv.indexProcessing;
lProccessed = Util.exec(lIndexProccessing, {
data : pIndex,
additional : pList
});
if(lProccessed)
pIndex = lProccessed;
/*
* если браузер поддерживает gzip-сжатие
* высылаем заголовок в зависимости от типа файла
*/
var lQuery = lSrv.Queries[lIndexName],
lHeader = main.generateHeaders(lIndexName, lSrv.Gzip, lQuery);
/* если браузер поддерживает gzip-сжатие - сжимаем данные*/
if(lSrv.Gzip) {
Zlib.gzip(pIndex,
lSrv.getGzipDataFunc(lHeader, lIndexName));
}
/* если не поддерживаеться - отсылаем данные без сжатия*/
else
lSrv.sendResponse(lHeader, pIndex, lIndexName);
};
};
/**
* Функция генерирует функцию считывания файла
* таким образом, что бы у нас было
* имя считываемого файла
* @param pName - полное имя файла
*/
CloudServer.getReadFileFunc = function(pName){
/*
* @pError - ошибка
* @pData - данные
* @pFromCache_o - прочитано с файла,
* или из одного из кешей
* Пример {cache: false, minify: true}
*/
var lReadFile = function(pError, pData, pFromCache_o){
var lSrv = CloudServer;
if (!pError){
console.log('file ' + pName + ' readed');
/* берём из кэша данные файла
* если их нет в кэше -
* сохраняем
*/
if(pFromCache_o && !pFromCache_o.cache &&
lSrv.Cache.isAllowed)
lSrv.Cache.set(pName, pData);
/* если кэш есть
* сохраняем его в переменную
* которая до этого будет пустая
* по скольку мы будем вызывать этот метод
* сами, ведь файл уже вычитан
*/
var lQuery = lSrv.Queries[pName],
lHeader = main.generateHeaders(pName, lSrv.Gzip, lQuery);
/* если браузер поддерживает gzip-сжатие - сжимаем данные*/
if( lSrv.Gzip && !(pFromCache_o && pFromCache_o.cache) )
/* сжимаем содержимое */
Zlib.gzip(pData,lSrv.getGzipDataFunc(lHeader, pName));
else
/* высылаем несжатые данные */
lSrv.sendResponse(lHeader, pData, pName);
}
else{
console.log(pError.path);
if(pError.path !== 'passwd.json'){
console.log(pError);
/* sending page not found */
lSrv.Statuses[pName] = 404;
lSrv.sendResponse('file not found', pError.toString(), pName);
}else
lSrv.sendResponse('OK', 'passwd.json');
}
};
return lReadFile;
};
/**
* Функция получает сжатые данные
* @param pHeader - заголовок файла
* @pName
*/
CloudServer.getGzipDataFunc = function(pHeader, pName){
return function(error, pResult){
if(!error){
/* отправляем сжатые данные
* вместе с заголовком
* если установлена работа с кэшем
* сохраняем сжатые данные
*/
if(CloudServer.Cache.isAllowed){
/* устанавливаем кєш */
console.log(pName+' gziped');
CloudServer.Cache.set(pName+'_gzip', pResult);
}
CloudServer.sendResponse(pHeader, pResult, pName);
}
else{
console.log(error);
CloudServer.sendResponse(pHeader, error);
}
};
};
/**
* Функция высылает ответ серверу
* @param pHead - заголовок
* @param Data - данные
* @param pName - имя отсылаемого файла
*/
CloudServer.sendResponse = function(pHead, pData, pName){
/* если у нас есть указатель на responce
* для соответствующего файла -
* высылаем его
*/
var lResponse = CloudServer.Responses[pName],
lStatus = CloudServer.Statuses[pName];
if(lResponse){
lResponse.writeHead(lStatus, pHead);
lResponse.end(pData);
console.log(pName + ' sended');
}
};
/**
* start server function
* @param pConfig
* @param pProcessing {index, appcache, rest}
*/
exports.start = function(pConfig, pProcessing){
CloudServer.start(pConfig, pProcessing);
};
exports.CloudServer = CloudServer;
})();

View file

@ -1,156 +1,148 @@
var fs = require('fs');
/* varible contain all watched file names
* {name: true}
*/
var FileNames = {},
NamesList_s = '',
FallBack_s = '';
/* function thet use for crossplatform
* access to fs.watch or fs.watchFile function
*/
var fs_watch = null;
var on_fs_watch = null;
var firstFileRead_b = true;
var Manifest = '';
setWatachFunctions();
/* function add file or files to manifest
* Examples:
* exports.addFiles('jquery.js'),
* exports.addFiles(['jquery.js', 'client.js']);
* exports.addFiles([{'http://cdn.jquery/jq.js':'jquery.js'}, 'client.js']);
*/
exports.addFiles = function(pFileNames){
/* if a couple files */
if(pFileNames instanceof Array)
for(var i=0; i < pFileNames.length; i++){
/* if fallback setted up */
var lCurrentName = pFileNames[i];
if(typeof lCurrentName === 'object')
for(var lName in lCurrentName){
FallBack_s += lName + ' ' + lCurrentName[lName] + '\n';
exports.watch(lCurrentName[lName]);
}
else exports.watch(pFileNames[i]);
}
else exports.watch(pFileNames);
(function(){
"use strict";
};
exports.createManifest = function(){
var lAllNames = cloudRequire(process.cwd() + '/hashes');
if(lAllNames)
for(var lName in lAllNames){
if(lName.indexOf('min') > 0)
lName = './min/' + lName;
exports.watch(lName);
}
processManifest();
};
exports.watch = function(pFileName){
console.log(pFileName + ' is watched');
if(!global.cloudcmd)
return console.log(
'# appcache.js' + '\n' +
'# -----------' + '\n' +
'# Module is part of Cloud Commander,' + '\n' +
'# used for work with Aplication Cache.' + '\n' +
'# If you wont to see at work set appcache: true' + '\n' +
'# in config.json and start cloudcmd.js' + '\n' +
'# http://github.com/coderaiser/cloudcmd' + '\n');
if(!FileNames[pFileName] &&
pFileName !== './cloudcmd.appcache'){
/* adding try...catch
* if watched files would be more then system limit
*/
var lWatch_f = tryCatch(function(){
fs_watch(pFileName, on_fs_watch(pFileName));
});
/* if file.exists function exist and
* file actually exists
*/
if(fs.exists)
fs.exists(pFileName, lWatch_f);
else lWatch_f();
NamesList_s += pFileName + '\n';
FileNames[pFileName] = true;
}
else if(firstFileRead_b){
processManifest();
firstFileRead_b = false;
}
};
function setWatachFunctions(){
if(process.platform === 'win32'){
/* good on windows */
fs_watch = fs.watch;
on_fs_watch = onWatch;
}
else{
/* good on linux */
fs_watch = fs.watchFile;
on_fs_watch = onWatchFile;
}
}
function onWatch (pFileName){
return function(pEvent, pFileName){
console.log(pEvent);
console.log('file ' + pFileName + ' is changed');
var main = global.cloudcmd.main,
fs = main.fs,
Util = main.util,
/* varible contain all watched file names
* {name: true}
*/
FileNames = {},
NamesList_s = '',
FallBack_s = '',
/* function thet use for crossplatform
* access to fs.watch or fs.watchFile function
*/
fs_watch = null,
on_fs_watch = null,
firstFileRead_b = true,
Manifest = '';
setWatachFunctions();
/* function add file or files to manifest
* Examples:
* exports.addFiles('jquery.js'),
* exports.addFiles(['jquery.js', 'client.js']);
* exports.addFiles([{'http://cdn.jquery/jq.js':'jquery.js'}, 'client.js']);
*/
exports.addFiles = function(pFileNames){
/* if a couple files */
if(pFileNames instanceof Array)
for(var i=0; i < pFileNames.length; i++){
/* if fallback setted up */
var lCurrentName = pFileNames[i];
if(typeof lCurrentName === 'object')
for(var lName in lCurrentName){
FallBack_s += lName + ' ' + lCurrentName[lName] + '\n';
exports.watch(lCurrentName[lName]);
}
else exports.watch(pFileNames[i]);
}
else exports.watch(pFileNames);
};
exports.createManifest = function(){
var lAllNames = main.require('node_modules/minify/hashes');
if(lAllNames)
for(var lName in lAllNames){
if(lName.indexOf('min') > 0)
lName = 'node_modules/minify/min/' + lName;
exports.watch(lName);
}
processManifest();
};
}
function onWatchFile(pFileName){
return function(pCurr, pPrev){
if(pCurr.mtime !== pPrev.mtime){
exports.watch = function(pFileName){
console.log(pFileName + ' is watched');
if(!FileNames[pFileName] &&
pFileName !== './cloudcmd.appcache'){
/* adding try...catch
* if watched files would be more then system limit
*/
var lWatch_f = function(){
Util.tryCatch(function(){
fs_watch(pFileName, on_fs_watch(pFileName));
});
};
/* if file.exists function exist and
* file actually exists
*/
if(fs.exists)
fs.exists(pFileName, lWatch_f);
else lWatch_f();
NamesList_s += pFileName + '\n';
FileNames[pFileName] = true;
}
else if(firstFileRead_b){
processManifest();
firstFileRead_b = false;
}
};
function setWatachFunctions(){
if(main.WIN32){
/* good on windows */
fs_watch = fs.watch;
on_fs_watch = onWatch;
}
else{
/* good on linux */
fs_watch = fs.watchFile;
on_fs_watch = onWatchFile;
}
}
function onWatch (){
return function(pEvent, pFileName){
console.log(pEvent);
console.log('file ' + pFileName + ' is changed');
processManifest();
}
};
}
function processManifest(){
Manifest = 'CACHE MANIFEST\n' +
'#' + new Date() + '\n' +
'CACHE:\n' +
NamesList_s +
'NETWORK:\n' +
'*\n' +
'FALLBACK:\n' +
FallBack_s;
fs.writeFile('cloudcmd.appcache', Manifest, function(){
console.log('cloudcmd.appcache refreshed');
});
}
/* function do safe require of needed module */
function cloudRequire(pModule){
try{
return require(pModule);
}
catch(pError){
return false;
}
}
/**
* function execute param function in
* try...catch block
*
* @param pFunction_f
*/
function tryCatch(pFunction_f){
return function(){
var lRet = true;
try{
pFunction_f();
}
catch(pError){lRet = pError;}
return lRet;
};
}
};
}
function onWatchFile(pFileName){
return function(pCurr, pPrev){
if(pCurr.mtime !== pPrev.mtime){
console.log('file ' + pFileName + ' is changed');
processManifest();
}
};
}
function processManifest(){
Manifest = 'CACHE MANIFEST\n' +
'#' + new Date() + '\n' +
'CACHE:\n' +
NamesList_s +
'NETWORK:\n' +
'*\n' +
'FALLBACK:\n' +
FallBack_s;
fs.writeFile('cloudcmd.appcache', Manifest, function(){
console.log('cloudcmd.appcache refreshed');
});
}
})();

83
lib/server/auth.js Normal file
View file

@ -0,0 +1,83 @@
/* https://github.com/prose/gatekeeper */
(function(){
"use strict";
if(!global.cloudcmd)
return console.log(
'# auth.js' + '\n' +
'# -----------' + '\n' +
'# Module is part of Cloud Commander,' + '\n' +
'# used for work with authentication.' + '\n' +
'# If you wont to see at work set auth' + '\n' +
'# parameters in config.json or environment' + '\n' +
'# and start cloudcmd.js or just do' + '\n' +
'# require(\'auth.js\').auth(pCode, pCallBack)' + '\n' +
'# http://github.com/coderaiser/cloudcmd' + '\n');
var main = global.cloudcmd.main,
https = main.https,
qs = main.querystring,
Config = main.config,
Util = main.util,
GithubAuth = {
host: "github.com",
port: 443,
path: "/login/oauth/access_token",
method: "POST"
};
/**
* function do authentication
* @param pCode
* @param pCallBack
*/
exports.auth = function(pCode, pCallBack){
pCode = pCode.replace('code=','');
console.log(pCode);
authenticate(pCode, function(token) {
var result = { "token": token };
console.log(result);
Util.exec(pCallBack, result);
});
};
function authenticate(pCode, pCallBack) {
var lId = Config.github_key,
lSecret = Config.github_secret,
lEnv = process.env,
lClientId = lEnv.github_key || lId,
lClientSecret = lEnv.github_secret || lSecret;
console.log(lClientId);
console.log(lClientSecret);
var data = qs.stringify({
client_id : lClientId,
client_secret : lClientSecret,
code : pCode
});
console.log(data);
GithubAuth.headers = { 'content-length': data.length };
var body = "",
req = https.request(GithubAuth, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) { body += chunk; });
res.on('end', function() {
pCallBack(qs.parse(body).access_token);
});
});
req.write(data);
req.end();
req.on('error', function(e) { pCallBack(e.message); });
}
})();

60
lib/server/cache.js Normal file
View file

@ -0,0 +1,60 @@
(function(){
"use strict";
var main = global.cloudcmd.main,
SRVDIR = main.SRVDIR;
/*
* Обьект для работы с кэшем
* аналог клиентского обьекта
* с тем отличием, что в нём
* будут храниться серверные
* данные, такие как файлы
* отдаваемые клиенту
* (файлы проэкта по большому
* счёту, для ускорения
* первичной загрузки)
*/
exports.Cache = {
/* приватный переключатель возможности работы с кэшем */
_allowed :true,
/* данные в которых храняться файлы
* в формате <поле> : <значение>
* _data[name]=pData;
* одному имени соответствуют
* одни данные
*/
_data :{},
/* функция говорит можно ли работать с кэшем */
isAllowed :(function(){
return this._allowed;
}),
/* функция устанавливает возможность работать с кэшем */
setAllowed :(function(pAllowed){
this._allowed=pAllowed;
}),
/* Если доступен кэш
* сохраняем в него данные
*/
set :(function(pName, pData){
if(this._allowed && pName && pData){
this._data[pName]=pData;
}
}),
/* Если доступен Cache принимаем из него данные*/
get :(function(pName){
if(this._allowed && pName){
return this._data[pName];
}
else return null;
}),
/* Функция очищает кэш*/
clear :(function(){
if(this._allowed){
this._data={};
}
})
};
})();

View file

@ -1,75 +0,0 @@
/* Модуль проверяет поменялось ли содержимое файла по хэшу. */
var fs = require('fs'),
crypto = require('crypto');
/* object contains hashes of files*/
var Hashes;
/*
* Function reads hash table of files
* checks is file changed or not
* and return result.
* @pFileName - name of file
* @pFileData - data of file
* result: boolean
*/
exports.check = function(pFileName, pFileData, pOverWrite_b){
var lReadedHash;
/* boolean hashes.json changed or not */
var lHashesChanged_b = false;
if(!Hashes)
try {
console.log('trying to read hashes.json');
Hashes = require(process.cwd() + '/hashes');
}catch(pError) {
console.log('hashes.json not found... \n');
Hashes = {};
}
for(var lFileName in Hashes)
/* if founded row with
* file name - save hash
*/
if (lFileName === pFileName) {
lReadedHash = Hashes[pFileName];
break;
}
/* create hash of file data */
var lFileHash = crypto.createHash('sha1');
lFileHash = crypto.createHash('sha1');
lFileHash.update(pFileData);
lFileHash = lFileHash.digest('hex');
if(lReadedHash !== lFileHash){
Hashes[pFileName] = lFileHash;
lHashesChanged_b = true;
}
if(pOverWrite_b){
/* if hashes file was changes - write it */
if(lHashesChanged_b)
fs.writeFile('./hashes.json',
JSON.stringify(Hashes),
fileWrited('./hashes.json'));
else console.log('no one file has been changed');
}
/* has file changed? */
return lHashesChanged_b;
};
/*
* Функция вызываеться после записи файла
* и выводит ошибку или сообщает,
* что файл успешно записан
*/
function fileWrited(pFileName){
"use strict";
return function(error){
console.log(error?error:('file '+pFileName+' writed...'));
};
}

208
lib/server/main.js Normal file
View file

@ -0,0 +1,208 @@
(function(){
"strict mode";
/* Global var accessible from any loaded module */
global.cloudcmd = {};
var DIR,
LIBDIR,
SRVDIR,
Util,
SLASH,
ISWIN32,
fs,
path,
zlib,
OK = 200,
ERROR = 404,
Extensions = {
'.css' : 'text/css',
'.js' : 'text/javascript',
'.png' : 'image/png',
'.json' : 'application/json',
'.html' : 'text/html',
'.woff' : 'font/woff',
'.appcache' : 'text/cache-manifest',
'.mp3' : 'audio/mpeg'
};
/* Native Modules*/
exports.crypto = require('crypto'),
exports.child_process = require('child_process'),
exports.fs = fs = require('fs'),
exports.http = require('http'),
exports.https = require('https'),
exports.path = path = require('path'),
exports.url = require('url'),
exports.querystring = require('querystring'),
/* Constants */
/* current dir + 2 levels up */
exports.WIN32 = ISWIN32 = isWin32();
exports.SLASH = SLASH = ISWIN32 ? '\\' : '/',
exports.SRVDIR = SRVDIR = __dirname + SLASH,
exports.LIBDIR = LIBDIR = path.normalize(SRVDIR + '../'),
exports.DIR = DIR = path.normalize(LIBDIR + '../'),
/* Functions */
exports.generateHeaders = generateHeaders,
exports.sendFile = sendFile,
exports.require = mrequire,
exports.librequire = librequire,
exports.srvrequire = srvrequire,
exports.rootrequire = rootrequire,
/* compitability with old versions of node */
exports.fs.exists = exports.fs.exists || exports.path.exists;
/* Needed Modules */
exports.util = Util = require(LIBDIR + 'util'),
exports.zlib = zlib = mrequire('zlib'),
/* Main Information */
exports.config = rootrequire('config');
exports.mainpackage = rootrequire('package');
/* Additional Modules */
/*
* Any of loaded below modules could work with global var so
* it should be initialized first. Becouse of almost any of
* moudles do not depends on each other all needed information
* for all modules is initialized hear.
*/
global.cloudcmd.main = exports;
exports.auth = srvrequire('auth').auth,
exports.appcache = srvrequire('appcache'),
exports.cache = srvrequire('cache').Cache,
exports.cloudfunc = librequire('cloudfunc'),
exports.rest = srvrequire('rest').api,
exports.socket = srvrequire('socket'),
exports.update = srvrequire('update'),
exports.minify = srvrequire('minify').Minify;
/*
* second initializing after all modules load, so global var is
* totally filled of all information that should know all modules
*/
global.cloudcmd.main = exports;
/**
* function do safe require of needed module
* @param {Strin} pSrc
*/
function mrequire(pSrc){
var lModule,
lError = Util.tryCatchLog(function(){
lModule = require(pSrc);
});
if(lError)
console.log(lError);
return lModule;
}
function rootrequire(pSrc){ return mrequire(DIR + pSrc); }
function librequire(pSrc){ return mrequire(LIBDIR + pSrc); }
function srvrequire(pSrc){ return mrequire(SRVDIR + pSrc); }
/**
* function check is current platform is win32
*/
function isWin32(){ return process.platform === 'win32'; }
/**
* Функция создаёт заголовки файлов
* в зависимости от расширения файла
* перед отправкой их клиенту
* @param pName - имя файла
* @param pGzip - данные сжаты gzip'ом
*/
function generateHeaders(pName, pGzip, pQuery){
var lType = '',
lCacheControl = 0,
lContentEncoding = '',
lRet,
lDot = pName.lastIndexOf('.'),
lExt = pName.substr(lDot);
if( Util.strCmp(lExt, '.appcache') )
lCacheControl = 1;
lType = Extensions[lExt] || 'text/plain';
if( !Util.isContainStr(lType, 'img') )
lContentEncoding = '; charset=UTF-8';
if(Util.strCmp(pQuery, 'download') )
lType = 'application/octet-stream';
if(!lCacheControl)
lCacheControl = 31337 * 21;
lRet = {
/* if type of file any, but img -
* then we shoud specify charset
*/
'Content-Type': lType + lContentEncoding,
'cache-control': 'max-age=' + lCacheControl,
'last-modified': new Date().toString(),
/* https://developers.google.com/speed/docs/best-practices
/caching?hl=ru#LeverageProxyCaching */
'Vary': 'Accept-Encoding'
};
if(pGzip)
lRet['content-encoding'] = 'gzip';
return lRet;
}
/**
* send file to client thru pipe
* and gzip it if client support
*
* @param pName - имя файла
* @param pGzip - данные сжаты gzip'ом
*/
function sendFile(pParams){
var lRet = false,
lName = pParams.name,
lReq = pParams.request,
lRes = pParams.response,
lEnc = lReq.headers['accept-encoding'] || '',
lGzip = lEnc.match(/\bgzip\b/),
lReadStream = fs.createReadStream(lName, {
'bufferSize': 4 * 1024
});
lReadStream.on('error', function(pError){
lRes.writeHead(ERROR, 'OK');
lRes.end(String(pError));
});
lRes.writeHead(OK, generateHeaders(lName, lGzip) );
if (lGzip)
lReadStream = lReadStream.pipe( zlib.createGzip() );
lReadStream.pipe(lRes);
return lRet;
}
})();

128
lib/server/minify.js Normal file
View file

@ -0,0 +1,128 @@
/* Обьект для сжатия скриптов и стилей по умолчанию - сжимаються */
(function(){
"use strict";
var main = global.cloudcmd.main,
DIR = main.DIR,
LIBDIR = main.LIBDIR;
exports.Minify = {
/* pathes to directories */
INDEX : DIR + 'index.html',
/* приватный переключатель минимизации */
_allowed :{
css : true,
js : true,
html : true,
img : true
},
/* minimize even if file not changed */
forse : false,
/* функция разрешает или
* запрещает минимизировать
* css/js/html
* @pAllowed: - структура, в которой
* передаються параметры
* минификации, вида
* {js:true,css:true,html:false; img:true}
* img отвечает за перевод картинок в base64
* и сохранение их в css-файл
*/
setAllowed :(function(pAllowed){
if(pAllowed){
this._allowed = pAllowed;
}
}),
/*
* Функция минимизирует css/js/html
* если установлены параметры минимизации
*/
doit :(function(){
var lMinify = main.require('minify');
if(!lMinify){
this._allowed = {js:false,css:false,html:false};
console.log('You coud install minify ' +
'for better download spead:\n' +
'npm i minify');
return this._allowed;
}
/*
* temporary changed dir path,
* becouse directory lib is write
* protected by others by default
* so if node process is started
* from other user (root for example
* in nodester) we can not write
* minified versions
*/
this.MinFolder = lMinify.MinFolder;
var lOptimizeParams = [],
lStyleCSS = DIR + 'css/style.css',
lResetCSS = DIR + 'css/reset.css';
if (this._allowed.js) {
lOptimizeParams.push(LIBDIR + 'client.js');
}
if (this._allowed.html)
lOptimizeParams.push(this.INDEX);
if (this._allowed.css) {
var lStyles = [];
lStyles[0] = {};
lStyles[0][lStyleCSS] = this._allowed.img;
lStyles[1] = {};
lStyles[1][lResetCSS] = this._allowed.img;
lOptimizeParams.push(lStyles[0]);
lOptimizeParams.push(lStyles[1]);
}
if (lOptimizeParams.length)
lMinify.optimize(lOptimizeParams);
this.Cache = lMinify.Cache;
return this._allowed;
}),
optimize: function(pName, pParams){
var lResult = true;
pParams.force = this.force;
if(this._allowed.css ||
this._allowed.js ||
this._allowed.html){
var lMinify = main.require('minify');
if(lMinify)
lMinify.optimize(pName, pParams);
else{
lResult = false;
this._allowed = {js:false,css:false,html:false};
console.log('Could not minify ' +
'without minify module\n' +
'npm i minify');
}
}
else lResult = false;
return lResult;
},
/* minification folder name */
MinFolder : '',
Cache : {}
};
})();

View file

@ -1,203 +0,0 @@
/* Module contains Cloud Commander SS(Server Side) objects.
*
* List of objects:
* - Cache
* - Minify
*/
/*
* Обьект для работы с кэшем
* аналог клиентского обьекта
* с тем отличием, что в нём
* будут храниться серверные
* данные, такие как файлы
* отдаваемые клиенту
* (файлы проэкта по большому
* счёту, для ускорения
* первичной загрузки)
*/
exports.Cache = {
/* приватный переключатель возможности работы с кэшем */
_allowed :true,
/* данные в которых храняться файлы
* в формате <поле> : <значение>
* _data[name]=pData;
* одному имени соответствуют
* одни данные
*/
_data :{},
/* функция говорит можно ли работать с кэшем */
isAllowed :(function(){
return this._allowed;
}),
/* функция устанавливает возможность работать с кэшем */
setAllowed :(function(pAllowed){
this._allowed=pAllowed;
}),
/* Если доступен кэш
* сохраняем в него данные
*/
set :(function(pName, pData){
if(this._allowed && pName && pData){
this._data[pName]=pData;
}
}),
/* Если доступен Cache принимаем из него данные*/
get :(function(pName){
if(this._allowed && pName){
return this._data[pName];
}
else return null;
}),
/* Функция очищает кэш*/
clear :(function(){
if(this._allowed){
this._data={};
}
})
};
/* Обьект для сжатия скриптов и стилей
* по умолчанию - сжимаються
*/
exports.Minify = {
/* pathes to directories */
INDEX :'index.html',
/* приватный переключатель минимизации */
_allowed :{
css : true,
js : true,
html : true,
img : true
},
/* minimize even if file not changed */
forse : false,
/* функция разрешает или
* запрещает минимизировать
* css/js/html
* @pAllowed: - структура, в которой
* передаються параметры
* минификации, вида
* {js:true,css:true,html:false; img:true}
* img отвечает за перевод картинок в base64
* и сохранение их в css-файл
*/
setAllowed :(function(pAllowed){
if(pAllowed){
this._allowed = pAllowed;
}
}),
/*
* Функция минимизирует css/js/html
* если установлены параметры минимизации
*/
doit :(function(){
if(this._allowed.css ||
this._allowed.js ||
this._allowed.html){
var lMinify;
try{
lMinify = require('minify');
}catch(pError){
this._allowed = {js:false,css:false,html:false};
console.log('You coud install minify ' +
'for better download spead:\n' +
'npm i minify');
return this._allowed;
}
/*
* temporary changed dir path,
* becouse directory lib is write
* protected by others by default
* so if node process is started
* from other user (root for example
* in nodester) we can not write
* minified versions
*/
this.MinFolder = '/' + lMinify.MinFolder;
var lOptimizeParams = [];
if (this._allowed.js) {
lOptimizeParams.push('client.js');
}
if (this._allowed.html)
lOptimizeParams.push(this.INDEX);
if (this._allowed.css) {
lOptimizeParams.push({
'./css/style.css' : this._allowed.img
});
lOptimizeParams.push({
'./css/reset.css': this._allowed.img
});
}
if (lOptimizeParams)
lMinify.optimize(lOptimizeParams);
this.Cache = lMinify.Cache;
return this._allowed;
}
}),
optimize: function(pName, pParams){
var lResult = true;
pParams.force = this.force;
if(this._allowed.css ||
this._allowed.js ||
this._allowed.html){
var lMinify = cloudRequire('minify');
if(lMinify)
lMinify.optimize(pName, pParams);
else{
lResult = false;
this._allowed = {js:false,css:false,html:false};
console.log('Could not minify ' +
'without minify module\n' +
'npm i minify');
}
}
else lResult = false;
return lResult;
},
/* minification folder name */
MinFolder : '',
Cache : {}
};
/* Обьект проверяет изменился ли файл */
exports.IsFileChanged = function(pFileName, pData, pOverWrite_b){
var lCheck = cloudRequire('is-file-changed');
if(lCheck){
return lCheck.IsFileChanged(pFileName, pData, pOverWrite_b);
}else
return true;
};
/* function do safe require of needed module */
function cloudRequire(pModule){
try{
return require(pModule);
}
catch(pError){
return false;
}
}

204
lib/server/rest.js Normal file
View file

@ -0,0 +1,204 @@
/* RESTfull module */
(function(){
"use strict";
if(!global.cloudcmd)
return console.log(
'# rest.js' + '\n' +
'# -----------' + '\n' +
'# Module is part of Cloud Commander,' + '\n' +
'# used for work with REST API.' + '\n' +
'# If you wont to see at work set rest: true' + '\n' +
'# and api_url in config.json' + '\n' +
'# http://github.com/coderaiser/cloudcmd' + '\n');
var main = global.cloudcmd.main,
Util = main.util,
Config = main.config,
APIURL = Config.api_url,
OK = 200,
Header = main.generateHeaders('api.json', false);
/**
* rest interface
* @pConnectionData {request, responce}
*/
exports.api = function(pConnectionData){
var lRet = false,
lReq = pConnectionData.request,
lRes = pConnectionData.response,
lUrl = lReq.url,
lMethod = lReq.method;
if( Util.isContainStr(lUrl, APIURL) ){
lRet = true;
getBody(lReq, function(pBody){
var lCommand = Util.removeStr(lUrl, APIURL),
lData = getData({
command : lCommand,
method : lMethod,
body : pBody,
request : lReq,
response : lRes
});
if(lData)
send({
response : lRes,
data : lData
});
});
}
return lRet;
};
/**
* send data
*
* @param pRes
* @param pData
*/
function send(pParams){
var lRes = pParams.response,
lData = pParams.data;
lRes.writeHead(OK, Header);
lRes.end( JSON.stringify(lData) );
}
/**
* getting data on method and command
*
* @param pParams {command, method, body, requrest, response}
*/
function getData(pParams){
var lResult,
lCmd = pParams.command,
lMethod = pParams.method;
if(lCmd[0] === '/'){
lCmd = Util.removeStr(lCmd, '/');
pParams.command = lCmd;
}
switch(lMethod){
case 'GET':
lResult = onGET(pParams);
break;
case 'PUT':
lResult = onPUT(pParams);
break;
}
return lResult;
}
/**
* process data on GET request
*
* @param pParams {command, method, body, requrest, response}
*/
function onGET(pParams){
var lResult = {error: 'command not found'},
lCmd = pParams.command,
lConfig = main.config,
lEnv = process.env,
lEnvKey,
lConfigKey;
switch(lCmd){
case '':
lResult = {info: 'Cloud Commander API v1'};
break;
case 'github_key':
lEnvKey = lEnv.github_key;
lConfigKey = lConfig.github_key,
lResult = lEnvKey || lConfigKey;
break;
case 'dropbox_chooser_key':
lEnvKey = lEnv.dropbox_chooser_key;
lConfigKey = lConfig.dropbox_chooser_key;
lResult = lEnvKey || lConfigKey;
break;
case 'kill':
pParams.data = {
mesage: 'Cloud Commander was killed'
};
send(pParams);
lResult = null;
break;
}
return lResult;
}
/**
* process data on PUT request
*
* @param pParams {command, method, body, requrest, response}
*/
function onPUT(pParams){
var lResult = {error: 'command not found'},
lCmd = pParams.command,
lBody = pParams.body,
lRes = pParams.response;
switch(lCmd){
case 'auth':
main.auth(lBody, function(pTocken){
send({
response: lRes,
data: pTocken
});
});
lResult = false;
break;
case 'read':
console.log(lBody);
var lFiles = lBody;
if( Util.isString(lFiles) ){
pParams.name = lFiles;
main.sendFile(pParams);
lResult = null;
}
break;
}
return lResult;
}
/**
* get body of url query
*
* @param pReq
* @param pCallBack
*/
function getBody(pReq, pCallBack){
var lBody = '';
pReq.on('data', function(chunk) {
lBody += chunk.toString();
});
pReq.on('end', function() {
Util.exec(pCallBack, lBody);
});
}
})();

View file

@ -1,78 +1,141 @@
/* module make possible connectoin thrue socket.io on a server */
var io = require('socket.io'),
exec = require('child_process').exec,
ClientFuncs = [],
OnMessageFuncs = [],
Win32_b = process.platform === 'win32';
/**
* Function listen on servers port
* @pServer {Object} started server object
*/
exports.listen = function(pServer){
io = io.listen(pServer);
(function(){
"use strict";
/* number of connections */
var lConnNum = 0;
io.sockets.on('connection', function (socket) {
++lConnNum;
socket.send('{"stdout":"client connected"}');
var main = global.cloudcmd.main,
SRVDIR = main.SRVDIR,
console.log('server connected');
io = main.require('socket.io'),
update = main.srvrequire('update'),
exec = main.child_process.exec,
Util = main.util,
if(!OnMessageFuncs[lConnNum])
OnMessageFuncs[lConnNum] = onMessage(lConnNum, socket);
var lConn_func = OnMessageFuncs[lConnNum];
socket.on('message', lConn_func);
});
};
/**
* function gets onMessage function
* that execute needed command
*
* @param pConnNum, pSocket
*/
function onMessage(pConnNum, pSocket){
return function(pCommand) {
console.log(pCommand);
/* change code page to unicode */
if(Win32_b)
pCommand = 'chcp 65001 |' + pCommand;
if(!ClientFuncs[pConnNum])
ClientFuncs[pConnNum] = getExec(pSocket);
var lExec_func = ClientFuncs[pConnNum];
exec(pCommand, lExec_func);
};
}
/**
* function send result of command to client
* @param pSocket
*/
function getExec(pSocket){
return function(pError, pStdout, pStderr) {
if (pError !== null) {
console.log('exec error: ' + pError);
}
ClientFuncs = [],
OnMessageFuncs = [],
Win32_b = main.WIN32;
var lExec = {
stdout : pStdout,
stderr : pStderr || pError
};
/**
* function listen on servers port
* @pServer {Object} started server object
*/
exports.listen = function(pServer){
io = io.listen(pServer);
var lExec_str = JSON.stringify(lExec);
pSocket.send(lExec_str);
console.log(lExec);
/* number of connections */
var lConnNum = 0;
io.sockets.on('connection', function (socket){
++lConnNum;
socket.send('{"stdout":"client connected"}');
console.log('server connected');
if(!OnMessageFuncs[lConnNum])
OnMessageFuncs[lConnNum] = onMessage(lConnNum, socket);
var lConn_func = OnMessageFuncs[lConnNum];
socket.on('message', lConn_func);
});
};
}
/**
* function gets onMessage function
* that execute needed command
*
* @param pConnNum, pSocket
*/
function onMessage(pConnNum, pSocket){
return function(pCommand) {
console.log(pCommand);
if( Util.isContainStr(pCommand, 'cloudcmd') ){
pCommand = Util.removeStr(pCommand, 'cloudcmd');
if( Util.isContainStr(pCommand, ' ') ){
pCommand = Util.removeStr(pCommand, ' ');
if( Util.isContainStr(pCommand, 'update') && update )
update.get();
if( Util.strCmp(pCommand, 'exit') )
if(main.WIN32)
pCommand = 'taskkill -f /PID ' + process.pid;
else
pCommand = 'kill -9 ' + process.pid;
}
else {
var lMsg = {
stdout : 'cloudcmd exit - for shutdown cloudcmd',
stderr : null
};
lMsg = JSON.stringify(lMsg);
pSocket.send(lMsg);
console.log('received from client: ' + pCommand);
console.log('sended to client: ' + lMsg);
return;
}
}
/* if we on windows and command is build in
* change code page to unicode becouse
* windows use unicode on non English versions
*/
if(Win32_b){
var lWinCommand = pCommand.toUpperCase();
if( Win32Commands.indexOf(lWinCommand) > 0 )
pCommand = 'chcp 65001 |' + pCommand;
}
if(!ClientFuncs[pConnNum])
ClientFuncs[pConnNum] = getExec(pSocket);
var lExec_func = ClientFuncs[pConnNum];
exec(pCommand, lExec_func);
};
}
/**
* function send result of command to client
* @param pSocket
*/
function getExec(pSocket){
return function(pError, pStdout, pStderr) {
if (pError !== null) {
console.log('exec error: ' + pError);
}
var lExec = {
stdout : pStdout,
stderr : pStderr || pError
},
lExec_str = JSON.stringify(lExec);
pSocket.send(lExec_str);
};
}
/* windows commands thet require
* unicode charset on locales
* different then English
*/
var Win32Commands = ['ASSOC', 'AT', 'ATTRIB', 'BREAK', 'CACLS', 'CALL',
'CD', 'CHCP', 'CHDIR', 'CHKDSK', 'CHKNTFS', 'CLS',
'CMD', 'COLOR', 'COMP', 'COMPACT', 'CONVERT', 'COPY',
'DATE', 'DEL', 'DIR', 'DISKCOMP', 'DISKCOPY', 'DOSKEY',
'ECHO', 'ENDLOCAL', 'ERASE', 'EXIT', 'FC', 'FIND',
'FINDSTR', 'FOR', 'FORMAT', 'FTYPE', 'GOTO', 'GRAFTABL',
'HELP', 'IF', 'LABEL', 'MD', 'MKDIR', 'MODE', 'MORE',
'MOVE', 'PATH', 'PAUSE', 'POPD', 'PRINT', 'PROMPT',
'PUSHD', 'RD', 'RECOVER', 'REM', 'REN', 'RENAME',
'REPLACE', 'RMDIR', 'SET', 'SETLOCAL', 'SHIFT', 'SORT',
'START', 'SUBST', 'TIME', 'TITLE', 'TREE', 'TYPE',
'VER', 'VERIFY', 'VOL', 'XCOPY'];
})();

64
lib/server/update.js Normal file
View file

@ -0,0 +1,64 @@
/* module update cloud commander */
(function(){
"use strict";
if(!global.cloudcmd)
return console.log(
'# update.js' + '\n' +
'# -----------' + '\n' +
'# Module is part of Cloud Commander,' + '\n' +
'# used for work update thru git.' + '\n' +
'# If you wont to see at work install git' + '\n' +
'# http://github.com/coderaiser/cloudcmd' + '\n');
var main = global.cloudcmd.main,
mainpackage = main.mainpackage,
exec = main.child_process.exec,
Util = main.util,
DIR = main.DIR;
exports.get = function(){
var lPull = function(){
exec('git pull', pull);
};
if( process.cwd === DIR )
lPull();
else
exec('cd ' + DIR, lPull);
};
/**
* function pulls cloud cmd content from repo
* @param pError
* @param pStdout
* @param pStderr
*/
function pull(pError, pStdout, pStderr){
var lExec;
if(!pError){
pStderr = '';
if(pStdout !== 'Already up-to-date.\n'){
pStdout = 'Cloud Commander updated. Restart to use new version.';
}
else pStdout = 'Cloud Commander is up to date.';
console.log(DIR);
if(mainpackage)
pStdout = 'Version ' + mainpackage.version + '\n' + pStdout;
lExec = {
stdout : pStdout,
stderr : pStderr || pError
};
}else
lExec = 'install git to get auto updates (works for cloned version)\n' +
'git clone http://github.com/coderaiser/cloudcmd';
Util.log(lExec);
}
})();

401
lib/util.js Normal file
View file

@ -0,0 +1,401 @@
/*
* Licensed under MIT License http://www.opensource.org/licenses/mit-license
* Module contain additional system functional
*/
var Util, exports;
(function(){
"use strict";
Util = exports || {};
var Scope = exports ? global : window;
/** setting function context
* @param {function} pFunction
* @param {object} pContext
*/
Util.bind = function(pFunction, pContext){
var lRet = false;
if( Util.isFunction(pFunction) )
lRet = pFunction.bind(pContext);
return lRet;
};
Util.breakpoint = function(){
var lRet = Util.tryCatch(function(){
debugger;
});
Util.log(lRet);
return lRet;
};
/**
* Функция ищет в имени файла расширение
* и если находит возвращает true
* @param pName - получает имя файла
* @param pExt - расширение
*/
Util.checkExtension = function(pName, pExt){
var lRet = false,
lLength = pName.length; /* длина имени*/
/* если длина имени больше
* длинны расширения -
* имеет смысл продолжать
*/
if (typeof pExt === 'string' && pName.length > pExt.length) {
var lExtNum = pName.lastIndexOf(pExt), /* последнее вхождение расширения*/
lExtSub = lLength - lExtNum; /* длина расширения*/
/* если pExt - расширение pName */
lRet = lExtSub === pExt.length;
}else if(typeof pExt === 'object' && pExt.length){
for(var i=0; i < pName.length; i++){
lRet = Util.checkExtension(pName, pExt[i]);
if(lRet)
break;
}
}
return lRet;
};
/* STRINGS */
/**
* function check is strings are equal
* @param pStr1
* @param pStr2
*/
Util.strCmp = function (pStr1, pStr2){
return this.isContainStr(pStr1, pStr2) &&
pStr1.length === pStr2.length;
};
/**
* function returns is pStr1 contains pStr2
* @param pStr1
* @param pStr2
*/
Util.isContainStr = function(pStr1, pStr2){
return pStr1 &&
pStr2 &&
pStr1.indexOf(pStr2) >= 0;
};
/**
* function log pArg if it's not empty
* @param pArg
*/
Util.log = function(pArg){
var lRet = pArg,
lConsole = Scope.console;
if(lConsole && pArg)
lConsole.log(pArg);
return lRet;
};
/**
* load functions thrue callbacks one-by-one
* @param pFunc_a {Array} - array of functions
* @param pData - not necessarily
*/
Util.loadOnLoad = function(pFunc_a, pData){
if( Util.isArray(pFunc_a) && pFunc_a.length) {
var lFunc_a = pFunc_a.slice(),
lFunc = lFunc_a.pop(),
lCallBack = function(pData){
return Util.loadOnLoad(lFunc_a, pData);
};
if( !Util.isUndefined(pData) )
pData = {
data : pData,
callback : lCallBack
};
Util.exec(lFunc , pData || lCallBack);
}
};
/**
* function remove substring from string
* @param pStr
* @param pSubStr
*/
Util.removeStr = function(pStr, pSubStr){
return pStr.replace(pSubStr,'');
};
/**
* invoke a couple of functions in paralel
*
* @param {Array} pFuncs
* @param {function} pCallback
*
* Example:
* i >=0, pFuncs[i] = function(param, callback){}
*/
Util.paralelExec = function(pFuncs, pCallback){
var done = [];
/* add items to array done*/
function addFunc(pNum){
done.push(pNum);
}
/*
* improve callback of funcs so
* we pop number of function and
* if it's last we call pCallBack
*/
function doneFunc(pParams){
Util.exec(pParams.callback);
var lNum = done.pop (pParams.number);
if(!lNum){
Util.exec(pCallback);
}
}
for(var i = 0, n = pFuncs.length; i < n; i++){
addFunc(i);
var lFunc = pFuncs[i].callback;
pFuncs[i].callback = Util.retExec(doneFunc, {
number : i,
callback : lFunc
});
}
};
/**
* functions check is pVarible is array
* @param pVarible
*/
Util.isArray = function(pVarible){
return pVarible instanceof Array;
};
/**
* functions check is pVarible is boolean
* @param pVarible
*/
Util.isBoolean = function(pVarible){
return Util.isType(pVarible, 'boolean');
};
/**
* functions check is pVarible is function
* @param pVarible
*/
Util.isFunction = function(pVarible){
return Util.isType(pVarible, 'function');
};
/**
* functions check is pVarible is object
* @param pVarible
*/
Util.isObject = function(pVarible){
return Util.isType(pVarible, 'object');
};
/**
* functions check is pVarible is string
* @param pVarible
*/
Util.isString = function(pVarible){
return Util.isType(pVarible, 'string');
};
/**
* functions check is pVarible is string
* @param pVarible
*/
Util.isUndefined = function(pVarible){
return Util.isType(pVarible, 'undefined');
};
/**
* functions check is pVarible is pType
* @param pVarible
* @param pType
*/
Util.isType = function(pVarible, pType){
return typeof pVarible === pType;
};
/**
* return save exec function
* @param pCallBack
* @param pArg
*/
Util.retExec = function(pCallBack, pArg){
return function(){
Util.exec(pCallBack, pArg);
};
};
/**
* return function wich exec function in params
* @param pCallBack
* @param pArg
*/
Util.retFunc = function(pCallBack, pArg){
return function(){
return Util.exec(pCallBack, pArg);
};
};
/**
* return load functions thrue callbacks one-by-one
* @param pFunc_a {Array} - array of functions
* @param pData - not necessarily
*/
Util.retLoadOnLoad = function(pFunc_a, pData){
return function(){
Util.loadOnLoad(pFunc_a, pData);
};
};
/**
* set value to property of object, if object exist
* @param pArgs {object, property, value}
*/
Util.setValue = function(pArgs){
var lRet = false;
if( Util.isObject(pArgs) ){
var lObj = pArgs.object,
lProp = pArgs.property,
lVal = pArgs.lVal;
if(lObj){
lObj[lProp] = lVal;
lRet = true;
}
}
return lRet;
};
/**
* function execute param function in
* try...catch block
*
* @param pCallBack
*/
Util.tryCatch = function(pCallBack){
var lRet;
try{
lRet = pCallBack();
}
catch(pError){
lRet = pError;
}
return lRet;
};
/**
* function execute param function in
* try...catch block and log result
*
* @param pTryFunc
*/
Util.tryCatchDebug = function(pTryFunc){
var lRet = Util.tryCatch(pTryFunc);
if(lRet)
Util.debug();
return lRet;
};
/**
* function execute param function in
* try...catch block and log result
*
* @param pTryFunc
*/
Util.tryCatchLog = function(pTryFunc){
var lRet;
lRet = Util.tryCatch(pTryFunc);
return Util.log(lRet);
};
/**
* function execute param function in
* try...catch block and log result
*
* @param pCallBack
*/
Util.tryCatchCall = function(pTryFunc, pCallBack){
var lRet;
lRet = Util.tryCatch(pTryFunc);
if(lRet)
Util.exec(pCallBack, lRet);
return lRet;
};
/**
* function do save exec of function
* @param pCallBack
* @param pArg
*/
Util.exec = function(pCallBack, pArg){
var lRet = false;
if( Util.isFunction(pCallBack) )
lRet = pCallBack(pArg);
return lRet;
};
/**
* Gets current time in format hh:mm:ss
*/
Util.getTime = function(){
var lRet,
date = new Date(),
hours = date.getHours(),
minutes = date.getMinutes(),
seconds = date.getSeconds();
minutes = minutes < 10 ? '0' + minutes : minutes;
seconds = seconds < 10 ? '0' + seconds : seconds;
lRet = hours + ":" + minutes + ":" + seconds;
return lRet;
};
/**
* Gets current date in format yy.mm.dd hh:mm:ss
*/
Util.getDate = function(){
var date = new Date(),
day = date.getDate(),
month = date.getMonth() + 1,
year = date.getFullYear(),
lRet = year + "-" + month + "-" + day + " " + Util.getTime();
return lRet;
};
})();

8
modules.json Normal file
View file

@ -0,0 +1,8 @@
[
"editor/_codemirror",
"menu",
"viewer",
"storage/_github",
"storage/_dropbox",
"terminal"
]

826
server.js
View file

@ -1,826 +0,0 @@
"use strict";
/* Обьект содержащий все функции и переменные
* серверной части Cloud Commander'а
*/
var CloudServer = {
/* main Cloud Commander configuration
* readed from config.json if it's
* exist
*/
Config : {
cache : {
allowed : true
},
appcache : false,
minification : {
js : false,
css : false,
html : true,
img : false
},
server : true,
logs : false,
port : 31337,
ip : '127.0.0.1'
},
/* функция, которая генерирует заголовки
* файлов, отправляемые сервером клиенту
*/
generateHeaders : function () {},
/* функция высылает
* данные клиенту
*/
sendResponse : function () {},
/* Обьект для работы с кэшем */
Cache : {},
/* Обьект через который
* выполняеться сжатие
* скриптов и стилей
*/
Minify : {},
/* Асоциативный масив обьектов для
* работы с ответами сервера
* высылаемыми на запрос о файле и
* хранащий информацию в виде
* Responses[name]=responce;
*/
Responses : {},
/*
* Асоциативный масив статусов
* ответов сервера
* высылаемыми на запрос о файле и
* хранащий информацию в виде
* Statuses[name] = 404;
*/
Statuses : {},
/*
* queries of file params
* example: ?download
*/
Queries : {},
/* ПЕРЕМЕННЫЕ
* Поддержка браузером JS */
NoJS : true,
/* Поддержка gzip-сжатия браузером */
Gzip : undefined,
/* server varible */
Server :{},
/* КОНСТАНТЫ */
INDEX : 'index.html',
LIBDIR : './lib',
LIBDIRSERVER : './lib/server',
Extensions :{
'.css' : 'text/css',
'.js' : 'text/javascript',
'.png' : 'image/png',
'.json' : 'application/json',
'.html' : 'text/html',
'.woff' : 'font/woff',
'.appcache' : 'text/cache-manifest',
'.mp3' : 'audio/mpeg'
}
};
var DirPath = '/';
/* модуль для работы с путями*/
var Path = cloudRequire('path'),
Fs = cloudRequire('fs'), /* модуль для работы с файловой системой*/
Querystring = cloudRequire('querystring');
/* node v0.4 not contains zlib */
var Zlib = cloudRequire('zlib'); /* модуль для сжатия данных gzip-ом*/
if(!Zlib)
console.log('to use gzip-commpression' +
'you should use newer node version\n');
/* добавляем модуль с функциями */
var CloudFunc = cloudRequire(CloudServer.LIBDIR +
'/cloudfunc');
CloudServer.AppCache = cloudRequire(CloudServer.LIBDIRSERVER +
'/appcache');
CloudServer.Socket = cloudRequire(CloudServer.LIBDIRSERVER +
'/socket');
CloudServer.Obj = cloudRequire(CloudServer.LIBDIRSERVER +
'/object');
if(CloudServer.Obj){
CloudServer.Cache = CloudServer.Obj.Cache;
CloudServer.Minify = CloudServer.Obj.Minify;
}
else
console.log('could not found one of Cloud Commander SS files');
/* базовая инициализация */
CloudServer.init = (function(){
/* Переменная в которой храниться кэш*/
this.Cache.setAllowed(CloudServer.Config.cache.allowed);
/* Change default parameters of
* js/css/html minification
*/
this.Minify.setAllowed(CloudServer.Config.minification);
/* Если нужно минимизируем скрипты */
this.Minify._allowed = this.Minify.doit();
var lAppCache = CloudServer.AppCache;
/* создаём файл app cache */
if(this.Config.appcache && lAppCache && this.Config.server){
var lFiles = [{'//themes.googleusercontent.com/static/fonts/droidsansmono/v4/ns-m2xQYezAtqh7ai59hJUYuTAAIFFn5GTWtryCmBQ4.woff' : './font/DroidSansMono.woff'},
{'//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js' : './lib/client/jquery.js'}];
if(this.Minify._allowed.css)
lFiles.push('./min/all.min.css');
lAppCache.addFiles(lFiles);
lAppCache.createManifest();
}
});
/**
* Функция создаёт сервер
* @param pConfig
*/
CloudServer.start = function (pConfig) {
if(pConfig)
this.Config = pConfig;
else
console.log('warning: configuretion file config.json not found...\n' +
'using default values...\n' +
JSON.stringify(this.Config));
this.init();
this.Port = process.env.PORT || /* c9 */
process.env.app_port || /* nodester */
process.env.VCAP_APP_PORT || /* cloudfoundry */
this.Config.port;
this.IP = process.env.IP || /* c9 */
this.Config.ip;
/* if Cloud Server started on jitsu */
if(process.env.HOME &&
!process.env.HOME.indexOf('/opt/haibu')) {
this.IP = '0.0.0.0';
}
/* server mode or testing mode */
if (!process.argv[2] && this.Config.server) {
var http = require('http');
try {
this.Server = http.createServer(this._controller);
this.Server.listen(this.Port, this.IP);
if(CloudServer.Socket)
CloudServer.Socket.listen(this.Server);
console.log('Cloud Commander server running at http://' +
this.IP + ':' + this.Port);
}catch(pError){
console.log('Cloud Commander server could not started');
console.log(pError);
}
}else{
console.log('Cloud Commander testing mode');
}
};
/**
* Функция создаёт заголовки файлов
* в зависимости от расширения файла
* перед отправкой их клиенту
* @param pName - имя файла
* @param pGzip - данные сжаты gzip'ом
*/
CloudServer.generateHeaders = function(pName, pGzip){
var lType = '';
var lCacheControl = 0;
var lContentEncoding = '';
/* высылаем заголовок в зависимости от типа файла */
var lDot = pName.lastIndexOf('.');
var lExt = pName.substr(lDot);
if(lExt === '.appcache')
lCacheControl = 1;
lType = CloudServer.Extensions[lExt] || 'text/plain';
if(lType.indexOf('img') < 0)
lContentEncoding = '; charset=UTF-8';
var lQuery = CloudServer.Queries[pName];
if(lQuery){
if(lQuery === 'download')
lType = 'application/octet-stream';
console.log(pName + lQuery);
}
if(!lCacheControl)
lCacheControl = 31337 * 21;
return {
/* if type of file any, but img -
* then we shoud specify charset
*/
'Content-Type': lType + lContentEncoding,
'cache-control': 'max-age=' + lCacheControl,
'last-modified': new Date().toString(),
'content-encoding': pGzip?'gzip':'',
/* https://developers.google.com/speed/docs/best-practices
/caching?hl=ru#LeverageProxyCaching */
'Vary': 'Accept-Encoding'
};
};
/**
* Главная функция, через которую проихсодит
* взаимодействие, обмен данными с клиентом
* @param req - запрос клиента (Request)
* @param res - ответ сервера (Response)
*/
CloudServer._controller = function(pReq, pRes)
{
/* Читаем содержимое папки, переданное в url */
var url = require("url");
var lParsedUrl = url.parse(pReq.url);
var pathname = lParsedUrl.pathname;
/* varible contain one of queris:
* download - change content-type for
* make downloading process
* from client js
* json - /no-js/ will be removed, and
* if we will wont get directory
* content wi will set json
* query like this
* ?json
*/
var lQuery = lParsedUrl.query;
if(lQuery)
console.log('query = ' + lQuery);
/* added supporting of Russian language in directory names */
pathname = Querystring.unescape(pathname);
console.log('pathname: ' + pathname);
/* получаем поддерживаемые браузером кодировки*/
var lAcceptEncoding = pReq.headers['accept-encoding'];
/* запоминаем поддерживает ли браузер
* gzip-сжатие при каждом обращении к серверу
* и доступен ли нам модуль zlib
*/
if (lAcceptEncoding &&
lAcceptEncoding.match(/\bgzip\b/) &&
Zlib){
CloudServer.Gzip = true;
}
/* путь в ссылке, который говорит
* что js отключен
*/
var lNoJS_s = CloudFunc.NOJS;
var lFS_s = CloudFunc.FS;
console.log("request for " + pathname + " received...");
/* если в пути нет информации ни о ФС,
* ни об отсутствии js,
* ни о том, что это корневой
* каталог - загружаем файлы проэкта
*/
if(pathname.indexOf(lFS_s) < 0 &&
pathname.indexOf(lNoJS_s) < 0 &&
pathname!=='/' &&
lQuery !=='json'){
/* если имена файлов проекта - загружаем их*/
/* убираем слеш и читаем файл с текущец директории*/
/* добавляем текующий каталог к пути */
var lName = '.' + pathname;
console.log('reading '+lName);
/* watching is file changed */
if(CloudServer.Config.appcache)
CloudServer.AppCache.watch(lName);
/* сохраняем указатель на response и имя */
CloudServer.Responses[lName] = pRes;
/* saving status OK for current file */
CloudServer.Statuses[lName] = 200;
/* Берём значение из кэша
* сжатый файл - если gzip-поддерживаеться браузером
* не сжатый - в обратном случае
*/
var lFileData=CloudServer.Cache.get(
CloudServer.Gzip?(lName+'_gzip'):lName);
console.log(Path.basename(lName));
var lMinify = CloudServer.Minify;
/* object thet contains information
* about the source of file data
*/
var lFromCache_o = {'cache': true};
/* if cache is empty and Cache allowed and Minify_allowed
* and in Minifys cache is files, so save it to
* CloudServer cache
*/
if(!lFileData &&
lMinify._allowed){
console.log('trying to read data from Minify.Cache');
lFromCache_o.cache = false;
lFileData = CloudServer.Minify.Cache[
Path.basename(lName)];
}
var lReadFileFunc_f = CloudServer.getReadFileFunc(lName);
/* если там что-то есть передаём данные в функцию
* readFile
*/
var lResult = true;
if(lFileData){
/* if file readed not from cache -
* he readed from minified cache
*/
if(lFromCache_o.cache === false)
lFromCache_o.minify = true;
else
lFromCache_o.minify = false;
console.log(lName + ' readed from cache');
/* передаём данные с кэша,
* если gzip включен - сжатые
* в обратном случае - несжатые
*/
lReadFileFunc_f(undefined, lFileData, lFromCache_o);
}
/* if file not in one of caches
* and minimizing setted then minimize it
*/
else if(lName.indexOf('min') < 0 &&
CloudServer.Minify){
var lMin_o = CloudServer.Config.minification;
var lCheck_f = function(pExt){
return CloudFunc.checkExtension(lName,pExt);
};
var isAllowd_b = (lCheck_f('js') && lMin_o.js) ||
(lCheck_f('css') && lMin_o.css) ||
(lCheck_f('html') && lMin_o.html);
if(isAllowd_b){
lResult = CloudServer.Minify.optimize(lName, {
cache: true,
callback: function(pFileData){
lReadFileFunc_f(undefined, pFileData, false);
}
});
}
else
lResult = false;
} else
lResult = false;
if(!lResult)
Fs.readFile(lName, lReadFileFunc_f);
}else{/* если мы имеем дело с файловой системой*/
/* если путь не начинаеться с no-js - значит
* js включен
*/
/* убираем пометку cloud, без которой c9.io
* не работает поскольку путь из двух слешей
* (/fs/no-js/) - очень короткий, нужно
* длиннее
*/
if(pathname.indexOf(lNoJS_s) !== lFS_s.length &&
pathname !== '/'){
CloudServer.NoJS = false;
}else pathname = pathname.replace(lNoJS_s,'');
/* убираем индекс файловой системы */
if(pathname.indexOf(lFS_s) === 0){
pathname = pathname.replace(lFS_s,'');
/* if query json setted up
* load json data, no-js false.
*/
if(lQuery === 'json'){
CloudServer.NoJS = false;
}
/* если посетитель только зашел на сайт
* no-js будет пустым, как и fs.
* Если в пути нету fs - посетитель только зашел на сайт
* загружаем его полностью.
*/
} else CloudServer.NoJS = true;
/* если в итоге путь пустой
* делаем его корневым
*/
if (pathname === '')
pathname = '/';
DirPath = pathname;
CloudServer.Responses[DirPath] = pRes;
CloudServer.Statuses[DirPath] = 200;
/* saving query of current file */
CloudServer.Queries[DirPath] = lQuery;
/* Проверяем с папкой ли мы имеем дело */
/* читаем основные данные о файле */
Fs.stat(DirPath, CloudServer._stated);
/* если установлено сжатие
* меняем название html-файла и
* загружаем сжатый html-файл в дальнейшем
*/
CloudServer.INDEX = (CloudServer.Minify._allowed.html ?
'.' + CloudServer.Minify.MinFolder + 'index.min.html'
: CloudServer.INDEX);
/*
* сохраним указатель на response
* и на статус ответа
*/
CloudServer.Responses[CloudServer.INDEX] = pRes;
CloudServer.Statuses [CloudServer.INDEX] = 200;
}
};
/**
* Function geted stat information about file
* @param pError
* @param pStat
*/
CloudServer._stated = function(pError, pStat){
if(pError){
CloudServer.Statuses[DirPath] = 404;
CloudServer.sendResponse('OK',pError.toString(), DirPath);
return;
}
/*
* если это каталог -
* читаем его содержимое
*/
if(pStat){
if(pStat.isDirectory())
Fs.readdir(DirPath, CloudServer._readDir);
/* отдаём файл */
else if(pStat.isFile()){
Fs.readFile(DirPath, CloudServer.getReadFileFunc(DirPath));
console.log('reading file: '+DirPath);
}
}
};
/**
* Функция читает ссылку или выводит информацию об ошибке
* @param pError
* @param pFiles
*/
CloudServer._readDir = function (pError, pFiles)
{
if(pError){
console.log(pError);
CloudServer.Statuses[DirPath] = 404;
CloudServer.sendResponse('OK',pError.toString(),
DirPath);
return;
}
/* Если мы не в корне добавляем слеш к будующим ссылкам */
if(DirPath !== '/')
{
DirPath += '/';
}
pFiles = pFiles.sort();
var lCount = 0;
var lStats = {};
/* asyn getting file states
* and putting it to lStats object
*/
var getFilesStat_f = function(pName){
return function(pError, pStat){
var fReturnFalse = function(){
return false;
};
if(pError)
lStats[pName] = {
'mode':0,
'size':0,
'isDirectory':fReturnFalse
};
else
lStats[pName] = pStat;
/* if this file is last - moving next */
if(++lCount === pFiles.length)
CloudServer._fillJSON(lStats, pFiles);
};
};
if(pFiles.length)
for(var i = 0; i < pFiles.length; i++){
/* Получаем информацию о файле*/
Fs.stat(DirPath + pFiles[i],
getFilesStat_f(pFiles[i]));
}
else CloudServer._fillJSON(null, pFiles);
};
/**
* Function fill JSON by file stats
*
* @param pStats - object, contain file stats.
* example {'1.txt': stat}
*
* @param pFiles - array of files of current directory
*/
CloudServer._fillJSON = function(pStats, pFiles){
/* данные о файлах в формате JSON*/
var lJSON = [];
var lJSONFile = {};
lJSON[0] = {
path:DirPath,
size:'dir'
};
var fReturnFalse=function returnFalse(){return false;};
for(var i = 0; i < pFiles.length; i++)
{
/*
*Переводим права доступа в 8-ричную систему
*/
var lName = pFiles[i];
var lMode = (pStats[lName].mode-0).toString(8);
var lStats = pStats[lName];
var lIsDir = lStats.isDirectory();
/* Если папка - выводим пиктограмму папки *
* В противоположном случае - файла */
lJSONFile = {'name':pFiles[i],
'size' : (lIsDir?'dir':lStats.size),
'uid' : lStats.uid,
'mode' : lMode};
lJSON[i+1] = lJSONFile;
}
/* заголовок ответа сервера */
var lHeader;
var lList;
/* если js недоступен
* или отключен выcылаем html-код
* и прописываем соответствующие заголовки
*/
if(CloudServer.NoJS){
var lPanel = CloudFunc.buildFromJSON(lJSON);
lList = '<ul id=left class=panel>';
lList += lPanel;
lList += '</ul>';
lList += '<ul id=right class="panel">';
lList += lPanel;
lList += '</ul>';
/* пробуем достать данные из кэша
* с жатием или без, взависимости
* от настроек
*/
var lFileData = CloudServer.Cache.get(CloudServer.INDEX);
/* если их нет там - вычитываем из файла*/
if(!lFileData) {
Fs.readFile(CloudServer.INDEX,
CloudServer.indexReaded(lList));
}else {
var lReaded_f = CloudServer.indexReaded(lList);
lReaded_f(false, lFileData);
}
}else{
/* в обычном режиме(когда js включен
* высылаем json-структуру файлов
* с соответствующими заголовками
*/
lList = JSON.stringify(lJSON);
lHeader = CloudServer.generateHeaders('fs.json', CloudServer.Gzip);
/* если браузер поддерживает gzip-сжатие - сжимаем данные*/
if(CloudServer.Gzip){
Zlib.gzip(lList,CloudServer.getGzipDataFunc(lHeader,CloudServer.INDEX));
}
/* если не поддерживаеться - отсылаем данные без сжатия*/
else
CloudServer.sendResponse(lHeader, lList, CloudServer.INDEX);
}
};
/**
*@param pList
*/
CloudServer.indexReaded = function(pList){
return function(pError, pIndex){
if(pError){
return console.log(pError);
}
/* и сохраняем в кэш */
CloudServer.Cache.set(CloudServer.INDEX, pIndex);
pIndex = pIndex.toString();
/* если выбрана опция минифизировать скрпиты
* меняем в index.html обычные css на
* минифицированый
*/
if(CloudServer.Minify._allowed.css)
pIndex = pIndex.replace('<link rel=stylesheet href="/css/reset.css">', '')
.replace('/css/style.css', CloudServer.Minify.MinFolder + 'all.min.css');
pIndex = pIndex.toString().replace('<div id=fm class=no-js>',
'<div id=fm class=no-js>'+pList);
/* меняем title */
pIndex = pIndex.replace('<title>Cloud Commander</title>',
'<title>' + CloudFunc.setTitle() + '</title>');
if(!CloudServer.Config.appcache)
pIndex = pIndex.replace(' manifest="/cloudcmd.appcache"', '');
var lHeader;
/* если браузер поддерживает gzip-сжатие*/
lHeader = CloudServer.generateHeaders('index.html', CloudServer.Gzip);
/* если браузер поддерживает gzip-сжатие - сжимаем данные*/
if(CloudServer.Gzip) {
Zlib.gzip(pIndex,
CloudServer.getGzipDataFunc(lHeader, CloudServer.INDEX));
}
/* если не поддерживаеться - отсылаем данные без сжатия*/
else
CloudServer.sendResponse(lHeader, pIndex, CloudServer.INDEX);
};
};
/**
* Функция генерирует функцию считывания файла
* таким образом, что бы у нас было
* имя считываемого файла
* @param pName - полное имя файла
*/
CloudServer.getReadFileFunc = function(pName){
/*
* @pError - ошибка
* @pData - данные
* @pFromCache_o - прочитано с файла,
* или из одного из кешей
* Пример {cache: false, minify: true}
*/
var lReadFile = function(pError, pData, pFromCache_o){
if (!pError){
console.log('file ' + pName + ' readed');
/* берём из кэша данные файла
* если их нет в кэше -
* сохраняем
*/
if(pFromCache_o && !pFromCache_o.cache && CloudServer.Cache.isAllowed)
CloudServer.Cache.set(pName, pData);
/* если кэш есть
* сохраняем его в переменную
* которая до этого будет пустая
* по скольку мы будем вызывать этот метод
* сами, ведь файл уже вычитан
*/
var lHeader = CloudServer.generateHeaders(pName, CloudServer.Gzip);
/* если браузер поддерживает gzip-сжатие - сжимаем данные*/
if( CloudServer.Gzip && !(pFromCache_o && pFromCache_o.cache) ){
/* сжимаем содержимое */
Zlib.gzip(pData,CloudServer.getGzipDataFunc(lHeader, pName));
}
else{
/* высылаем несжатые данные */
CloudServer.sendResponse(lHeader, pData, pName);
}
}
else
{
console.log(pError.path);
if(pError.path !== 'passwd.json')
{
console.log(pError);
/* sending page not found */
CloudServer.Statuses[pName] = 404;
CloudServer.sendResponse('file not found', pError.toString(), pName);
}else{
CloudServer.sendResponse('OK', 'passwd.json');
}
}
};
return lReadFile;
};
/**
* Функция получает сжатые данные
* @param pHeader - заголовок файла
* @pName
*/
CloudServer.getGzipDataFunc = function(pHeader, pName){
return function(error, pResult){
if(!error){
/* отправляем сжатые данные
* вместе с заголовком
*/
/* если установлена работа с кэшем
* сохраняем сжатые данные
*/
if(CloudServer.Cache.isAllowed){
/* устанавливаем кєш */
console.log(pName+' gziped');
CloudServer.Cache.set(pName+'_gzip', pResult);
}
CloudServer.sendResponse(pHeader, pResult, pName);
}
else{
console.log(error);
CloudServer.sendResponse(pHeader, error);
}
};
};
/**
* Функция высылает ответ серверу
* @param pHead - заголовок
* @param Data - данные
* @param pName - имя отсылаемого файла
*/
CloudServer.sendResponse = function(pHead, pData, pName){
/* если у нас есть указатель на responce
* для соответствующего файла -
* высылаем его
*/
var lResponse = CloudServer.Responses[pName];
var lStatus = CloudServer.Statuses[pName];
if(lResponse){
lResponse.writeHead(
lStatus,
pHead);
lResponse.end(pData);
console.log(pName + ' sended');
}
};
/**
* function do safe require of needed module
* @param pModule
*/
function cloudRequire(pModule){
try{
return require(pModule);
}
catch(pError){
return false;
}
}
exports.start = function(pConfig){
CloudServer.start(pConfig);
};

12
shell/c9kill.sh Executable file
View file

@ -0,0 +1,12 @@
kill -9 `ps ax|grep node-openshift|grep -v grep|awk '{print $1}'`
# print finded process
ProcessList=`ps ax|grep node-openshift`
echo $ProcessList
# getting pid of process
PID=`echo "${ProcessList}"|grep -v grep|awk '{print $1}'`
echo $PID
#kill it
if test ! $PID
then echo 'process not found'
else kill -9 $PID && echo 'killed process'
fi

9
shell/cloudcmd.bat Normal file
View file

@ -0,0 +1,9 @@
:: -------------------------------------
:: Changing charset back to 866, because
:: all win32 default commands, that work
:: thru cmd.exe showing out in unicode
:: charset. So Cloud Commander changes
:: 866 charset to Unicode 65001 sometime
:: when it's neaded.
:: -------------------------------------
node ../cloudcmd || chcp 866

12
shell/deploy.sh Executable file
View file

@ -0,0 +1,12 @@
cd ..
echo 'appfog'
af update
echo 'http://cloudcmd.aws.af.cm/'
echo 'cloud foundry'
vmc update
echo 'http://cloudcmd.cloudfoundry.com/'
echo 'nodester'
git push nodester master
echo 'heroku'
git push heroku master
cd shell

View file

@ -1,5 +1,5 @@
git checkout dev
cd node_modules
cd ../node_modules
git clone git://github.com/coderaiser/minify
cd minify
git checkout dev

17
shell/kill.js Normal file
View file

@ -0,0 +1,17 @@
/* c9.io kill active node process */
(function(){
"use strict";
var exec = require('child_process').exec,
lCmd = 'kill -9' + ' ' + /* kill finded process */
'`ps ax' + '|' + /* show all process */
'grep node-openshift' + '|' + /* find node-openshift */
'grep -v grep' + '|' + /* exlude grep command */
'awk "{print $1}"`'; /* show first collumn */
exec(lCmd, function(error, stdout, stderr){
console.log(error || stdout || stderr);
});
})();

12
shell/secret.bat Normal file
View file

@ -0,0 +1,12 @@
#
# part of Cloud Commander
# win32 version
# secrets of github and dropbox
# must not be shared
# http://github.com/coderaiser/cloudcmd
#
# for using just add %-symbol on start and end of name
# like %github_secret%
set github_key=435c670f44320e287ad6
set github_secret=a8b49e8c8c1a6253b149d224149a076270d8d614

12
shell/secret.sh Executable file
View file

@ -0,0 +1,12 @@
#!/bin/bash
#
# part of Cloud Commander
# *nix version
# secrets of github and dropbox
# must not be shared
# http://github.com/coderaiser/cloudcmd
#
# for using just add $-symbol on start of name
# like $github_secret
export github_secret=afe9bed1e810c5dc44c4c2a953fc6efb1e5b0545

View file

@ -1,8 +1,12 @@
var CloudFunc = require('../lib/cloudfunc');
var assert = require('assert');
try{
var lJSON = [{
(function(){
"use strict";
var DIR = process.cwd() + '/',
main = require(DIR + 'lib/server/main'),
CloudFunc = main.cloudfunc,
lJSON = [{
"path": "/etc/X11/",
"size": "dir"
}, {
@ -17,8 +21,10 @@ try{
"mode": "100755"
}];
console.time('CloudFunc.buildFromJSON');
start();
var lResult = CloudFunc.buildFromJSON(lJSON);
end();
var lExpect =
'<li class=path>' +
'<span class="path-icon clear-cache"' +
@ -27,7 +33,7 @@ try{
'<span class="path-icon refresh-icon" title="refresh (Ctrl+R)">'+
'<a href="/fs/no-js/etc/X11"></a></span>' +
'<span>' +
'<a class=links href="/fs/no-js" title=""/"">/</a>' +
'<a class=links href="/fs/no-js" title="/">/</a>' +
'<a class=links href="/fs/no-js/etc" title="/etc">' +
'etc' +
'</a>/X11/' +
@ -40,38 +46,53 @@ try{
'<span class=owner>owner</span>' +
'<span class=mode>mode</span>' +
'</li>' +
'<li class=current-file>' +
'<span class="mini-icon directory"></span>' +
'<span class=name><a href="/fs/no-js/etc">..</a></span>' +
'<span class=size>&lt;dir&gt;</span>' +
'<span class=owner>.</span><span class=mode></span>' +
'</li>' +
'<li class>' +
'<li draggable class=current-file>' +
'<span class="mini-icon directory"></span>' +
'<span class=name>' +
'<a href="/fs/no-js/etc/X11/applnk">applnk</a>' +
'<a href="/fs/no-js/etc" draggable=true>..</a>' +
'</span>' +
'<span class=size>&lt;dir&gt;</span>' +
'<span class=owner>root</span>' +
'<span class=mode>rwx r-x r-x</span>' +
'<span class=owner>.</span>' +
'<span class=mode></span>' +
'</li>' +
'<li class>' +
'<span class="mini-icon text-file"></span>' +
'<span class=name>' +
'<a href="/fs/no-js/etc/X11/prefdm" target="_blank">' +
'prefdm' +
'<li draggable class>' +
'<span draggable class="mini-icon directory"></span>' +
'<span draggable class=name>' +
'<a href="/fs/no-js/etc/X11/applnk" ' +
'title="applnk" draggable=true>applnk</a>' +
'</span>' +
'<span draggable class=size>&lt;dir&gt;</span>' +
'<span draggable class=owner>root</span>' +
'<span draggable class=mode>rwx r-x r-x</span>' +
'</li>' +
'<li draggable class>' +
'<span draggable class="mini-icon text-file"></span>' +
'<span draggable class=name>' +
'<a href="/fs/no-js/etc/X11/prefdm" ' +
'target="_blank" title="prefdm" draggable=true>' +
'prefdm' +
'</a>' +
'</span>' +
'<span class=size>1.30kb</span>' +
'<span class=owner>root</span>' +
'<span class=mode>rwx r-x r-x</span>' +
'</li>1';
'<span draggable class=size>1.30kb</span>' +
'<span draggable class=owner>root</span>' +
'<span draggable class=mode>rwx r-x r-x</span>' +
'</li>';
for(var i = 0, n = lExpect.length; i < n; i++)
if(lResult[i] !== lExpect[i]){
console.log('Error in char number: ' + i + '\n' +
'Expect: ' + lExpect.substr(i) + '\n' +
'Result: ' + lResult.substr(i) );
break;
}
if(i===n)
console.log('CloudFunc.buildFromJSON: OK');
console.timeEnd('CloudFunc.buildFromJSON');
assert.equal(
lResult,
lExpect, 'Something wrong in buildFromJSON');
}
catch(pError){
console.log(pError);
}
function start(){
return console.time('CloudFunc.buildFromJSON');
}
function end(){
return console.timeEnd('CloudFunc.buildFromJSON');
}
})();

31
test/test.sh Normal file → Executable file
View file

@ -1,16 +1,15 @@
#!/bin/sh
#linting js files
npm i jshint
echo "jshint server.js client.js cloudcmd.js"
./node_modules/jshint/bin/hint --config ./test/.jshintrc ./server.js ./client.js ./cloudcmd.js
echo "jshint lib/cloudfunc.js lib/client/keyBinding.js"
./node_modules/jshint/bin/hint --config ./test/.jshintrc ./lib/cloudfunc.js ./node_modules/minify/minify.js ./lib/client/keyBinding.js
echo "jshint ./package.json ./config.json"
./node_modules/jshint/bin/hint --config ./test/.jshintrc ./package.json ./config.json
#linting css files
npm i recess
echo "recess css/*.css"
./node_modules/recess/bin/recess css/*.css
node ./test/test.js
node cloudcmd.js test
ls ./min
#!/bin/sh
npm i recess jshint
echo "jshint server.js client.js cloudcmd.js"
node_modules/jshint/bin/hint --config test/.jshintrc lib/server.js lib/client.js cloudcmd.js
echo "jshint lib/cloudfunc.js lib/client/keyBinding.js"
node_modules/jshint/bin/hint --config test/.jshintrc lib/util.js lib/cloudfunc.js lib/client/keyBinding.js
echo "lib/client/dom.js lib/client/ie.js lib/client/menu.js lib/client/socket.js ./lib/client/terminal.js lib/client/viewer.js lib/client/storage/_github.js lib/client/menu.js lib/client/editor/_codemirror.js"
node_modules/jshint/bin/hint --config test/.jshintrc lib/client/dom.js lib/client/ie.js lib/client/menu.js lib/client/socket.js ./lib/client/terminal.js lib/client/viewer.js lib/client/storage/_github.js lib/client/menu.js lib/client/editor/_codemirror.js
echo "jshint ./package.json ./config.json"
node_modules/jshint/bin/hint --config test/.jshintrc ./package.json ./config.json
#linting css files
echo "recess css/*.css"
./node_modules/recess/bin/recess css/*.css
node test/test.js
node cloudcmd.js test