mirror of
https://github.com/coderaiser/cloudcmd.git
synced 2026-01-23 18:55:26 +00:00
v 0.1.8
This commit is contained in:
commit
5c5be0946c
77 changed files with 26745 additions and 8729 deletions
23
.travis.yml
23
.travis.yml
|
|
@ -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
202
ChangeLog
|
|
@ -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
156
README.md
|
|
@ -1,156 +0,0 @@
|
|||
Cloud Commander [](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
|
||||
|
|
@ -1 +0,0 @@
|
|||
node cloudcmd || chcp 866
|
||||
222
cloudcmd.js
222
cloudcmd.js
|
|
@ -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);
|
||||
}
|
||||
})();
|
||||
34
config.json
34
config.json
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
BIN
img/logo/cloudcmd.cdr
Normal file
Binary file not shown.
BIN
img/logo/cloudcmd.png
Normal file
BIN
img/logo/cloudcmd.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 82 KiB |
38
index.html
38
index.html
|
|
@ -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
862
lib/client.js
Normal 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
1115
lib/client/dom.js
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -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;
|
||||
})();
|
||||
|
|
@ -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
770
lib/client/editor/codemirror/mode/javascript.js
vendored
770
lib/client/editor/codemirror/mode/javascript.js
vendored
|
|
@ -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 });
|
||||
|
|
|
|||
644
lib/client/editor/codemirror/mode/xml.js
vendored
644
lib/client/editor/codemirror/mode/xml.js
vendored
|
|
@ -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});
|
||||
|
|
|
|||
21
lib/client/editor/codemirror/package.json
Normal file
21
lib/client/editor/codemirror/package.json
Normal 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"}]
|
||||
}
|
||||
42
lib/client/editor/codemirror/theme/night.css
vendored
42
lib/client/editor/codemirror/theme/night.css
vendored
|
|
@ -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; }
|
||||
|
|
|
|||
23
lib/client/editor/codemirror3/LICENSE
Normal file
23
lib/client/editor/codemirror3/LICENSE
Normal 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.
|
||||
8
lib/client/editor/codemirror3/README.md
Normal file
8
lib/client/editor/codemirror3/README.md
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# CodeMirror [](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
|
||||
231
lib/client/editor/codemirror3/codemirror.css
Normal file
231
lib/client/editor/codemirror3/codemirror.css
Normal 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;
|
||||
}
|
||||
}
|
||||
4124
lib/client/editor/codemirror3/codemirror.js
Normal file
4124
lib/client/editor/codemirror3/codemirror.js
Normal file
File diff suppressed because it is too large
Load diff
409
lib/client/editor/codemirror3/mode/javascript.js
Normal file
409
lib/client/editor/codemirror3/mode/javascript.js
Normal 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 });
|
||||
318
lib/client/editor/codemirror3/mode/xml.js
Normal file
318
lib/client/editor/codemirror3/mode/xml.js
Normal 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});
|
||||
21
lib/client/editor/codemirror3/package.json
Normal file
21
lib/client/editor/codemirror3/package.json
Normal 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"}]
|
||||
}
|
||||
21
lib/client/editor/codemirror3/theme/night.css
Normal file
21
lib/client/editor/codemirror3/theme/night.css
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/* Loosely based on the Midnight Textmate theme */
|
||||
|
||||
.cm-s-night { background: #0a001f; color: #f8f8f8; }
|
||||
.cm-s-night div.CodeMirror-selected { background: #447 !important; }
|
||||
.cm-s-night .CodeMirror-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; }
|
||||
|
|
@ -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');
|
||||
})();
|
||||
149
lib/client/ie.js
149
lib/client/ie.js
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
})();
|
||||
8569
lib/client/jquery-ui/jquery-ui-1.8.23.custom.js
vendored
Normal file
8569
lib/client/jquery-ui/jquery-ui-1.8.23.custom.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -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;
|
||||
});
|
||||
};
|
||||
})();
|
||||
|
|
@ -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;
|
||||
})();
|
||||
|
|
@ -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
|
||||
|
|
|
|||
55
lib/client/storage/_dropbox.js
Normal file
55
lib/client/storage/_dropbox.js
Normal 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;
|
||||
})();
|
||||
170
lib/client/storage/_github.js
Normal file
170
lib/client/storage/_github.js
Normal 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;
|
||||
})();
|
||||
25
lib/client/storage/github/LICENSE
Normal file
25
lib/client/storage/github/LICENSE
Normal 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.
|
||||
272
lib/client/storage/github/README.md
Normal file
272
lib/client/storage/github/README.md
Normal 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.
|
||||
499
lib/client/storage/github/github.js
Normal file
499
lib/client/storage/github/github.js
Normal 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);
|
||||
82
lib/client/storage/github/lib/base64.js
Normal file
82
lib/client/storage/github/lib/base64.js
Normal 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
|
||||
1189
lib/client/storage/github/lib/underscore.js
Normal file
1189
lib/client/storage/github/lib/underscore.js
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -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;
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
})();
|
||||
115
lib/client/viewer/fancybox/CHANGELOG.md
Normal file
115
lib/client/viewer/fancybox/CHANGELOG.md
Normal 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.
|
||||
217
lib/client/viewer/fancybox/README.md
Normal file
217
lib/client/viewer/fancybox/README.md
Normal 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
|
||||
|
|
@ -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 ),
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
411
lib/client/viewer/fancybox/jquery.fancybox.js
vendored
411
lib/client/viewer/fancybox/jquery.fancybox.js
vendored
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
913
lib/cloudfunc.js
913
lib/cloudfunc.js
|
|
@ -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><dir></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' ?
|
||||
'<dir>'
|
||||
/* если это файл - получаем
|
||||
* короткий размер
|
||||
*/
|
||||
: 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><dir></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' ?
|
||||
'<dir>'
|
||||
/* если это файл - получаем
|
||||
* короткий размер
|
||||
*/
|
||||
: 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
756
lib/server.js
Normal 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;
|
||||
})();
|
||||
|
|
@ -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
83
lib/server/auth.js
Normal 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
60
lib/server/cache.js
Normal 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={};
|
||||
}
|
||||
})
|
||||
};
|
||||
})();
|
||||
|
|
@ -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
208
lib/server/main.js
Normal 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
128
lib/server/minify.js
Normal 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 : {}
|
||||
};
|
||||
})();
|
||||
|
|
@ -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
204
lib/server/rest.js
Normal 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);
|
||||
});
|
||||
}
|
||||
|
||||
})();
|
||||
|
|
@ -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
64
lib/server/update.js
Normal 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
401
lib/util.js
Normal 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
8
modules.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[
|
||||
"editor/_codemirror",
|
||||
"menu",
|
||||
"viewer",
|
||||
"storage/_github",
|
||||
"storage/_dropbox",
|
||||
"terminal"
|
||||
]
|
||||
826
server.js
826
server.js
|
|
@ -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
12
shell/c9kill.sh
Executable 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
9
shell/cloudcmd.bat
Normal 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
12
shell/deploy.sh
Executable 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
|
||||
|
|
@ -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
17
shell/kill.js
Normal 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
12
shell/secret.bat
Normal 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
12
shell/secret.sh
Executable 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
|
||||
89
test/test.js
89
test/test.js
|
|
@ -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><dir></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><dir></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><dir></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
31
test/test.sh
Normal file → Executable 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue