191
ChangeLog
|
|
@ -1,3 +1,194 @@
|
|||
2013.02.13, v0.8.0
|
||||
|
||||
fix:
|
||||
- (test) template
|
||||
- (cloudfunc) "draggable" -> "draggable "
|
||||
- (cloudfunc) buildFromJSON: lHeader -> header
|
||||
- (test) add ""
|
||||
- (test) add draggable
|
||||
- (test) li -> div
|
||||
- (files) p.error -> error
|
||||
- (test) add "
|
||||
- (path) rm " "
|
||||
- (test) JSON_Files -> JSON_FILES
|
||||
- (polyfill) args not defined
|
||||
- (server) lHost -> host
|
||||
- (test) change template
|
||||
- (util) retExec: add unshift
|
||||
- (rest) onPut: lFiles.password -> lFiles && lFiles.password
|
||||
- (console) access to socket before it loads
|
||||
- (cloudcmd) pParams -> params
|
||||
- (util) Util.exec -> Util.retExec.apply
|
||||
- (main) getQuery
|
||||
- (css) error position: add left
|
||||
- (listeners) changeLinks: pPanelID -> panelId
|
||||
- (menu) if menu could not load keys do not working
|
||||
- (terminal) resizing
|
||||
- (terminal) CHANNEL_RESIZE -> CHANNEL_TERMINAL_RESIZE
|
||||
- (terminal) new line
|
||||
- (terminal) maxSize
|
||||
- (terminal) when back to term, first simbol wasn't wrote
|
||||
- (rest) onFS: Util.log -> Util.log.bind(Util)
|
||||
- (terminal) onConnection bind: add context
|
||||
- (terminal) add rmKeys
|
||||
- (polyfill) bind
|
||||
- (console) chat do not broadcast
|
||||
- (dom) getNotCurrentDirPath: add this
|
||||
- (console) io - > exec
|
||||
- (users) File -> FILE
|
||||
- (config) init: change order
|
||||
- (filepicker) change to proto
|
||||
- (menu) set key bind when close
|
||||
- (github) api_url -> apiURL
|
||||
- (loader) anyLoadOnLoad: lParam -> param, callback -> func
|
||||
- (console) connNum -> conNum
|
||||
- (console) onMessageFunc -> Clients
|
||||
- (socket) ret -> io
|
||||
- (socket) change onDisconect
|
||||
- (socket) lRet -> ret
|
||||
- (socket) add removeListeners
|
||||
- (cloudfunc) RIGH_TPANE -> RIGHT_PANEL
|
||||
- (menu) if ESC current file is one mouse pointer on it
|
||||
- (dom) scrollIntoViewIfNeeded: add false
|
||||
- (dom) getPanel isLeft check: class -> id
|
||||
- (view) ',' -> ';'
|
||||
- (client) getJSONfromFileTable: owner -> uid
|
||||
- (client) getJSONfromFileTable: i: 2 -> 0
|
||||
- (client) baseInit: add 'js-'
|
||||
- (client) createFileTable: found -> !found
|
||||
- (view) add margin only when view text documents
|
||||
- (dom) selectByPattern: show message if not esc
|
||||
- (dom) selectByPattern: "." -> "\."
|
||||
- (dom) selectByPattern: DOM.alert -> Dialog.alert
|
||||
- chore(cloudcmd) fix lint warnings
|
||||
- (socket) prevent memory leak
|
||||
- (socket) getSpawn: add tryCachLog
|
||||
- (edit) add emmet
|
||||
- (cloudcmd) minify -> Minify
|
||||
- (loader) add Images
|
||||
- (client) baseInit: mv setCurrentFile to top
|
||||
- (dom) add max length for storage file
|
||||
- (express) if username or password is change, auth do not work
|
||||
- (listeners) changeLinks: get back current
|
||||
- (dom) getById: rm pElement
|
||||
- (dom) udpateCurrentInfo: files -> files.children
|
||||
- (css) .keyspanel: rm height
|
||||
- (css) .delete-button: change icon
|
||||
- (key) insert
|
||||
- (dom) first 2 files not select
|
||||
- (dom) CurrentInfo: prev, next not updates
|
||||
- (config) if view is load config do not show
|
||||
- (key) ";" -> ","
|
||||
- (config) init
|
||||
- (dom) getCurrentData: this -> Cmd
|
||||
- (edit) DOM.getData -> Info.getData
|
||||
- (dom) getFiles: add panel
|
||||
- (client) baseInit pCallBack: mv to end
|
||||
|
||||
feature:
|
||||
- (util) spead improvement; exec: rm condition
|
||||
- (cloudfunc) buildFromJSON: rm draggable from header
|
||||
- (package) update minify to v0.2.5
|
||||
- (util) rm setTimeout
|
||||
- (util) rm setValue
|
||||
- (util) rm call
|
||||
- (main) rm gzipData
|
||||
- (rest) create recursive dir
|
||||
- (console) update to v2.10.0
|
||||
- (rest) add recursiv copy dir (rimraf -> fs-extra)
|
||||
- (main) add getPathName
|
||||
- (express) add join as express middleware
|
||||
- (dom) Images: improved access speed
|
||||
- (util) copyObj: from, to -> to, from
|
||||
- (listeners) onConextMenu: rm showLoad
|
||||
- (style) user-select : rm path
|
||||
- (rest) add ability to remove directory with files
|
||||
- (util) rm bind
|
||||
- (terminal) add info if pty not installed
|
||||
- (console) (dis)connected: add "console"
|
||||
- (terminal) add connect/disconnect events
|
||||
- (terminal) add Size
|
||||
- (terminal) dir : home -> DIR
|
||||
- (terminal) add update
|
||||
- (terminal) add cursor style
|
||||
- (main) mrequire: tryCatchLog - > tryCatch
|
||||
- (main) mrequire: tryCatch -> tryCatchLog
|
||||
- (terminal) add
|
||||
- (view) rm afterShow, mv css to style
|
||||
- (util) rm retFunc
|
||||
- (console) update to v2.8.0
|
||||
- (console) rm unused vars
|
||||
- (commander) rm file.sort: no use
|
||||
- (css) add media print
|
||||
- (style) .files: rm cursor
|
||||
- (util) add slice
|
||||
- (util) loadOnLoad: rm data
|
||||
- (key) <shift> + <f7> - add new file
|
||||
- (loader) pop -> shift
|
||||
- (util) loadOnLoad: rm data
|
||||
- (util) loadOnLoad: pop -> shift - change params order
|
||||
- (socket) getSpawn: add error sending
|
||||
- (socket) addListener: first parameter could be object
|
||||
- (console) addEvenets: add connect, disconect
|
||||
- (panel) add
|
||||
- (cloudfunc) rm Path
|
||||
- (key) add Ctrl + \ -> go to root
|
||||
- (key) go to parent dir on backspace
|
||||
- (style) add .reduce-text
|
||||
- (style) prevent text from wrapping in .size and .owner
|
||||
- (style) if size to long it reduce
|
||||
- (style) if owner name to long - it's reduce
|
||||
- (users) add
|
||||
- (key) add help call on "?"
|
||||
- (commander) uid -> owner
|
||||
- (dom) scrollIntViewIfNeeded: add second param
|
||||
- (dom) CurrentInfo: add filesPassive
|
||||
- (dom) getByClass -> getByClassAll
|
||||
- (util) rm bind
|
||||
- (files) readPipe: pop -> shift
|
||||
- (dom) add getCurrentOwner
|
||||
- (style) rm #path
|
||||
- (view) add margin
|
||||
- (dom) add extended selection (*, +, -)
|
||||
- (package) http-auth: 2.1.x
|
||||
- (package) minify: v0.2.4
|
||||
- (edit) add autocomletion
|
||||
- (edit) emmet: add css suport
|
||||
- (edit) add emmet
|
||||
- (notify) add
|
||||
- (loader) add
|
||||
- (path) add title with full path
|
||||
- (css) style: new line when path to long
|
||||
- (listeners) open file on new tab on <ctrl> + click
|
||||
- (style) max-width 600px: hide .size, .owner, .mode
|
||||
- (style) .cmd-button color : rgb(49,123,249) -> #222
|
||||
- (listeners) initKeysPanel: rm i === 12
|
||||
- (menu) From Filepicker -> From Cloud
|
||||
- (css) icons: add menu icons
|
||||
- (menu) getItem: add icon processing
|
||||
- (css) mv icons to icons.css
|
||||
- (css) add .icon-file
|
||||
- (menu) add icons
|
||||
- (util) add getStrSmallfirst
|
||||
- (menu) getConfig: rm callback
|
||||
- (css) .cmd-button width: 9% -> 5%
|
||||
- (html) buttons: mv name to title
|
||||
- (css) keyspanel: add rename, copy and move icons
|
||||
- (css) keyspanel: add icons for: help, view, edit, delete, menu, console and contact
|
||||
- (style) cmd-button: add directory icon
|
||||
- (font) add config icon
|
||||
- (css) style height: .fm 85%, .keyspanel 15%
|
||||
- (css) .cmd-button hover: rm border color
|
||||
- (config) auth: false
|
||||
- (cloudcmd) add http authorization
|
||||
- (listeners) ListenersProto: add Info
|
||||
- (dom) add CurrentInfo
|
||||
- (key) rm lCurrent
|
||||
- (config) add Loading
|
||||
- (style) .cmd-button focus: add outline
|
||||
- (contact) add
|
||||
- (cloudfunc) getJoinURL: rm first "/"
|
||||
|
||||
2013.12.09, v0.7.0
|
||||
|
||||
fix:
|
||||
|
|
|
|||
119
HELP.md
|
|
@ -1,11 +1,11 @@
|
|||
Cloud Commander v0.7.0 [![NPM version][NPMIMGURL]][NPMURL] [![Dependency Status][DependencyStatusIMGURL]][DependencyStatusURL] [![Build Status][BuildStatusIMGURL]][BuildStatusURL]
|
||||
Cloud Commander v0.8.0 [![NPM version][NPMIMGURL]][NPMURL] [![Dependency Status][DependencyStatusIMGURL]][DependencyStatusURL] [![Build Status][BuildStatusIMGURL]][BuildStatusURL]
|
||||
===============
|
||||
###[Main][MainURL] [Blog][BlogURL] Live(![IO][IO_LIVE_IMG] [IO][IOURL], ![JitSu][JitSu_LIVE_IMG] [JitSu][JitSuURL], ![Heroku][Heroku_LIVE_IMG] [Heroku][HerokuURL] ![RunKite][RunKite_LIVE_IMG] [RunKite][RunKiteURL])
|
||||
###[Main][MainURL] [Blog][BlogURL] Live(![IO][IO_LIVE_IMG] [IO][IOURL], ![JitSu][JitSu_LIVE_IMG] [JitSu][JitSuURL], ![Heroku][Heroku_LIVE_IMG] [Heroku][HerokuURL])
|
||||
[NPMIMGURL]: https://badge.fury.io/js/cloudcmd.png
|
||||
[BuildStatusIMGURL]: https://secure.travis-ci.org/coderaiser/cloudcmd.png?branch=master
|
||||
[DependencyStatusIMGURL]: https://gemnasium.com/coderaiser/cloudcmd.png
|
||||
[FlattrIMGURL]: https://api.flattr.com/button/flattr-badge-large.png
|
||||
[NPM_INFO_IMG]: https://nodei.co/npm/cloudcmd.png?downloads=true&&stars
|
||||
[NPM_INFO_IMG]: https://nodei.co/npm/cloudcmd.png
|
||||
[NPMURL]: https://npmjs.org/package/cloudcmd "npm"
|
||||
[BuildStatusURL]: http://travis-ci.org/coderaiser/cloudcmd "Build Status"
|
||||
[DependencyStatusURL]: https://gemnasium.com/coderaiser/cloudcmd "Dependency Status"
|
||||
|
|
@ -15,11 +15,9 @@ Cloud Commander v0.7.0 [![NPM version][NPMIMGURL]][NPMURL] [![Dependency Status]
|
|||
[IOURL]: http://io.cloudcmd.io "IO"
|
||||
[JitSuURL]: http://cloudcmd.jit.su "JitSu"
|
||||
[HerokuURL]: http://cloudcmd.herokuapp.com/ "Heroku"
|
||||
[RunKiteURL]: http://cloudcmd.apps.runkite.com/ "RunKite"
|
||||
[IO_LIVE_IMG]: https://status-ok.cloudcmd.io/host/io.cloudcmd.io/fs?json "IO"
|
||||
[JitSu_LIVE_IMG]: https://status-ok.cloudcmd.io/host/cloudcmd.jit.su/fs?json "JitSu"
|
||||
[HEROKU_LIVE_IMG]: https://status-ok.cloudcmd.io/host/cloudcmd.herokuapp.com/fs?json "Heroku"
|
||||
[RunKite_LIVE_IMG]: https://status-ok.cloudcmd.io/host/cloudcmd.apps.runkite.com/fs?json "RunKite"
|
||||
[IO_LIVE_IMG]: http://status-ok.cloudcmd.io/host/io.cloudcmd.io/fs?json "IO"
|
||||
[JitSu_LIVE_IMG]: http://status-ok.cloudcmd.io/host/cloudcmd.jit.su/fs?json "JitSu"
|
||||
[HEROKU_LIVE_IMG]: http://status-ok.cloudcmd.io/host/cloudcmd.herokuapp.com/fs?json "Heroku"
|
||||
|
||||
**Cloud Commander** - cloud file manager with console and editor.
|
||||
|
||||
|
|
@ -49,28 +47,25 @@ All you need is
|
|||
and unpack or just clone repository from github:
|
||||
|
||||
```
|
||||
git clone git://github.com/coderaiser/cloudcmd.git
|
||||
cd cloudcmd
|
||||
node cloudcmd
|
||||
git clone git://github.com/coderaiser/cloudcmd.git
|
||||
cd cloudcmd
|
||||
npm install
|
||||
node cloudcmd
|
||||
```
|
||||
or install in npm:
|
||||
|
||||
```
|
||||
npm i cloudcmd -g
|
||||
cloudcmd
|
||||
npm install cloudcmd -g
|
||||
cloudcmd
|
||||
```
|
||||
|
||||
Additional modules
|
||||
---------------
|
||||
**Cloud Commander's Server Side** not using additional modules for main functionality.
|
||||
But for console and 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 (type in **Cloud Commander** directory):
|
||||
|
||||
npm i
|
||||
**Cloud Commander** could work without any modules installed.
|
||||
But for console, minification, file system operations etc, recommended
|
||||
install additional modules with next commnad (type in **Cloud Commander**'s directory):
|
||||
|
||||
npm install
|
||||
|
||||
Hot keys
|
||||
---------------
|
||||
|
|
@ -82,15 +77,20 @@ Hot keys
|
|||
- **F5** - copy
|
||||
- **F6** - rename/move
|
||||
- **F7** - new dir
|
||||
- **F7** + **shift** = new file
|
||||
- **F8, Delete** - remove current file
|
||||
- **F9** - menu
|
||||
- **F10** - config
|
||||
- **(*)** - select/unselect all
|
||||
- **(+)** - expand selection
|
||||
- **(-)** - shrink selection
|
||||
- **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
|
||||
- **Ctrl + a** - select all files in a panel
|
||||
- **up, down, enter** - filesystem navigation
|
||||
- **Ctrl + \** - go to the root directory
|
||||
- **Tab** - move via panels
|
||||
- **Page Up** - up on one page
|
||||
- **Page Down** - down on one page
|
||||
|
|
@ -100,6 +100,7 @@ Hot keys
|
|||
- **Insert** - select current file
|
||||
- **Shift + F10** - context menu
|
||||
- **~** - console
|
||||
- **Ctrl + Click** - open file on new tab
|
||||
|
||||
Edit
|
||||
---------------
|
||||
|
|
@ -160,6 +161,9 @@ All main configuration could be done via [config.json](json/config.json "Config"
|
|||
|
||||
```js
|
||||
{
|
||||
"auth" : false, /* enable http authentication */
|
||||
"username" : "root", /* username for authentication */
|
||||
"password" : "toor", /* password hash in sha-1 for authentication*/
|
||||
"apiURL" :"/api/v1",
|
||||
"appCache" : false, /* cache files for offline use */
|
||||
"analytics" : true, /* google analytics suport */
|
||||
|
|
@ -241,7 +245,7 @@ server {
|
|||
server_name io.cloudcmd.io;
|
||||
access_log /var/log/nginx/io.cloudcmd.io.access.log;
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8000/;
|
||||
proxy_pass http://127.0.0.1:8000/;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -263,6 +267,19 @@ server {
|
|||
}
|
||||
```
|
||||
|
||||
For websocket suport (nginx >= v1.3.13) modify server block:
|
||||
|
||||
```sh
|
||||
location / {
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
proxy_pass http://127.0.0.1:8000/;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
If you need redirection from **http** to **https**, it's simple:
|
||||
|
||||
```sh
|
||||
|
|
@ -286,15 +303,6 @@ do something like this:
|
|||
|
||||
nohup node cloudcmd
|
||||
|
||||
Authorization
|
||||
---------------
|
||||
Cloud Commander could authorize clients on GitHub via openID.
|
||||
All things that should be done is must be added **id** and **secret** of application
|
||||
from github settings page and added to [modules.json](json/modules.json) (id just) and env variable (secret)
|
||||
with names: *github_id*, *github_secret*, *dropbox_key*, *dropbox_secret* etc in
|
||||
[secret.bat](shell/secret.bat) *(on win32)* or [secret.sh](shell/secret.sh) *(on nix)*.
|
||||
|
||||
|
||||
Start
|
||||
---------------
|
||||
To start **Cloud Commander** only one command needed:
|
||||
|
|
@ -327,10 +335,9 @@ and then, if there is new version
|
|||
npm r cloudcmd
|
||||
npm i cloudcmd
|
||||
|
||||
Extensions
|
||||
Additional modules list
|
||||
---------------
|
||||
**Cloud Commander** desinged to easily porting extensions.
|
||||
For extend main functionality Cloud Commander use next modules:
|
||||
To extend capabilities of file manager next modules used:
|
||||
|
||||
- [Ace] [AceURL]
|
||||
- [FancyBox] [FancyBoxURL]
|
||||
|
|
@ -339,14 +346,20 @@ For extend main functionality Cloud Commander use next modules:
|
|||
- [github] [githubURL]
|
||||
- [dropbox-js] [dropbox-jsURL]
|
||||
- [jquery] [jqueryURL]
|
||||
- [socket.io] [socketIOURL]
|
||||
- [http-auth] [httpAuthURL]
|
||||
- [fs-extra] [fs-extraURL]
|
||||
|
||||
[AceURL]: //ace.ajax.org/ "Ace"
|
||||
[AceURL]: http://ace.ajax.org/ "Ace"
|
||||
[FancyBoxURL]: //github.com/fancyapps/fancyBox "FancyBox"
|
||||
[jQuery-contextMenuURL]: //github.com/medialize/jQuery-contextMenu "jQuery-contextMenu"
|
||||
[jq-consoleURL]: //github.com/replit/jq-console "jq-console"
|
||||
[jq-consoleURL]: //github.com/replit/jq-console "jq-console"
|
||||
[githubURL]: //github.com/michael/github "github"
|
||||
[dropbox-jsURL]: //github.com/dropbox/dropbox-js "dropbox-js"
|
||||
[jqueryURL]: //jquery.com
|
||||
[socketIOURL]: http://socket.io
|
||||
[httpAuthURL]: //github.com/gevorg/http-auth
|
||||
[fs-extraURL]: //github.com/jprichardson/node-fs-extra "fs-extra"
|
||||
|
||||
Contributing
|
||||
---------------
|
||||
|
|
@ -366,23 +379,23 @@ so to get it you should type a couple more commands:
|
|||
|
||||
Version history
|
||||
---------------
|
||||
|
||||
- *2013.12.09*, **[v0.7.0](//github.com/coderaiser/cloudcmd-archive/raw/master/cloudcmd-v0.7.0.zip)**
|
||||
- *2013.11.08*, **[v0.6.0](//github.com/coderaiser/cloudcmd-archive/raw/master/cloudcmd-v0.6.0.zip)**
|
||||
- *2013.10.17*, **[v0.5.0](//github.com/coderaiser/cloudcmd-archive/raw/master/cloudcmd-v0.5.0.zip)**
|
||||
- *2013.09.27*, **[v0.4.0](//github.com/coderaiser/cloudcmd-archive/raw/master/cloudcmd-v0.4.0.zip)**
|
||||
- *2013.08.01*, **[v0.3.0](//github.com/coderaiser/cloudcmd-archive/raw/master/cloudcmd-v0.3.0.zip)**
|
||||
- *2013.04.22*, **[v0.2.0](//github.com/coderaiser/cloudcmd-archive/raw/master/cloudcmd-v0.2.0.zip)**
|
||||
- *2013.03.01*, **[v0.1.9](//github.com/coderaiser/cloudcmd-archive/raw/master/cloudcmd-v0.1.9.zip)**
|
||||
- *2012.12.12*, **[v0.1.8](//github.com/coderaiser/cloudcmd-archive/raw/master/cloudcmd-v0.1.8.zip)**
|
||||
- *2012.10.01*, **[v0.1.7](//github.com/coderaiser/cloudcmd-archive/raw/master/cloudcmd-v0.1.7.zip)**
|
||||
- *2012.08.24*, **[v0.1.6](//github.com/coderaiser/cloudcmd-archive/raw/master/cloudcmd-v0.1.6.zip)**
|
||||
- *2012.08.06*, **[v0.1.5](//github.com/coderaiser/cloudcmd-archive/raw/master/cloudcmd-v0.1.5.zip)**
|
||||
- *2012.07.27*, **[v0.1.4](//github.com/coderaiser/cloudcmd-archive/raw/master/cloudcmd-v0.1.4.zip)**
|
||||
- *2012.07.19*, **[v0.1.3](//github.com/coderaiser/cloudcmd-archive/raw/master/cloudcmd-v0.1.3.zip)**
|
||||
- *2012.07.14*, **[v0.1.2](//github.com/coderaiser/cloudcmd-archive/raw/master/cloudcmd-v0.1.2.zip)**
|
||||
- *2012.07.11*, **[v0.1.1](//github.com/coderaiser/cloudcmd-archive/raw/master/cloudcmd-v0.1.1.zip)**
|
||||
- *2012.07.09*, **[v0.1.0](//github.com/coderaiser/cloudcmd-archive/raw/master/cloudcmd-v0.1.0.zip)**
|
||||
- *2014.02.13*, **[v0.8.0](//github.com/cloudcmd/archive/raw/master/cloudcmd-v0.8.0.zip)**
|
||||
- *2013.12.09*, **[v0.7.0](//github.com/cloudcmd/archive/raw/master/cloudcmd-v0.7.0.zip)**
|
||||
- *2013.11.08*, **[v0.6.0](//github.com/cloudcmd/archive/raw/master/cloudcmd-v0.6.0.zip)**
|
||||
- *2013.10.17*, **[v0.5.0](//github.com/cloudcmd/archive/raw/master/cloudcmd-v0.5.0.zip)**
|
||||
- *2013.09.27*, **[v0.4.0](//github.com/cloudcmd/archive/raw/master/cloudcmd-v0.4.0.zip)**
|
||||
- *2013.08.01*, **[v0.3.0](//github.com/cloudcmd/archive/raw/master/cloudcmd-v0.3.0.zip)**
|
||||
- *2013.04.22*, **[v0.2.0](//github.com/cloudcmd/archive/raw/master/cloudcmd-v0.2.0.zip)**
|
||||
- *2013.03.01*, **[v0.1.9](//github.com/cloudcmd/archive/raw/master/cloudcmd-v0.1.9.zip)**
|
||||
- *2012.12.12*, **[v0.1.8](//github.com/cloudcmd/archive/raw/master/cloudcmd-v0.1.8.zip)**
|
||||
- *2012.10.01*, **[v0.1.7](//github.com/cloudcmd/archive/raw/master/cloudcmd-v0.1.7.zip)**
|
||||
- *2012.08.24*, **[v0.1.6](//github.com/cloudcmd/archive/raw/master/cloudcmd-v0.1.6.zip)**
|
||||
- *2012.08.06*, **[v0.1.5](//github.com/cloudcmd/archive/raw/master/cloudcmd-v0.1.5.zip)**
|
||||
- *2012.07.27*, **[v0.1.4](//github.com/cloudcmd/archive/raw/master/cloudcmd-v0.1.4.zip)**
|
||||
- *2012.07.19*, **[v0.1.3](//github.com/cloudcmd/archive/raw/master/cloudcmd-v0.1.3.zip)**
|
||||
- *2012.07.14*, **[v0.1.2](//github.com/cloudcmd/archive/raw/master/cloudcmd-v0.1.2.zip)**
|
||||
- *2012.07.11*, **[v0.1.1](//github.com/cloudcmd/archive/raw/master/cloudcmd-v0.1.1.zip)**
|
||||
- *2012.07.09*, **[v0.1.0](//github.com/cloudcmd/archive/raw/master/cloudcmd-v0.1.0.zip)**
|
||||
|
||||
License
|
||||
---------------
|
||||
|
|
|
|||
14
README.md
|
|
@ -1,6 +1,6 @@
|
|||
Cloud Commander v0.7.0 [![NPM version][NPMIMGURL]][NPMURL] [![Dependency Status][DependencyStatusIMGURL]][DependencyStatusURL] [![Build Status][BuildStatusIMGURL]][BuildStatusURL]
|
||||
Cloud Commander v0.8.0 [![NPM version][NPMIMGURL]][NPMURL] [![Dependency Status][DependencyStatusIMGURL]][DependencyStatusURL] [![Build Status][BuildStatusIMGURL]][BuildStatusURL]
|
||||
===============
|
||||
###[Main][MainURL] [Blog][BlogURL] Live(![IO][IO_LIVE_IMG] [IO][IOURL], ![JitSu][JitSu_LIVE_IMG] [JitSu][JitSuURL], ![Heroku][Heroku_LIVE_IMG] [Heroku][HerokuURL] ![RunKite][RunKite_LIVE_IMG] [RunKite][RunKiteURL])
|
||||
###[Main][MainURL] [Blog][BlogURL] Live(![IO][IO_LIVE_IMG] [IO][IOURL], ![JitSu][JitSu_LIVE_IMG] [JitSu][JitSuURL], ![Heroku][Heroku_LIVE_IMG] [Heroku][HerokuURL])
|
||||
[NPMIMGURL]: https://badge.fury.io/js/cloudcmd.png
|
||||
[BuildStatusIMGURL]: https://secure.travis-ci.org/coderaiser/cloudcmd.png?branch=master
|
||||
[DependencyStatusIMGURL]: https://gemnasium.com/coderaiser/cloudcmd.png
|
||||
|
|
@ -15,14 +15,12 @@ Cloud Commander v0.7.0 [![NPM version][NPMIMGURL]][NPMURL] [![Dependency Status]
|
|||
[IOURL]: http://io.cloudcmd.io "IO"
|
||||
[JitSuURL]: http://cloudcmd.jit.su "JitSu"
|
||||
[HerokuURL]: http://cloudcmd.herokuapp.com/ "Heroku"
|
||||
[RunKiteURL]: http://cloudcmd.apps.runkite.com/ "RunKite"
|
||||
[IO_LIVE_IMG]: https://status-ok.cloudcmd.io/host/io.cloudcmd.io/fs?json "IO"
|
||||
[JitSu_LIVE_IMG]: https://status-ok.cloudcmd.io/host/cloudcmd.jit.su/fs?json "JitSu"
|
||||
[HEROKU_LIVE_IMG]: https://status-ok.cloudcmd.io/host/cloudcmd.herokuapp.com/fs?json "Heroku"
|
||||
[RunKite_LIVE_IMG]: https://status-ok.cloudcmd.io/host/cloudcmd.apps.runkite.com/fs?json "RunKite"
|
||||
[IO_LIVE_IMG]: http://status-ok.cloudcmd.io/host/io.cloudcmd.io/fs?json "IO"
|
||||
[JitSu_LIVE_IMG]: http://status-ok.cloudcmd.io/host/cloudcmd.jit.su/fs?json "JitSu"
|
||||
[HEROKU_LIVE_IMG]: http://status-ok.cloudcmd.io/host/cloudcmd.herokuapp.com/fs?json "Heroku"
|
||||
|
||||
**Cloud Commander** - cloud file manager with console and editor.
|
||||
|
||||

|
||||
|
||||
[![Flattr][FlattrIMGURL]][FlattrURL]
|
||||
[![Flattr][FlattrIMGURL]][FlattrURL]
|
||||
|
|
|
|||
153
cloudcmd.js
|
|
@ -29,10 +29,11 @@
|
|||
CERT = DIR + 'ssl/ssl.crt',
|
||||
|
||||
FILE_TMPL = HTMLDIR + 'file.html',
|
||||
PANEL_TMPL = HTMLDIR + 'panel.html',
|
||||
PATH_TMPL = HTMLDIR + 'path.html',
|
||||
LINK_TMPL = HTMLDIR + 'link.html',
|
||||
|
||||
FileTemplate, PathTemplate, LinkTemplate,
|
||||
FileTemplate, PanelTemplate, PathTemplate, LinkTemplate,
|
||||
|
||||
FS = CloudFunc.FS;
|
||||
/* reinit main dir os if we on Win32 should be backslashes */
|
||||
|
|
@ -44,13 +45,15 @@
|
|||
/**
|
||||
* additional processing of index file
|
||||
*/
|
||||
function indexProcessing(pData) {
|
||||
var lPath, lReplace, lKeysPanel,
|
||||
lData = pData.data,
|
||||
lAdditional = pData.additional;
|
||||
function indexProcessing(options) {
|
||||
var keysPanel, left, right,
|
||||
LEFT = CloudFunc.PANEL_LEFT,
|
||||
RIGHT = CloudFunc.PANEL_RIGHT,
|
||||
data = options.data,
|
||||
panel = options.panel;
|
||||
|
||||
if (!Config.appCache)
|
||||
lData = Util.removeStr(lData, [
|
||||
data = Util.removeStr(data, [
|
||||
/* min */
|
||||
' manifest=/cloudcmd.appcache',
|
||||
/* normal */
|
||||
|
|
@ -59,15 +62,27 @@
|
|||
|
||||
if (!Config.showKeysPanel) {
|
||||
lKeysPanel = '<div class="keyspanel';
|
||||
lData = lData.replace(lKeysPanel + '"', lKeysPanel +' hidden"');
|
||||
data = data.replace(keysPanel + '"', keysPanel +' hidden"');
|
||||
}
|
||||
|
||||
lData = Util.render(lData, {
|
||||
title : CloudFunc.getTitle(),
|
||||
fm : lAdditional
|
||||
left = Util.render(PanelTemplate, {
|
||||
id : LEFT,
|
||||
side : 'left',
|
||||
content : panel
|
||||
});
|
||||
|
||||
return lData;
|
||||
right = Util.render(PanelTemplate, {
|
||||
id : RIGHT,
|
||||
side : 'right',
|
||||
content : panel
|
||||
});
|
||||
|
||||
data = Util.render(data, {
|
||||
title : CloudFunc.getTitle(),
|
||||
fm : left + right
|
||||
});
|
||||
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -88,13 +103,6 @@
|
|||
AppCache.createManifest();
|
||||
}
|
||||
|
||||
/**
|
||||
* rest interface
|
||||
* @pParams pConnectionData {request, responce}
|
||||
*/
|
||||
function rest(pConnectionData) {
|
||||
return Util.exec(main.rest, pConnectionData);
|
||||
}
|
||||
|
||||
function init() {
|
||||
var lServerDir, lArg, lParams, lFiles;
|
||||
|
|
@ -136,11 +144,11 @@
|
|||
|
||||
lParams = {
|
||||
appcache : appCacheProcessing,
|
||||
rest : rest,
|
||||
rest : main.rest,
|
||||
route : route
|
||||
},
|
||||
|
||||
lFiles = [FILE_TMPL, PATH_TMPL, LINK_TMPL];
|
||||
lFiles = [FILE_TMPL, PANEL_TMPL, PATH_TMPL, LINK_TMPL];
|
||||
|
||||
if (Config.ssl)
|
||||
lFiles.push(KEY, CERT);
|
||||
|
|
@ -150,6 +158,7 @@
|
|||
Util.log(pErrors);
|
||||
else {
|
||||
FileTemplate = pFiles[FILE_TMPL];
|
||||
PanelTemplate = pFiles[PANEL_TMPL];
|
||||
PathTemplate = pFiles[PATH_TMPL];
|
||||
LinkTemplate = pFiles[LINK_TMPL];
|
||||
|
||||
|
|
@ -164,16 +173,15 @@
|
|||
});
|
||||
}
|
||||
|
||||
function readConfig(pCallBack) {
|
||||
fs.readFile(CONFIG_PATH, function(pError, pData) {
|
||||
var msg, status, str, readed;
|
||||
function readConfig(callback) {
|
||||
fs.readFile(CONFIG_PATH, 'utf8', function(error, data) {
|
||||
var msg, status, readed;
|
||||
|
||||
if (pError)
|
||||
if (error)
|
||||
status = 'error';
|
||||
else {
|
||||
status = 'ok';
|
||||
str = pData.toString(),
|
||||
readed = Util.parseJSON(str);
|
||||
readed = Util.parseJSON(data);
|
||||
|
||||
main.config = Config = readed;
|
||||
}
|
||||
|
|
@ -181,34 +189,42 @@
|
|||
msg = CloudFunc.formatMsg('read', 'config', status);
|
||||
Util.log(msg);
|
||||
|
||||
Util.exec(pCallBack);
|
||||
Util.exec(callback);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* routing of server queries
|
||||
*/
|
||||
function route(pParams) {
|
||||
var lRet = main.checkParams(pParams);
|
||||
function route(request, response, callback) {
|
||||
var ret, name, params, isAuth, isFS;
|
||||
|
||||
if (lRet) {
|
||||
var p = pParams;
|
||||
if (request && response) {
|
||||
ret = true;
|
||||
name = main.getPathName(request);
|
||||
isAuth = Util.strCmp(name, ['/auth', '/auth/github']);
|
||||
isFS = Util.strCmp(name, '/') || Util.isContainStrAtBegin(name, FS);
|
||||
|
||||
if ( Util.strCmp(p.name, ['/auth', '/auth/github']) ) {
|
||||
Util.log('* Routing' +
|
||||
'-> ' + p.name);
|
||||
params = {
|
||||
request : request,
|
||||
response : response,
|
||||
name : name
|
||||
};
|
||||
|
||||
if (isAuth) {
|
||||
Util.log('* Routing' + '-> ' + name);
|
||||
|
||||
pParams.name = main.HTMLDIR + p.name + '.html';
|
||||
lRet = main.sendFile( pParams );
|
||||
params.name = main.HTMLDIR + name + '.html';
|
||||
main.sendFile(params);
|
||||
} else if (isFS)
|
||||
sendContent(params);
|
||||
else {
|
||||
ret = false;
|
||||
Util.exec(callback);
|
||||
}
|
||||
else if ( Util.isContainStrAtBegin(p.name, FS) || Util.strCmp( p.name, '/') )
|
||||
lRet = sendContent( pParams );
|
||||
|
||||
else
|
||||
lRet = false;
|
||||
}
|
||||
|
||||
return lRet;
|
||||
return ret;
|
||||
}
|
||||
|
||||
function sendContent(pParams) {
|
||||
|
|
@ -218,56 +234,53 @@
|
|||
p = pParams;
|
||||
p.name = Util.removeStrOneTime(p.name, CloudFunc.FS) || main.SLASH;
|
||||
|
||||
fs.stat(p.name, function(pError, pStat) {
|
||||
if (!pError)
|
||||
if ( pStat.isDirectory() )
|
||||
fs.stat(p.name, function(error, stat) {
|
||||
if (error)
|
||||
main.sendError(pParams, error);
|
||||
else
|
||||
if (stat.isDirectory())
|
||||
processContent(pParams);
|
||||
else
|
||||
main.sendFile( pParams );
|
||||
else
|
||||
main.sendError(pParams, pError);
|
||||
main.sendFile(pParams);
|
||||
});
|
||||
}
|
||||
|
||||
return lRet;
|
||||
}
|
||||
|
||||
function processContent(pParams) {
|
||||
var p,
|
||||
lRet = main.checkParams(pParams);
|
||||
function processContent(params) {
|
||||
var p = params,
|
||||
ret = main.checkParams(params);
|
||||
|
||||
if (lRet) {
|
||||
p = pParams;
|
||||
main.commander.getDirContent(p.name, function(pError, pJSON) {
|
||||
var lQuery, isJSON;
|
||||
if (ret)
|
||||
main.commander.getDirContent(p.name, function(error, json) {
|
||||
var query, isJSON;
|
||||
|
||||
if (pError)
|
||||
main.sendError(pParams, pError);
|
||||
if (error)
|
||||
main.sendError(params, error);
|
||||
else {
|
||||
lQuery = main.getQuery(p.request);
|
||||
isJSON = Util.isContainStr(lQuery, 'json');
|
||||
query = main.getQuery(p.request);
|
||||
isJSON = Util.isContainStr(query, 'json');
|
||||
|
||||
if (!isJSON)
|
||||
readIndex(pJSON, pParams);
|
||||
readIndex(json, params);
|
||||
else {
|
||||
p.data = Util.stringifyJSON(pJSON);
|
||||
p.data = Util.stringifyJSON(json);
|
||||
p.name +='.json';
|
||||
main.sendResponse(p, null, true);
|
||||
main.sendResponse(params, null, true);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function readIndex(pJSON, params) {
|
||||
var p = params;
|
||||
|
||||
Util.ifExec(!minify, function(params) {
|
||||
Util.ifExec(!Minify, function(params) {
|
||||
var name = params && params.name;
|
||||
|
||||
fs.readFile(name || INDEX_PATH, 'utf8', function(error, template) {
|
||||
var lPanel, lList,
|
||||
var panel,
|
||||
config = main.config,
|
||||
minify = config.minify;
|
||||
|
||||
|
|
@ -275,13 +288,11 @@
|
|||
main.sendError(p, error);
|
||||
else {
|
||||
p.name = INDEX_PATH,
|
||||
lPanel = CloudFunc.buildFromJSON(pJSON, FileTemplate, PathTemplate, LinkTemplate),
|
||||
lList = '<div id=left class=panel>' + lPanel + '</div>' +
|
||||
'<div id=right class=panel>' + lPanel + '</div>';
|
||||
panel = CloudFunc.buildFromJSON(pJSON, FileTemplate, PathTemplate, LinkTemplate),
|
||||
|
||||
main.sendResponse(p, indexProcessing({
|
||||
additional : lList,
|
||||
data : template,
|
||||
panel : panel,
|
||||
data : template,
|
||||
}), true);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
white-space: normal;
|
||||
}
|
||||
|
||||
.list li{
|
||||
.list li {
|
||||
list-style-type: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
-o-user-select: none;
|
||||
user-select: none;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.config .form-control{
|
||||
|
|
|
|||
94
css/icons.css
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
.icon-help:before {
|
||||
font-family : 'Fontello';
|
||||
content : '\e801 ';
|
||||
}
|
||||
|
||||
.icon-rename:before {
|
||||
font-family : 'Fontello';
|
||||
content : '\e802 ';
|
||||
}
|
||||
|
||||
.icon-view:before {
|
||||
font-family : 'Fontello';
|
||||
content : '\e803 ';
|
||||
}
|
||||
|
||||
.icon-edit:before {
|
||||
font-family : 'Fontello';
|
||||
content : '\e804 ';
|
||||
}
|
||||
|
||||
.icon-copy:before {
|
||||
font-family : 'Fontello';
|
||||
content : '\e805 ';
|
||||
}
|
||||
|
||||
.icon-move:before {
|
||||
font-family : 'Fontello';
|
||||
content : '\e806 ';
|
||||
}
|
||||
|
||||
.icon-directory:before {
|
||||
font-family : 'Fontello';
|
||||
content : '\e807 ';
|
||||
}
|
||||
|
||||
.icon-delete:before {
|
||||
font-family : 'Fontello';
|
||||
content : '\e808 ';
|
||||
}
|
||||
|
||||
.icon-menu:before {
|
||||
font-family : 'Fontello';
|
||||
content : '\e809 ';
|
||||
}
|
||||
|
||||
.icon-config:before {
|
||||
font-family : 'Fontello';
|
||||
content : '\e80a ';
|
||||
}
|
||||
|
||||
.icon-console:before {
|
||||
font-family : 'Fontello';
|
||||
content : '\e80b ';
|
||||
}
|
||||
|
||||
.icon-contact:before {
|
||||
font-family : 'Fontello';
|
||||
content : '\e80c ';
|
||||
}
|
||||
|
||||
.icon-file::before {
|
||||
font-family : 'Fontello';
|
||||
content : '\e80d ';
|
||||
}
|
||||
|
||||
.icon-upload-to::before {
|
||||
font-family : 'Fontello';
|
||||
content : '\e80e ';
|
||||
}
|
||||
|
||||
.icon-from-cloud::before {
|
||||
font-family : 'Fontello';
|
||||
content : '\e80f ';
|
||||
}
|
||||
|
||||
.icon-download::before {
|
||||
font-family : 'Fontello';
|
||||
content : '\e810 ';
|
||||
}
|
||||
|
||||
.icon-new::before {
|
||||
font-family : 'Fontello';
|
||||
content : '\e811 ';
|
||||
}
|
||||
|
||||
.icon-unselect-all::before {
|
||||
font-family : 'Fontello';
|
||||
content : '\e812 ';
|
||||
}
|
||||
|
||||
.icon-zip-file::before {
|
||||
font-family : 'Fontello';
|
||||
content : '\e813 ';
|
||||
}
|
||||
|
|
@ -29,44 +29,7 @@ body {
|
|||
Links
|
||||
========================================================================== */
|
||||
|
||||
a{
|
||||
color: #00e;
|
||||
text-decoration:none;
|
||||
}
|
||||
a:visited { color: #551a8b; }
|
||||
a:hover { color: #06e; }
|
||||
a:focus { outline: thin dotted; }
|
||||
|
||||
/* Improve readability when focused and hovered in all browsers: h5bp.com/h */
|
||||
a:hover, a:active { outline: 0; }
|
||||
|
||||
/*
|
||||
* 1. Display hand cursor for clickable form elements
|
||||
* 2. Allow styling of clickable form elements in iOS
|
||||
* 3. Correct inner spacing displayed oddly in IE7 (doesn't effect IE6)
|
||||
*/
|
||||
|
||||
/* ==== non-semantic helper classes ========================================
|
||||
Please define your styles before this section.
|
||||
========================================================================== */
|
||||
|
||||
/* Hide from both screenreaders and browsers: h5bp.com/u */
|
||||
.hidden { display: none !important; visibility: hidden; }
|
||||
|
||||
/* preformatted text */
|
||||
.pre {white-space:pre}
|
||||
|
||||
/* ==== print styles =======================================================
|
||||
Print styles.
|
||||
Inlined to avoid required HTTP connection: h5bp.com/r
|
||||
========================================================================== */
|
||||
|
||||
@media print {
|
||||
* { background: transparent !important;
|
||||
color: black !important; box-shadow:none !important; text-shadow: none !important;
|
||||
filter:none !important; -ms-filter: none !important; } /* Black prints faster: h5bp.com/s */
|
||||
a, a:visited { text-decoration: underline; }
|
||||
a[href]:after { content: " (" attr(href) ")"; }
|
||||
.ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } /* Don't show links for images, or javascript/internal links */
|
||||
@page { margin: 0.5cm; }
|
||||
}
|
||||
|
|
|
|||
184
css/style.css
|
|
@ -21,13 +21,21 @@
|
|||
src: local('Droid Sans Mono'), local('DroidSansMono'), url('//themes.googleusercontent.com/static/fonts/droidsansmono/v4/ns-m2xQYezAtqh7ai59hJUYuTAAIFFn5GTWtryCmBQ4.woff') format('woff');
|
||||
}
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
height: 95%;
|
||||
font:16px "Droid Sans Mono";
|
||||
background-color:white;
|
||||
}
|
||||
|
||||
.fm, #left>li, #right>li, .path, .fm-header,
|
||||
.mini-icon, .name, .size, .owner, .mode, .keyspanel, .cmd-button {
|
||||
.hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.fm, .keyspanel {
|
||||
-webkit-user-select : none;
|
||||
-moz-user-select : none;
|
||||
-ms-user-select : none;
|
||||
|
|
@ -35,6 +43,14 @@ body {
|
|||
user-select : none;
|
||||
}
|
||||
|
||||
.links {
|
||||
-webkit-user-select : initial;
|
||||
-moz-user-select : initial;
|
||||
-ms-user-select : initial;
|
||||
-o-user-select : initial;
|
||||
user-select : text;
|
||||
}
|
||||
|
||||
.path-icon {
|
||||
position : relative;
|
||||
top : 3px;
|
||||
|
|
@ -42,8 +58,6 @@ body {
|
|||
display : inline-block;
|
||||
width : 15px;
|
||||
height : 15px;
|
||||
color : #46A4C3;/*#55BF3F; green*/
|
||||
text-shadow :black 0 2px 1px;
|
||||
}
|
||||
|
||||
.path-icon:hover {
|
||||
|
|
@ -62,11 +76,12 @@ body {
|
|||
|
||||
.error::before {
|
||||
position : relative;
|
||||
left : -15px;
|
||||
font-family : 'Fontello';
|
||||
font-size : 14px;
|
||||
color : rgb(222, 41, 41);
|
||||
cursor : default;
|
||||
content : '\2757';
|
||||
content : '\e800';
|
||||
}
|
||||
|
||||
.loading {
|
||||
|
|
@ -79,7 +94,7 @@ body {
|
|||
}
|
||||
|
||||
.refresh-icon {
|
||||
background:url(/img/panel_refresh.png) no-repeat;
|
||||
background:url(/img/refresh.png) no-repeat;
|
||||
}
|
||||
|
||||
.refresh-icon:active {
|
||||
|
|
@ -87,12 +102,9 @@ body {
|
|||
}
|
||||
|
||||
.cmd-button {
|
||||
width: 9%;
|
||||
width: 5%;
|
||||
margin: 20px 2px 0 2px;
|
||||
overflow: hidden;
|
||||
color: rgb(49,123,249);
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: #222;
|
||||
background-color: white;
|
||||
border: 1.5px solid;
|
||||
border-color: rgb(49,123,249);
|
||||
|
|
@ -101,7 +113,7 @@ body {
|
|||
}
|
||||
|
||||
.cmd-button:hover {
|
||||
border: 1.5px solid rgb(0,0,0);
|
||||
border: 1.5px solid;
|
||||
transition: ease 0.5s;
|
||||
}
|
||||
|
||||
|
|
@ -111,16 +123,20 @@ body {
|
|||
transition: ease 0.1s;
|
||||
}
|
||||
|
||||
.cmd-button:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.clear-storage {
|
||||
margin-right: 6px;
|
||||
margin-left: 7px;
|
||||
background:url(/img/console_clear.png) -4px -4px no-repeat;
|
||||
background:url(/img/clear.png) -4px -4px no-repeat;
|
||||
}
|
||||
.clear-storage:active {
|
||||
top:5px;
|
||||
}
|
||||
|
||||
.links {
|
||||
.links a {
|
||||
color:red;
|
||||
}
|
||||
|
||||
|
|
@ -147,16 +163,14 @@ body {
|
|||
background-image:url('/img/txt.png');
|
||||
}
|
||||
.fm {
|
||||
height: 90%;
|
||||
height: 85%;
|
||||
margin: 26px 26px 0 26px;
|
||||
}
|
||||
.fm-header {
|
||||
font-weight: bold;
|
||||
}
|
||||
#path{
|
||||
margin-left:1.5%;
|
||||
}
|
||||
.left, #left {
|
||||
|
||||
.panel-left {
|
||||
float:left;
|
||||
}
|
||||
|
||||
|
|
@ -169,11 +183,12 @@ body {
|
|||
color: rgb(254,159,224);
|
||||
}
|
||||
|
||||
.right, #right {
|
||||
.panel-right {
|
||||
float:right;
|
||||
}
|
||||
.panel {
|
||||
width: 46%;
|
||||
height: 90%;
|
||||
padding: 20px;
|
||||
margin: 0;
|
||||
border: 1.5px solid;
|
||||
|
|
@ -193,9 +208,10 @@ body {
|
|||
.name {
|
||||
float: left;
|
||||
width: 35%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.name a:hover {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.size {
|
||||
|
|
@ -204,17 +220,35 @@ body {
|
|||
margin-right: 27px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.owner {
|
||||
display : inline-block;
|
||||
width : 14%;
|
||||
/* when inline-block
|
||||
* vertical align should be
|
||||
* set top to prevent additional
|
||||
* spaces behind lines
|
||||
*/
|
||||
vertical-align : top;
|
||||
}
|
||||
|
||||
.mode {
|
||||
float: right;
|
||||
width: 23%;
|
||||
}
|
||||
|
||||
.reduce-text {
|
||||
overflow : hidden;
|
||||
text-overflow : ellipsis;
|
||||
white-space : nowrap;
|
||||
}
|
||||
|
||||
.files {
|
||||
overflow-y: auto;
|
||||
height: 95%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
overflow-y: auto;
|
||||
list-style-type: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
a {
|
||||
|
|
@ -222,7 +256,7 @@ a {
|
|||
}
|
||||
a:hover, a:active {
|
||||
color : #06e;
|
||||
cursor: pointer;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
/* Если размер окна очень маленький
|
||||
|
|
@ -251,23 +285,32 @@ a:hover, a:active {
|
|||
|
||||
/* меняем иконки на шрифтовые*/
|
||||
.mini-icon {
|
||||
color: rgb(246, 224, 124);
|
||||
color: rgba(246, 224, 124, 0.56);
|
||||
font: 60px 'Fontello';
|
||||
width: 40%;
|
||||
height: 0;
|
||||
margin-left: 0;
|
||||
float: right;
|
||||
position: relative;
|
||||
top: 10px
|
||||
color : rgb(246, 224, 124);
|
||||
color : rgba(246, 224, 124, 0.56);
|
||||
font : 16px 'Fontello';
|
||||
width : 6%;
|
||||
margin-left : 10px;
|
||||
float : left;
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.size, .owner, .mode {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.name {
|
||||
float: none;
|
||||
width: 100%;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.directory::before {
|
||||
content: '\1f4c1';
|
||||
content: '\e807';
|
||||
}
|
||||
.text-file::before {
|
||||
color: rgb(26, 224, 124);
|
||||
color: rgba(26, 224, 124, 0.56);
|
||||
content: '\1f4c4';
|
||||
content: '\e80d';
|
||||
}
|
||||
.text-file {
|
||||
background-image:none;
|
||||
|
|
@ -278,43 +321,7 @@ a:hover, a:active {
|
|||
display:none;
|
||||
}
|
||||
|
||||
.mode,.size,.owner {
|
||||
/* располагаем элементы
|
||||
* один под другим
|
||||
*/
|
||||
display: table;
|
||||
float: none;
|
||||
width: 0;
|
||||
|
||||
text-align: left;
|
||||
}
|
||||
/* выводим заголовки рядом с полями */
|
||||
.name::before {
|
||||
content: 'name:';
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
}
|
||||
.mode::before {
|
||||
content: 'mode:';
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
}
|
||||
.size::before {
|
||||
content: 'size:';
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
}
|
||||
.owner::before {
|
||||
content: 'owner:';
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.name {
|
||||
float: none;
|
||||
width:100%;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.cmd-button {
|
||||
width: 20%;
|
||||
|
|
@ -337,7 +344,36 @@ a:hover, a:active {
|
|||
width:94%;
|
||||
}
|
||||
/* если правая панель не помещаеться - прячем её */
|
||||
#right, .cmd-button#f5, .cmd-button#f6 {
|
||||
.panel-right, .cmd-button#f5, .cmd-button#f6 {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
.panel {
|
||||
width: 94%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.keyspanel, .panel-right {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.files {
|
||||
overflow-y: visible;
|
||||
}
|
||||
|
||||
.current-file {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.path-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mini-icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
201
font/LICENSE
|
|
@ -1,201 +0,0 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
186
font/fontello.json
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
{
|
||||
"name": "",
|
||||
"css_prefix_text": "icon-",
|
||||
"css_use_suffix": false,
|
||||
"hinting": true,
|
||||
"units_per_em": 1000,
|
||||
"ascent": 850,
|
||||
"glyphs": [
|
||||
{
|
||||
"uid": "60617c8adc1e7eb3c444a5491dd13f57",
|
||||
"css": "attention-circled-1",
|
||||
"code": 59395,
|
||||
"src": "custom_icons",
|
||||
"selected": false,
|
||||
"svg": {
|
||||
"path": "M429 71Q545 71 644 129T800 285T857 500T800 715T644 871T429 929T213 871T58 715T0 500T58 285T213 129T429 71ZM500 767V661Q500 653 495 648T483 643H376Q368 643 363 648T357 661V767Q357 775 363 780T376 786H483Q490 786 495 780T500 767ZM499 575L509 229Q509 222 503 219Q498 214 490 214H367Q359 214 354 219Q348 222 348 229L358 575Q358 581 363 585T377 589H480Q488 589 493 585T499 575Z",
|
||||
"width": 857.1
|
||||
},
|
||||
"search": [
|
||||
"attention-circled"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "9347a40928618e76958fa67e5333f1c5",
|
||||
"css": "doc-text-1",
|
||||
"code": 59397,
|
||||
"src": "custom_icons",
|
||||
"selected": false,
|
||||
"svg": {
|
||||
"path": "M212 542L212 452L492 452L492 542L212 542ZM600 50Q642 50 671 79T700 150L700 850Q700 890 671 920T600 950L100 950Q60 950 30 920T0 850L0 150Q0 108 30 79T100 50L600 50ZM600 850L600 150L100 150L100 850L600 850ZM490 258L490 346L210 346L210 258L490 258ZM490 650L490 738L210 738L210 650L490 650Z",
|
||||
"width": 700
|
||||
},
|
||||
"search": [
|
||||
"doc-text"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "6405321f27abab4a505476c917d8c748",
|
||||
"css": "folder-1",
|
||||
"code": 59398,
|
||||
"src": "custom_icons",
|
||||
"selected": false,
|
||||
"svg": {
|
||||
"path": "M954 350Q986 350 994 362T1000 398L958 850Q956 874 946 887T904 900L98 900Q46 900 42 850L0 398Q-2 374 6 362T46 350L954 350ZM920 240L930 280L84 280L98 148Q102 128 118 114T154 100L318 100Q370 100 404 134L434 164Q466 200 520 200L860 200Q880 200 898 212T920 240Z",
|
||||
"width": 1001
|
||||
},
|
||||
"search": [
|
||||
"folder"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "2ba2063e74ee7b74f6c46a949d37a130",
|
||||
"css": "cog-2",
|
||||
"code": 59399,
|
||||
"src": "custom_icons",
|
||||
"selected": false,
|
||||
"svg": {
|
||||
"path": "M0 498Q0 460 7 415Q57 419 102 396T172 328Q197 283 195 233T165 142Q229 86 315 56Q338 97 379 122T469 146T558 122T622 56Q707 86 772 142Q751 181 751 227T774 313Q798 354 839 377T926 400Q937 449 937 498Q937 530 930 572Q885 573 846 597T783 660Q758 704 760 752T786 841Q720 901 641 934Q618 892 577 867T487 842Q434 842 391 870T327 944Q245 918 174 862Q202 821 204 771T181 676Q154 630 107 606T9 589Q0 548 0 498ZM293 498Q293 571 344 622T469 673T593 622T644 498T593 374T469 322T344 374T293 498Z",
|
||||
"width": 937.5
|
||||
},
|
||||
"search": [
|
||||
"cog-1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "17ebadd1e3f274ff0205601eef7b9cc4",
|
||||
"css": "help-circled",
|
||||
"code": 59393,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "c5fd349cbd3d23e4ade333789c29c729",
|
||||
"css": "eye",
|
||||
"code": 59395,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "9a76bc135eac17d2c8b8ad4a5774fc87",
|
||||
"css": "download",
|
||||
"code": 59408,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "f5999a012fc3752386635ec02a858447",
|
||||
"css": "download-cloud",
|
||||
"code": 59407,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "de2fc7a5c986ab8c622f63455d7cf814",
|
||||
"css": "upload-cloud",
|
||||
"code": 59406,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "41087bc74d4b20b55059c60a33bf4008",
|
||||
"css": "edit",
|
||||
"code": 59396,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "85528017f1e6053b2253785c31047f44",
|
||||
"css": "comment",
|
||||
"code": 59404,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "b035c28eba2b35c6ffe92aee8b0df507",
|
||||
"css": "attention-circled",
|
||||
"code": 59392,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "f48ae54adfb27d8ada53d0fd9e34ee10",
|
||||
"css": "trash",
|
||||
"code": 59400,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "c8585e1e5b0467f28b70bce765d5840c",
|
||||
"css": "docs",
|
||||
"code": 59397,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "559647a6f430b3aeadbecd67194451dd",
|
||||
"css": "menu",
|
||||
"code": 59401,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "3c24ee33c9487bbf18796ca6dffa1905",
|
||||
"css": "resize-small",
|
||||
"code": 59411,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "6020aff067fc3c119cdd75daa5249220",
|
||||
"css": "exchange",
|
||||
"code": 59398,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "823a9e02e643318116fea40a00190e4e",
|
||||
"css": "asterisk",
|
||||
"code": 59410,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "fa10777b2d88cc64cd6e4f26ef0e5264",
|
||||
"css": "terminal",
|
||||
"code": 59403,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "1189604bf305b6b03a74685ce60e6632",
|
||||
"css": "doc-text",
|
||||
"code": 59405,
|
||||
"src": "entypo"
|
||||
},
|
||||
{
|
||||
"uid": "0ccb084ddeeae372673793ed0b45bb4a",
|
||||
"css": "folder",
|
||||
"code": 59399,
|
||||
"src": "entypo"
|
||||
},
|
||||
{
|
||||
"uid": "67f793f91864e379458a92ccc61d9ccf",
|
||||
"css": "sort-alphabet",
|
||||
"code": 59394,
|
||||
"src": "typicons"
|
||||
},
|
||||
{
|
||||
"uid": "7336247ba3db350dec8d6c0a47cef966",
|
||||
"css": "cog-1",
|
||||
"code": 59402,
|
||||
"src": "mfglabs"
|
||||
},
|
||||
{
|
||||
"uid": "9e0404ba55575a540164db9a5ad511df",
|
||||
"css": "doc-new",
|
||||
"code": 59409,
|
||||
"src": "elusive"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,40 +1,31 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>
|
||||
Created by FontForge 20100429 at Thu Jan 24 11:50:44 2013
|
||||
By root
|
||||
Copyright (C) 2012 by original authors @ fontello.com
|
||||
</metadata>
|
||||
<metadata>Copyright (C) 2013 by original authors @ fontello.com</metadata>
|
||||
<defs>
|
||||
<font id="cloudcmd" horiz-adv-x="364" >
|
||||
<font-face
|
||||
font-family="cloudcmd"
|
||||
font-weight="500"
|
||||
font-stretch="normal"
|
||||
units-per-em="1000"
|
||||
panose-1="2 0 6 3 0 0 0 0 0 0"
|
||||
ascent="850"
|
||||
descent="-150"
|
||||
bbox="14.6522 -100 1015.35 800"
|
||||
underline-thickness="50"
|
||||
underline-position="-100"
|
||||
unicode-range="U+2757-1F4C4"
|
||||
/>
|
||||
<missing-glyph
|
||||
d="M33 0v666h265v-666h-265zM66 33h199v600h-199v-600z" />
|
||||
<glyph glyph-name=".notdef"
|
||||
d="M33 0v666h265v-666h-265zM66 33h199v600h-199v-600z" />
|
||||
<glyph glyph-name=".null" horiz-adv-x="0"
|
||||
/>
|
||||
<glyph glyph-name="nonmarkingreturn" horiz-adv-x="333"
|
||||
/>
|
||||
<glyph glyph-name="uni2757" unicode="❗" horiz-adv-x="887"
|
||||
d="M15 350q0 123 60 219.5t153 151.5q101 58 216 58q123 0 219.5 -60t151.5 -154q57 -99 57 -215q0 -123 -60 -219.5t-153 -151.5q-101 -58 -215 -58q-123 0 -220 60t-152 154q-57 99 -57 215zM382 636q-7 0 -13 -4.5t-6 -10.5l10 -346q0 -6 6 -10t13 -4h103q17 0 19 14
|
||||
l10 346q0 6 -6 10.5t-13 4.5h-123zM372 83q0 -7 6 -13t13 -6h107q7 0 12 5.5t5 13.5v106q0 18 -17 18h-107q-8 0 -13.5 -5.5t-5.5 -12.5v-106z" />
|
||||
<glyph glyph-name="u1F4C1" unicode="📁" horiz-adv-x="1031"
|
||||
d="M969 500q29 0 38.5 -10.5t7.5 -37.5l-42 -452q-2 -27 -13.5 -38.5t-40.5 -11.5h-806q-51 0 -56 50l-42 452q-2 27 7.5 37.5t38.5 10.5h908zM935 610l10 -40h-846l14 132q4 19 20 33.5t36 14.5h164q52 0 86 -34l30 -30q31 -36 86 -36h340q20 0 38 -12t22 -28z" />
|
||||
<glyph glyph-name="u1F4C4" unicode="📄" horiz-adv-x="730"
|
||||
d="M227 398h280v-90h-280v90zM615 800q42 0 71 -29t29 -71v-700q0 -41 -29.5 -70.5t-70.5 -29.5h-500q-40 0 -70 30t-30 70v700q0 41 29.5 70.5t70.5 29.5h500zM615 700h-500v-700h500v700zM505 504h-280v88h280v-88zM505 200v-88h-280v88h280z" />
|
||||
</font>
|
||||
</defs></svg>
|
||||
<font id="fontello" horiz-adv-x="1000" >
|
||||
<font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
|
||||
<missing-glyph horiz-adv-x="1000" />
|
||||
<glyph glyph-name="help-circled" unicode="" d="m500 82v107q0 8-5 13t-13 5h-107q-8 0-13-5t-5-13v-107q0-8 5-13t13-5h107q8 0 13 5t5 13z m143 375q0 49-31 91t-77 65t-95 23q-136 0-207-119q-9-14 4-24l74-55q4-4 10-4q9 0 14 7q30 38 48 51q19 14 48 14q27 0 48-15t21-33q0-21-11-34t-38-25q-35-16-65-48t-29-70v-20q0-8 5-13t13-5h107q8 0 13 5t5 13q0 10 12 27t30 28q18 10 28 16t25 19t25 27t16 34t7 45z m214-107q0-117-57-215t-156-156t-215-58t-216 58t-155 156t-58 215t58 215t155 156t216 58t215-58t156-156t57-215z" horiz-adv-x="857.1" />
|
||||
<glyph glyph-name="doc-new" unicode="" d="m0-150l0 818l188 182l519 0l0-348l-86 0l0 260l-369 0l0-156l-166 0l0-668l418 0l0-88l-504 0z m373 207l0 162l209 0l0 207l160 0l0-207l207 0l0-162l-207 0l0-207l-160 0l0 207l-209 0z" horiz-adv-x="949" />
|
||||
<glyph glyph-name="download" unicode="" d="m714 100q0 15-10 25t-25 11t-26-11t-10-25t10-25t26-11t25 11t10 25z m143 0q0 15-10 25t-26 11t-25-11t-10-25t10-25t25-11t26 11t10 25z m72 125v-179q0-22-16-37t-38-16h-821q-23 0-38 16t-16 37v179q0 22 16 38t38 16h259l75-76q33-32 76-32t76 32l76 76h259q22 0 38-16t16-38z m-182 318q10-23-8-40l-250-250q-10-10-25-10t-25 10l-250 250q-17 17-8 40q10 21 33 21h143v250q0 15 11 25t25 11h143q14 0 25-11t10-25v-250h143q24 0 33-21z" horiz-adv-x="928.6" />
|
||||
<glyph glyph-name="download-cloud" unicode="" d="m714 332q0 8-5 13t-13 5h-125v196q0 8-5 13t-12 5h-108q-7 0-12-5t-5-13v-196h-125q-8 0-13-5t-5-13q0-8 5-13l196-196q5-5 13-5t13 5l196 196q5 6 5 13z m357-125q0-89-62-151t-152-63h-607q-103 0-177 73t-73 177q0 72 39 134t105 92q-1 17-1 24q0 118 84 202t202 84q87 0 159-49t105-129q40 35 93 35q59 0 101-42t42-101q0-43-23-77q72-17 119-76t46-133z" horiz-adv-x="1071.4" />
|
||||
<glyph glyph-name="upload-cloud" unicode="" d="m714 368q0 8-5 13l-196 196q-5 5-13 5t-13-5l-196-196q-5-6-5-13q0-8 5-13t13-5h125v-196q0-8 5-13t12-5h108q7 0 12 5t5 13v196h125q8 0 13 5t5 13z m357-161q0-89-62-151t-152-63h-607q-103 0-177 73t-73 177q0 72 39 134t105 92q-1 17-1 24q0 118 84 202t202 84q87 0 159-49t105-129q40 35 93 35q59 0 101-42t42-101q0-43-23-77q72-17 119-76t46-133z" horiz-adv-x="1071.4" />
|
||||
<glyph glyph-name="edit" unicode="" d="m496 189l64 65l-85 85l-64-65v-31h53v-54h32z m245 402q-9 9-18 0l-196-196q-9-9 0-18t18 0l196 196q9 9 0 18z m45-331v-106q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h464q35 0 65-14q9-4 10-13q2-10-5-16l-27-28q-8-8-18-4q-13 3-25 3h-464q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v70q0 7 5 12l36 36q8 8 20 4t11-16z m-54 411l161-160l-375-375h-161v160z m248-73l-51-52l-161 161l51 51q16 16 38 16t38-16l85-84q16-16 16-38t-16-38z" horiz-adv-x="1000" />
|
||||
<glyph glyph-name="comment" unicode="" d="m1000 350q0-97-67-179t-182-130t-251-48q-39 0-81 4q-110-97-257-135q-27-8-63-12q-10-1-17 5t-10 16v1q-2 2 0 6t1 6t2 5l4 5t4 5t4 5q4 5 17 19t20 22t17 22t18 28t15 33t15 42q-88 50-138 123t-51 157q0 73 40 139t106 114t160 76t194 28q136 0 251-48t182-130t67-179z" horiz-adv-x="1000" />
|
||||
<glyph glyph-name="attention-circled" unicode="" d="m429 779q116 0 215-58t156-156t57-215t-57-215t-156-156t-215-58t-216 58t-155 156t-58 215t58 215t155 156t216 58z m71-696v106q0 8-5 13t-12 5h-107q-8 0-13-5t-6-13v-106q0-8 6-13t13-6h107q7 0 12 6t5 13z m-1 192l10 346q0 7-6 10q-5 5-13 5h-123q-8 0-13-5q-6-3-6-10l10-346q0-6 5-10t14-4h103q8 0 13 4t6 10z" horiz-adv-x="857.1" />
|
||||
<glyph glyph-name="trash" unicode="" d="m286 439v-321q0-8-5-13t-13-5h-36q-8 0-13 5t-5 13v321q0 8 5 13t13 5h36q8 0 13-5t5-13z m143 0v-321q0-8-5-13t-13-5h-36q-8 0-13 5t-5 13v321q0 8 5 13t13 5h36q8 0 13-5t5-13z m142 0v-321q0-8-5-13t-12-5h-36q-8 0-13 5t-5 13v321q0 8 5 13t13 5h36q7 0 12-5t5-13z m72-404v529h-500v-529q0-12 4-22t8-15t6-5h464q2 0 6 5t8 15t4 22z m-375 601h250l-27 65q-4 5-9 6h-177q-6-1-10-6z m518-18v-36q0-8-5-13t-13-5h-54v-529q0-46-26-80t-63-34h-464q-37 0-63 33t-27 79v531h-53q-8 0-13 5t-5 13v36q0 8 5 13t13 5h172l39 93q9 21 31 35t44 15h178q22 0 44-15t30-35l39-93h173q8 0 13-5t5-13z" horiz-adv-x="785.7" />
|
||||
<glyph glyph-name="docs" unicode="" d="m946 636q23 0 38-16t16-38v-678q0-23-16-38t-38-16h-535q-23 0-38 16t-16 38v160h-303q-23 0-38 16t-16 38v375q0 22 11 49t27 42l228 228q15 16 42 27t49 11h232q23 0 38-16t16-38v-183q38 23 71 23h232z m-303-119l-167-167h167v167z m-357 214l-167-167h167v167z m109-361l176 176v233h-214v-233q0-22-15-38t-38-15h-233v-357h286v143q0 22 11 49t27 42z m534-449v643h-215v-232q0-22-15-38t-38-15h-232v-358h500z" horiz-adv-x="1000" />
|
||||
<glyph glyph-name="eye" unicode="" d="m929 314q-85 132-213 197q34-58 34-125q0-104-73-177t-177-73t-177 73t-73 177q0 67 34 125q-128-65-213-197q75-114 187-182t242-68t242 68t187 182z m-402 215q0 11-8 19t-19 7q-70 0-120-50t-50-119q0-12 8-19t19-8t19 8t8 19q0 48 34 82t82 34q11 0 19 8t8 19z m473-215q0-19-11-38q-78-129-210-206t-279-77t-279 77t-210 206q-11 19-11 38t11 39q78 128 210 205t279 78t279-78t210-205q11-20 11-39z" horiz-adv-x="1000" />
|
||||
<glyph glyph-name="resize-small" unicode="" d="m429 314v-250q0-14-11-25t-25-10t-25 10l-81 81l-185-186q-5-5-13-5t-13 5l-63 64q-6 5-6 13t6 13l185 185l-80 80q-11 11-11 25t11 25t25 11h250q14 0 25-11t11-25z m421 375q0-7-6-13l-185-185l80-80q11-11 11-25t-11-25t-25-11h-250q-14 0-25 11t-10 25v250q0 14 10 25t25 10t25-10l81-81l185 186q6 5 13 5t13-5l63-64q6-5 6-13z" horiz-adv-x="857.1" />
|
||||
<glyph glyph-name="exchange" unicode="" d="m1000 189v-107q0-7-5-12t-13-6h-768v-107q0-7-5-12t-13-6q-6 0-13 6l-178 178q-5 5-5 13q0 8 5 13l179 178q5 5 12 5q8 0 13-5t5-13v-107h768q7 0 13-5t5-13z m0 304q0-8-5-13l-179-179q-5-5-12-5q-8 0-13 6t-5 12v107h-768q-7 0-13 6t-5 12v107q0 8 5 13t13 5h768v107q0 8 5 13t13 5q6 0 13-5l178-178q5-5 5-13z" horiz-adv-x="1000" />
|
||||
<glyph glyph-name="asterisk" unicode="" d="m827 264q26-14 33-43t-7-55l-35-61q-15-26-44-33t-54 7l-149 85v-171q0-29-21-50t-50-22h-71q-29 0-51 22t-21 50v171l-148-85q-26-15-55-7t-43 33l-36 61q-14 26-7 55t34 43l148 86l-148 86q-26 14-34 43t7 55l36 61q15 26 43 33t55-7l148-85v171q0 29 21 50t51 22h71q29 0 50-22t21-50v-171l149 85q26 15 54 7t44-33l35-62q15-25 7-54t-33-43l-148-86z" horiz-adv-x="928.6" />
|
||||
<glyph glyph-name="terminal" unicode="" d="m326 301l-260-260q-5-5-12-5t-13 5l-28 28q-6 6-6 13t6 13l219 219l-219 220q-6 5-6 12t6 13l28 28q5 6 13 6t12-6l260-260q6-5 6-13t-6-13z m603-255v-35q0-8-5-13t-13-5h-536q-8 0-13 5t-5 13v35q0 8 5 13t13 5h536q8 0 13-5t5-13z" horiz-adv-x="928.6" />
|
||||
<glyph glyph-name="doc-text" unicode="" d="m212 308l0 90l280 0l0-90l-280 0z m388 492q42 0 71-29t29-71l0-700q0-40-29-70t-71-30l-500 0q-40 0-70 30t-30 70l0 700q0 42 30 71t70 29l500 0z m0-800l0 700l-500 0l0-700l500 0z m-110 592l0-88l-280 0l0 88l280 0z m0-392l0-88l-280 0l0 88l280 0z" horiz-adv-x="700" />
|
||||
<glyph glyph-name="folder" unicode="" d="m954 500q32 0 40-12t6-36l-42-452q-2-24-12-37t-42-13l-806 0q-52 0-56 50l-42 452q-2 24 6 36t40 12l908 0z m-34 110l10-40l-846 0l14 132q4 20 20 34t36 14l164 0q52 0 86-34l30-30q32-36 86-36l340 0q20 0 38-12t22-28z" horiz-adv-x="1001" />
|
||||
<glyph glyph-name="sort-alphabet" unicode="" d="m516 165q12-25-2-50t-44-25q-33 0-47 29l-37 75l-249 0l-37-75q-9-20-30-27t-40 3q-20 9-27 29t3 41l209 417q13 27 47 27t46-27z m-326 134l143 0l-72 143z m905-209l-313 0q-30 0-45 27t4 57l250 332l-209 0q-21 0-37 16t-15 37t15 36t37 16l313 0q30 0 45-28t-4-56l-250-333l209 0q21 0 37-15t15-37t-15-37t-37-15z m-417 209l-105 0q-21 0-36 15t-15 36t15 37t36 15l105 0q22 0 37-15t15-37t-15-36t-37-15z" horiz-adv-x="1147" />
|
||||
<glyph glyph-name="cog-1" unicode="" d="m0 352q0 38 7 83q50-4 95 19t70 68q25 45 23 95t-30 91q64 56 150 86q23-41 64-66t90-24t89 24t64 66q85-30 150-86q-21-39-21-85t23-86q24-41 65-64t87-23q11-49 11-98q0-32-7-74q-45-1-84-25t-63-63q-25-44-23-92t26-89q-66-60-145-93q-23 42-64 67t-90 25q-53 0-96-28t-64-74q-82 26-153 82q28 41 30 91t-23 95q-27 46-74 70t-98 17q-9 41-9 91z m293 0q0-73 51-124t125-51t124 51t51 124t-51 124t-124 52t-125-52t-51-124z" horiz-adv-x="937.5" />
|
||||
<glyph glyph-name="menu" unicode="" d="m857 100v-71q0-15-10-25t-26-11h-785q-15 0-25 11t-11 25v71q0 15 11 25t25 11h785q15 0 26-11t10-25z m0 286v-72q0-14-10-25t-26-10h-785q-15 0-25 10t-11 25v72q0 14 11 25t25 10h785q15 0 26-10t10-25z m0 285v-71q0-15-10-25t-26-11h-785q-15 0-25 11t-11 25v71q0 15 11 26t25 10h785q15 0 26-10t10-26z" horiz-adv-x="857.1" />
|
||||
</font>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 8.5 KiB |
|
|
@ -6,9 +6,11 @@
|
|||
(function(){
|
||||
'use strict';
|
||||
|
||||
if(window.opener){
|
||||
var lGitHub = window.opener.CloudCommander.GitHub;
|
||||
lGitHub.autorize(lGitHub.callback, window.location.search);
|
||||
var opener = window.opener;
|
||||
|
||||
if (opener){
|
||||
var GitHub = opener.CloudCmd.GitHub;
|
||||
GitHub.autorize(GitHub.callback, window.location.search);
|
||||
window.close();
|
||||
}
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -3,18 +3,19 @@
|
|||
<head></head>
|
||||
<body>
|
||||
<script>
|
||||
(function(){
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var lOpener = window.opener;
|
||||
if(lOpener){
|
||||
var CloudCmd = lOpener.CloudCmd;
|
||||
var opener = window.opener;
|
||||
|
||||
if (opener) {
|
||||
var CloudCmd = opener.CloudCmd;
|
||||
|
||||
CloudCmd.getModules(function(pModules){
|
||||
var Util = lOpener.Util,
|
||||
lStorage = Util.findObjByNameInArr(pModules, 'storage'),
|
||||
lGitHub = Util.findObjByNameInArr(lStorage, 'GitHub'),
|
||||
GitHub_ID = lGitHub && lGitHub.key;
|
||||
CloudCmd.getModules(function(pModules) {
|
||||
var Util = opener.Util,
|
||||
Storage = Util.findObjByNameInArr(pModules, 'storage'),
|
||||
GitHub = Util.findObjByNameInArr(Storage, 'GitHub'),
|
||||
GitHub_ID = GitHub && GitHub.key;
|
||||
|
||||
window.location =
|
||||
'https://github.com/login/oauth/authorize?client_id=' +
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
<div class="border">
|
||||
<ul class="list" >
|
||||
<li><span><input type="checkbox" id="auth" {{ auth }} ></input></span><label for="auth"> Auth</label></li>
|
||||
<li><span><input type="text" class="form-control" placeholder="username" id="username" value="{{ username }}"></input></span></li>
|
||||
<li><span><input type="password" class="form-control" placeholder="password" id="password" value="{{ password }}" ></input></span></li>
|
||||
<li><span><input type="checkbox" id="appCache" {{ appCache }} ></input></span><label for="appCache"> App cache</label></li>
|
||||
<li><span><input type="checkbox" id="analytics" {{ analytics }} ></input></span><label for="analytics"> Analytics</label></li>
|
||||
<li><span><input type="checkbox" id="diff" {{ diff }} ></input></span><label for="diff"> Diff</label></li>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<li draggable class="{{ className }}">
|
||||
<span class="mini-icon {{ type }}"></span>
|
||||
<span class=name>{{ name }}</span>
|
||||
<span class=size>{{ size }}</span><span class=owner>{{ owner }}</span>
|
||||
<span class=mode>{{ mode }}</span>
|
||||
<span class="name reduce-text">{{ name }}</span>
|
||||
<span class="size reduce-text">{{ size }}</span>
|
||||
<span class="owner reduce-text">{{ owner }}</span>
|
||||
<span class="mode reduce-text">{{ mode }}</span>
|
||||
</li>
|
||||
|
|
@ -10,41 +10,45 @@
|
|||
<link href=/img/favicon/favicon.png rel=icon type=image/png />
|
||||
<title>{{ title }}</title>
|
||||
|
||||
<link rel=stylesheet href=/combine/css/reset.css:css/style.css>
|
||||
<link rel=stylesheet href=/join/css/reset.css:css/style.css:css/icons.css>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class=fm>{{ fm }}</div>
|
||||
<div class="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 - delete</button>
|
||||
<button id=f9 class=cmd-button>F9 - menu</button>
|
||||
<button id=f10 class=cmd-button>F10 - config</button>
|
||||
<button id=~ class=cmd-button>~ - console</button>
|
||||
<button id=f1 class="cmd-button reduce-text icon-help" title="help">F1</button>
|
||||
<button id=f2 class="cmd-button reduce-text icon-rename" title="rename">F2</button>
|
||||
<button id=f3 class="cmd-button reduce-text icon-view" title="view">F3</button>
|
||||
<button id=f4 class="cmd-button reduce-text icon-edit " title="edit">F4</button>
|
||||
<button id=f5 class="cmd-button reduce-text icon-copy" title="copy">F5</button>
|
||||
<button id=f6 class="cmd-button reduce-text icon-move" title="move">F6</button>
|
||||
<button id=f7 class="cmd-button reduce-text icon-directory" title="make directory">F7</button>
|
||||
<button id=f8 class="cmd-button reduce-text icon-delete" title="delete">F8</button>
|
||||
<button id=f9 class="cmd-button reduce-text icon-menu" title="menu">F9</button>
|
||||
<button id=f10 class="cmd-button reduce-text icon-config" title="config">F10</button>
|
||||
<button id=~ class="cmd-button reduce-text icon-console" title="console">~</button>
|
||||
<button id=contact class="cmd-button reduce-text icon-contact" title="contact"></button>
|
||||
</div>
|
||||
<script>
|
||||
(function() {
|
||||
!(function() {
|
||||
'use strict';
|
||||
|
||||
var script, Height,
|
||||
var script,
|
||||
lib = 'lib/',
|
||||
client = lib + 'client/',
|
||||
files = [
|
||||
'lib/client/key.js',
|
||||
'lib/client/listeners.js',
|
||||
'lib/client.js',
|
||||
'lib/client/dom.js',
|
||||
'lib/cloudfunc.js',
|
||||
'lib/util.js'
|
||||
lib + 'util.js',
|
||||
lib + 'cloudfunc.js',
|
||||
client + 'dom.js',
|
||||
client + 'loader.js',
|
||||
client + 'notify.js',
|
||||
lib + 'client.js',
|
||||
client + 'listeners.js',
|
||||
client + 'key.js'
|
||||
],
|
||||
url = getCombineURL(files);
|
||||
url = getJoinURL(files);
|
||||
|
||||
window.addEventListener('load', createScript);
|
||||
setPanelHeight();
|
||||
|
||||
function createScript() {
|
||||
script = document.createElement('script');
|
||||
|
|
@ -55,9 +59,9 @@
|
|||
window.removeEventListener('load', createScript);
|
||||
}
|
||||
|
||||
function getCombineURL(files) {
|
||||
function getJoinURL(files) {
|
||||
var regExp = new RegExp(',', 'g'),
|
||||
url = '/combine/' + files;
|
||||
url = '/join/' + files;
|
||||
|
||||
url = url.replace(regExp, ':');
|
||||
|
||||
|
|
@ -68,29 +72,6 @@
|
|||
CloudCmd.init();
|
||||
script.removeEventListener('load', scriptLoad);
|
||||
}
|
||||
|
||||
function setPanelHeight() {
|
||||
var style;
|
||||
/* устанавливаем размер высоты таблицы файлов
|
||||
* исходя из размеров разрешения экрана
|
||||
*
|
||||
* формируем и округляем высоту экрана
|
||||
* при разрешениии 1024x1280:
|
||||
* 658 -> 700
|
||||
*/
|
||||
|
||||
Height = window.screen.height;
|
||||
Height -= (Height / 3).toFixed();
|
||||
Height = (Height / 100).toFixed() * 100;
|
||||
|
||||
style = document.createElement('style');
|
||||
style.innerText = '.files {' +
|
||||
'height:' + Height +'px;' +
|
||||
'}';
|
||||
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
|||
1
html/panel.html
Normal file
|
|
@ -0,0 +1 @@
|
|||
<div id="{{ id }}" class="panel panel-{{ side }}">{{ content }}</div>
|
||||
|
|
@ -1 +1 @@
|
|||
<div class=path><span class="path-icon clear-storage" id=clear-storage title="clear storage (Ctrl+D)"></span><span class="path-icon refresh-icon" title="refresh (Ctrl+R)"><a href="{{ link }}"></a></span><span>{{ path }}</span></div>
|
||||
<div id="js-path" class="reduce-text" title="{{ fullPath }}"><span class="path-icon clear-storage" title="clear storage (Ctrl+D)"></span><span class="path-icon refresh-icon" title="refresh (Ctrl+R)"><a href="{{ link }}"></a></span><span class=links>{{ path }}</span></div>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 872 B After Width: | Height: | Size: 872 B |
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 41 KiB |
|
|
@ -1,4 +1,7 @@
|
|||
{
|
||||
"auth": false,
|
||||
"username": "root",
|
||||
"password": "435b41068e8665513a20070c033b08b9c66e4332",
|
||||
"apiURL": "/api/v1",
|
||||
"appСache": false,
|
||||
"analytics": true,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@
|
|||
"view",
|
||||
"help",
|
||||
"config",
|
||||
"contact",
|
||||
"socket",
|
||||
"terminal",
|
||||
"console", {
|
||||
"name": "storage",
|
||||
"data": [{
|
||||
|
|
|
|||
226
lib/client.js
|
|
@ -1,18 +1,19 @@
|
|||
var Util, DOM, CloudFunc;
|
||||
/* Функция которая возвратит обьект CloudCmd
|
||||
* @CloudFunc - обьект содержащий общий функционал
|
||||
* клиентский и серверный
|
||||
*/
|
||||
var Util, DOM, CloudFunc, CloudCmd;
|
||||
|
||||
(function(scope, Util, DOM) {
|
||||
(function(scope, Util, DOM, CloudFunc) {
|
||||
'use strict';
|
||||
|
||||
scope.CloudCmd = new CloudCmdProto(Util, DOM);
|
||||
scope.CloudCmd = new CloudCmdProto(Util, DOM, CloudFunc);
|
||||
|
||||
function CloudCmdProto(Util, DOM) {
|
||||
function CloudCmdProto(Util, DOM, CloudFunc) {
|
||||
var Key, Config, Modules, Extensions,
|
||||
FileTemplate, PathTemplate, LinkTemplate, Listeners,
|
||||
CloudCmd = this;
|
||||
Images = DOM.Images,
|
||||
Info = DOM.CurrentInfo,
|
||||
CloudCmd = this,
|
||||
Storage = DOM.Storage;
|
||||
|
||||
this.LIBDIR = '/lib/';
|
||||
|
|
@ -45,7 +46,7 @@ var Util, DOM, CloudFunc, CloudCmd;
|
|||
lLink += '?json';
|
||||
|
||||
if (lLink || lCurrentLink.target !== '_blank') {
|
||||
DOM.Images.showLoad(pNeedRefresh ? {top:true} : null);
|
||||
Images.showLoad(pNeedRefresh ? {top:true} : null);
|
||||
|
||||
/* загружаем содержимое каталога */
|
||||
CloudCmd.ajaxLoad(lLink, { refresh: pNeedRefresh });
|
||||
|
|
@ -79,34 +80,40 @@ var Util, DOM, CloudFunc, CloudCmd;
|
|||
* @pParams = {name, path, func, dobefore, arg}
|
||||
*/
|
||||
function loadModule(pParams) {
|
||||
var lName, lPath, lFunc, lDoBefore, lSlash, lAfterSlash,
|
||||
isContain;
|
||||
|
||||
if (pParams) {
|
||||
var lName = pParams.name,
|
||||
lPath = pParams.path,
|
||||
lFunc = pParams.func,
|
||||
lDoBefore = pParams.dobefore;
|
||||
lName = pParams.name,
|
||||
lPath = pParams.path,
|
||||
lFunc = pParams.func,
|
||||
lDoBefore = pParams.dobefore;
|
||||
|
||||
if ( Util.isString(pParams) )
|
||||
if (Util.isString(pParams))
|
||||
lPath = pParams;
|
||||
|
||||
if (lPath && !lName) {
|
||||
lName = Util.getStrBigFirst(lPath);
|
||||
lName = Util.removeStr(lName, '.js');
|
||||
|
||||
var lSlash = lName.indexOf('/');
|
||||
lSlash = lName.indexOf('/');
|
||||
if (lSlash > 0) {
|
||||
var lAfterSlash = lName.substr(lSlash);
|
||||
lAfterSlash = lName.substr(lSlash);
|
||||
lName = Util.removeStr(lName, lAfterSlash);
|
||||
}
|
||||
}
|
||||
|
||||
if ( !Util.isContainStr(lPath, '.js') )
|
||||
isContain = Util.isContainStr(lPath, '.js');
|
||||
if (!isContain)
|
||||
lPath += '.js';
|
||||
|
||||
if (!CloudCmd[lName])
|
||||
if (!CloudCmd[lName]) {
|
||||
CloudCmd[lName] = function(pArg) {
|
||||
var path = CloudCmd.LIBDIRCLIENT + lPath;
|
||||
|
||||
Util.exec(lDoBefore);
|
||||
|
||||
return DOM.jsload(CloudCmd.LIBDIRCLIENT + lPath, lFunc ||
|
||||
return DOM.jsload(path, lFunc ||
|
||||
function() {
|
||||
var Proto = CloudCmd[lName];
|
||||
|
||||
|
|
@ -114,6 +121,9 @@ var Util, DOM, CloudFunc, CloudCmd;
|
|||
CloudCmd[lName] = new Proto(pArg);
|
||||
});
|
||||
};
|
||||
|
||||
CloudCmd[lName].show = CloudCmd[lName];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -126,9 +136,9 @@ var Util, DOM, CloudFunc, CloudCmd;
|
|||
|
||||
lCallBack = function() {
|
||||
Util.loadOnLoad([
|
||||
Util.retFunc(CloudCmd.route, location.hash),
|
||||
baseInit,
|
||||
initModules,
|
||||
baseInit,
|
||||
Util.bind(CloudCmd.route, location.hash)
|
||||
]);
|
||||
},
|
||||
lFunc = function(pCallBack) {
|
||||
|
|
@ -178,45 +188,47 @@ var Util, DOM, CloudFunc, CloudCmd;
|
|||
});
|
||||
|
||||
CloudCmd.getModules(function(pModules) {
|
||||
pModules = pModules || [];
|
||||
pModules = pModules || [];
|
||||
|
||||
var i, n, lStorage = 'storage',
|
||||
lShowLoadFunc = Util.retFunc( DOM.Images.showLoad, {top:true} ),
|
||||
var i, n, module, storageObj, mod, name, path,
|
||||
STORAGE = 'storage',
|
||||
showLoadFunc = Util.bind(Images.showLoad, {
|
||||
top:true
|
||||
}),
|
||||
|
||||
lDoBefore = {
|
||||
'edit' : lShowLoadFunc,
|
||||
'view' : lShowLoadFunc,
|
||||
'menu' : lShowLoadFunc
|
||||
doBefore = {
|
||||
'edit' : showLoadFunc,
|
||||
'view' : showLoadFunc,
|
||||
'menu' : showLoadFunc
|
||||
},
|
||||
|
||||
lLoad = function(pName, pPath, pDoBefore) {
|
||||
load = function(name, path, func) {
|
||||
loadModule({
|
||||
path : pPath,
|
||||
name : pName,
|
||||
dobefore : pDoBefore
|
||||
name : name,
|
||||
path : path,
|
||||
dobefore : func
|
||||
});
|
||||
};
|
||||
|
||||
for (i = 0, n = pModules.length; i < n ; i++) {
|
||||
var lModule = pModules[i];
|
||||
module = pModules[i];
|
||||
|
||||
if ( Util.isString(lModule) )
|
||||
lLoad(null, lModule, lDoBefore[lModule]);
|
||||
if (Util.isString(module))
|
||||
load(null, module, doBefore[module]);
|
||||
}
|
||||
|
||||
var lStorageObj = Util.findObjByNameInArr( pModules, lStorage ),
|
||||
lMod = Util.getNamesFromObjArray( lStorageObj );
|
||||
storageObj = Util.findObjByNameInArr(pModules, STORAGE),
|
||||
mod = Util.getNamesFromObjArray(storageObj);
|
||||
|
||||
for (i = 0, n = lMod.length; i < n; i++) {
|
||||
var lName = lMod[i],
|
||||
lPath = lStorage + '/_' + lName.toLowerCase();
|
||||
for (i = 0, n = mod.length; i < n; i++) {
|
||||
name = mod[i],
|
||||
path = STORAGE + '/_' + name.toLowerCase();
|
||||
|
||||
lLoad(lName, lPath);
|
||||
load(name, path);
|
||||
}
|
||||
|
||||
|
||||
Util.exec(pCallBack);
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -224,42 +236,39 @@ var Util, DOM, CloudFunc, CloudCmd;
|
|||
function baseInit(pCallBack) {
|
||||
var LIB = CloudCmd.LIBDIR,
|
||||
LIBCLIENT = CloudCmd.LIBDIRCLIENT,
|
||||
files = [
|
||||
LIB + 'cloudfunc.js',
|
||||
LIBCLIENT + 'listeners.js'
|
||||
];
|
||||
files = DOM.getFiles(),
|
||||
LEFT = CloudFunc.PANEL_LEFT,
|
||||
RIGHT = CloudFunc.PANEL_RIGHT;
|
||||
|
||||
/* выделяем строку с первым файлом */
|
||||
if (files)
|
||||
DOM.setCurrentFile(files[0]);
|
||||
|
||||
Listeners = CloudCmd.Listeners;
|
||||
|
||||
Listeners.init();
|
||||
/* загружаем Google Analytics */
|
||||
Listeners.analytics();
|
||||
Listeners.changeLinks(CloudFunc.LEFTPANEL);
|
||||
Listeners.changeLinks(CloudFunc.RIGHTPANEL);
|
||||
Listeners.changeLinks(LEFT);
|
||||
Listeners.changeLinks(RIGHT);
|
||||
|
||||
CloudCmd.KeysPanel = Listeners.initKeysPanel();
|
||||
Listeners.initKeysPanel();
|
||||
|
||||
CloudCmd.getConfig(function(config) {
|
||||
var localStorage = config.localStorage;
|
||||
var localStorage = config.localStorage,
|
||||
dirPath = DOM.getCurrentDirPath();
|
||||
|
||||
/* устанавливаем переменную доступности кэша */
|
||||
Storage.setAllowed(localStorage);
|
||||
/* Устанавливаем кэш корневого каталога */
|
||||
var lDirPath = DOM.getCurrentDirPath();
|
||||
|
||||
lDirPath = CloudFunc.removeLastSlash(lDirPath) || '/';
|
||||
dirPath = CloudFunc.rmLastSlash(dirPath) || '/';
|
||||
|
||||
if (!Storage.get(lDirPath))
|
||||
Storage.set(lDirPath, getJSONfromFileTable());
|
||||
if (!Storage.get(dirPath))
|
||||
Storage.set(dirPath, getJSONfromFileTable());
|
||||
});
|
||||
|
||||
Util.exec(pCallBack);
|
||||
|
||||
/* выделяем строку с первым файлом */
|
||||
var lFiles = DOM.getFiles();
|
||||
if (lFiles)
|
||||
DOM.setCurrentFile(lFiles[0]);
|
||||
|
||||
Util.exec(CloudCmd.Key);
|
||||
Util.exec(pCallBack);
|
||||
}
|
||||
|
||||
function getSystemFile(pGlobal, pURL) {
|
||||
|
|
@ -305,7 +314,7 @@ var Util, DOM, CloudFunc, CloudCmd;
|
|||
lPanel = pCurrent && pCurrent.parentElement,
|
||||
lPath = DOM.getCurrentDirPath(lPanel),
|
||||
lLink = CloudFunc.FS + lPath,
|
||||
lNotSlashlLink = CloudFunc.removeLastSlash(lLink),
|
||||
lNotSlashlLink = CloudFunc.rmLastSlash(lLink),
|
||||
lLoad = CloudCmd.loadDir(lNotSlashlLink, lNEEDREFRESH);
|
||||
|
||||
lLoad();
|
||||
|
|
@ -324,6 +333,7 @@ var Util, DOM, CloudFunc, CloudCmd;
|
|||
|
||||
/* Отображаем красивые пути */
|
||||
var lSLASH = '/',
|
||||
title = CloudFunc.getTitle(lCleanPath),
|
||||
lFSPath = decodeURI(pPath),
|
||||
lNOJSPath = Util.removeStr( lFSPath, '?json' ),
|
||||
lCleanPath = Util.removeStrOneTime( lNOJSPath, CloudFunc.FS ) || lSLASH,
|
||||
|
|
@ -338,7 +348,7 @@ var Util, DOM, CloudFunc, CloudCmd;
|
|||
if (!pOptions.nohistory)
|
||||
DOM.setHistory(lNOJSPath, null, lNOJSPath);
|
||||
|
||||
DOM.setTitle( CloudFunc.getTitle(lCleanPath) );
|
||||
DOM.setTitle(title);
|
||||
|
||||
/* если доступен localStorage и
|
||||
* в нём есть нужная нам директория -
|
||||
|
|
@ -399,7 +409,7 @@ var Util, DOM, CloudFunc, CloudCmd;
|
|||
lDir = DOM.getCurrentDirName(),
|
||||
|
||||
lName = DOM.getCurrentName(lCurrent),
|
||||
lWasRefresh_b = lPath === pJSON.path,
|
||||
wasRefresh = lPath === pJSON.path,
|
||||
lFuncs = [
|
||||
CloudCmd.getFileTemplate,
|
||||
CloudCmd.getPathTemplate,
|
||||
|
|
@ -408,7 +418,8 @@ var Util, DOM, CloudFunc, CloudCmd;
|
|||
|
||||
Util.asyncCall(lFuncs, function(pTemplate, pPathTemplate, pLinkTemplate) {
|
||||
/* очищаем панель */
|
||||
var i = panel.childNodes.length;
|
||||
var n, found,
|
||||
i = panel.childNodes.length;
|
||||
|
||||
while(i--)
|
||||
panel.removeChild(panel.lastChild);
|
||||
|
|
@ -416,24 +427,24 @@ var Util, DOM, CloudFunc, CloudCmd;
|
|||
panel.innerHTML = CloudFunc.buildFromJSON(pJSON, pTemplate, pPathTemplate, pLinkTemplate);
|
||||
|
||||
files = DOM.getFiles(panel);
|
||||
/* если нажали на ссылку на верхний каталог*/
|
||||
var lFound;
|
||||
|
||||
/* searching current file */
|
||||
if (lWasRefresh_b) {
|
||||
var n = files.length;
|
||||
for(i = 0; i < n ; i++) {
|
||||
if (wasRefresh) {
|
||||
n = files.length;
|
||||
|
||||
for (i = 0; i < n ; i++) {
|
||||
var lVarCurrent = files[i],
|
||||
lVarName = DOM.getCurrentName(lVarCurrent);
|
||||
|
||||
lFound = lVarName === lName;
|
||||
found = lVarName === lName;
|
||||
|
||||
if (lFound) {
|
||||
if (found) {
|
||||
lCurrent = files[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!lFound) /* .. */
|
||||
if (!found) /* .. */
|
||||
lCurrent = files[0];
|
||||
|
||||
DOM.setCurrentFile(lCurrent);
|
||||
|
|
@ -443,49 +454,60 @@ var Util, DOM, CloudFunc, CloudCmd;
|
|||
if (lName === '..' && lDir !== '/')
|
||||
currentToParent(lDir);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Функция генерирует JSON из html-таблицы файлов и
|
||||
* используеться при первом заходе в корень
|
||||
*/
|
||||
function getJSONfromFileTable() {
|
||||
var lLeft = DOM.getById('left'),
|
||||
lPath = DOM.getByClass('path')[0].textContent,
|
||||
var current, name, size, owner, mode, ret,
|
||||
path = DOM.getCurrentDirPath(),
|
||||
infoFiles = Info.files,
|
||||
|
||||
lFileTable = {
|
||||
path : lPath,
|
||||
fileTable = {
|
||||
path : path,
|
||||
files : []
|
||||
},
|
||||
|
||||
files = lFileTable.files,
|
||||
files = fileTable.files,
|
||||
|
||||
lLI = DOM.getByTag('li', lLeft),
|
||||
i, n = lLI.length;
|
||||
i, n = infoFiles.length;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
current = infoFiles[i];
|
||||
name = DOM.getCurrentName(current);
|
||||
size = DOM.getCurrentSize(current);
|
||||
owner = DOM.getCurrentOwner(current);
|
||||
mode = DOM.getCurrentMode(current);
|
||||
|
||||
/* счётчик элементов файлов в DOM
|
||||
* Если путь отличный от корневного
|
||||
* второй элемент li - это ссылка на верхний
|
||||
* каталог '..'
|
||||
*/
|
||||
|
||||
/* пропускам Path и Header*/
|
||||
for (i = 2; i < n; i++) {
|
||||
var lCurrent = lLI[i],
|
||||
lName = DOM.getCurrentName(lCurrent),
|
||||
lSize = DOM.getCurrentSize(lCurrent),
|
||||
mode = CloudFunc.getNumericPermissions(mode);
|
||||
|
||||
lMode = DOM.getCurrentMode(lCurrent);
|
||||
lMode = CloudFunc.getNumericPermissions(lMode);
|
||||
|
||||
if (lName !== '..')
|
||||
lFileTable.files.push({
|
||||
name: lName,
|
||||
size: lSize,
|
||||
mode: lMode
|
||||
if (name !== '..')
|
||||
files.push({
|
||||
name : name,
|
||||
size : size,
|
||||
mode : mode,
|
||||
owner : owner
|
||||
});
|
||||
}
|
||||
return Util.stringifyJSON(lFileTable);
|
||||
|
||||
ret = Util.stringifyJSON(fileTable);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
this.goToParentDir = function() {
|
||||
var path = Info.dirPath,
|
||||
parentPath = Info.parentDirPath;
|
||||
|
||||
if (path !== parentPath) {
|
||||
path = parentPath;
|
||||
path = CloudFunc.FS + CloudFunc.rmLastSlash(path);
|
||||
|
||||
Util.exec(CloudCmd.loadDir(path));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
})(this, Util, DOM);
|
||||
})(this, Util, DOM, CloudFunc);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
var CloudCmd, Util, DOM;
|
||||
(function(CloudCmd, Util, DOM){
|
||||
|
||||
(function(CloudCmd, Util, DOM) {
|
||||
'use strict';
|
||||
|
||||
CloudCmd.Config = ConfigProto;
|
||||
|
||||
function ConfigProto() {
|
||||
var Key = CloudCmd.Key,
|
||||
var Loading = true,
|
||||
Key = CloudCmd.Key,
|
||||
Images = DOM.Images,
|
||||
Events = DOM.Events,
|
||||
INPUT = 'INPUT',
|
||||
|
|
@ -15,13 +17,16 @@ var CloudCmd, Util, DOM;
|
|||
Config = this;
|
||||
|
||||
function init(pCallBack) {
|
||||
Util.loadOnLoad([
|
||||
Config.show,
|
||||
CloudCmd.View,
|
||||
]);
|
||||
Loading = true;
|
||||
|
||||
DOM.Events.addKey(listener);
|
||||
DOM.setButtonKey('f10', Config.show);
|
||||
Util.loadOnLoad([
|
||||
CloudCmd.View,
|
||||
function(callback) {
|
||||
Loading = false;
|
||||
Util.exec(callback);
|
||||
},
|
||||
Config.show
|
||||
]);
|
||||
}
|
||||
|
||||
this.show = function() {
|
||||
|
|
@ -30,8 +35,10 @@ var CloudCmd, Util, DOM;
|
|||
cssLoad
|
||||
];
|
||||
|
||||
Images.showLoad({top:true});
|
||||
Util.asyncCall(funcs, fillTemplate);
|
||||
if (!Loading) {
|
||||
Images.showLoad({top:true});
|
||||
Util.asyncCall(funcs, fillTemplate);
|
||||
}
|
||||
};
|
||||
|
||||
function cssLoad(callback) {
|
||||
|
|
@ -90,15 +97,6 @@ var CloudCmd, Util, DOM;
|
|||
CloudCmd.View.hide();
|
||||
};
|
||||
|
||||
function listener(pEvent){
|
||||
var f10 = Key.F10,
|
||||
isBind = Key.isBind(),
|
||||
key = pEvent.keyCode;
|
||||
|
||||
/* если клавиши можно обрабатывать */
|
||||
if (isBind && key === f10)
|
||||
Config.show();
|
||||
}
|
||||
|
||||
function changeConfig(config) {
|
||||
var name;
|
||||
|
|
|
|||
|
|
@ -2,47 +2,49 @@ var CloudCmd, Util, DOM, CloudFunc, $;
|
|||
(function(CloudCmd, Util, DOM, CloudFunc) {
|
||||
'use strict';
|
||||
|
||||
var Buffer = {
|
||||
log : '',
|
||||
error : ''
|
||||
};
|
||||
|
||||
CloudCmd.Console = ConsoleProto;
|
||||
|
||||
function ConsoleProto(CallBack) {
|
||||
var Name = 'Console',
|
||||
Buffer = {
|
||||
log : '',
|
||||
error : ''
|
||||
},
|
||||
Loading,
|
||||
jqconsole,
|
||||
Element,
|
||||
MouseBinded,
|
||||
Socket,
|
||||
Key = CloudCmd.Key,
|
||||
Images = DOM.Images,
|
||||
Notify = DOM.Notify,
|
||||
|
||||
CHANNEL = CloudFunc.CHANNEL_CONSOLE,
|
||||
|
||||
Console = this;
|
||||
|
||||
function init() {
|
||||
var lFunc, lIsFunc = Util.isFunction(CloudCmd.View);
|
||||
|
||||
Loading = true;
|
||||
|
||||
if (lIsFunc)
|
||||
lFunc = CloudCmd.View;
|
||||
else
|
||||
lFunc = Util.exec;
|
||||
|
||||
Util.loadOnLoad([
|
||||
Console.show,
|
||||
load,
|
||||
lFunc,
|
||||
DOM.jqueryLoad,
|
||||
DOM.socketLoad
|
||||
CloudCmd.View,
|
||||
load,
|
||||
CloudCmd.Socket,
|
||||
function(callback) {
|
||||
Socket = CloudCmd.Socket;
|
||||
Util.exec(callback);
|
||||
},
|
||||
Console.show,
|
||||
addListeners,
|
||||
]);
|
||||
|
||||
DOM.Events.addKey(listener);
|
||||
DOM.setButtonKey('~', Console.show);
|
||||
}
|
||||
|
||||
this.show = function() {
|
||||
this.show = show;
|
||||
this.log = log;
|
||||
this.error = error;
|
||||
|
||||
function show(callback) {
|
||||
if (!Loading) {
|
||||
Images.showLoad({top:true});
|
||||
|
||||
|
|
@ -61,12 +63,9 @@ var CloudCmd, Util, DOM, CloudFunc, $;
|
|||
|
||||
// Handle a command.
|
||||
var handler = function(command) {
|
||||
var lSocket = CloudCmd.Socket;
|
||||
if (command) {
|
||||
Images.showLoad({ top:true });
|
||||
|
||||
if(lSocket)
|
||||
lSocket.send(command);
|
||||
Socket.emit(CHANNEL, command);
|
||||
}
|
||||
|
||||
jqconsole.Prompt(true, handler);
|
||||
|
|
@ -77,55 +76,57 @@ var CloudCmd, Util, DOM, CloudFunc, $;
|
|||
}
|
||||
|
||||
CloudCmd.View.show(Element, function() {
|
||||
var l$Console = jqconsole.$console,
|
||||
l$Input = jqconsole.$input_source,
|
||||
lFocus = function() {
|
||||
var console = jqconsole.$console,
|
||||
input = jqconsole.$input_source,
|
||||
|
||||
focus = function() {
|
||||
var x = window.scrollX,
|
||||
y = window.scrollY;
|
||||
|
||||
l$Input.focus();
|
||||
input.focus();
|
||||
window.scrollTo(x,y);
|
||||
};
|
||||
|
||||
lFocus();
|
||||
focus();
|
||||
|
||||
if (!MouseBinded) {
|
||||
MouseBinded = true;
|
||||
|
||||
$(l$Console).unbind('mouseup');
|
||||
$(l$Console).mouseup(function() {
|
||||
if( !window.getSelection().toString() ) {
|
||||
var lTop = l$Console.scrollTop();
|
||||
console.unbind('mouseup');
|
||||
console.mouseup(function() {
|
||||
var top,
|
||||
isSelection = '' + window.getSelection();
|
||||
|
||||
if (!isSelection) {
|
||||
top = console.scrollTop();
|
||||
|
||||
lFocus();
|
||||
l$Console.scrollTop(lTop);
|
||||
focus();
|
||||
console.scrollTop(top);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Util.exec(callback);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
this.hide = function() {
|
||||
CloudCmd.View.hide();
|
||||
};
|
||||
function log(pText) {
|
||||
write(pText, 'log');
|
||||
}
|
||||
|
||||
this.log = function(pText) {
|
||||
log(pText, 'log');
|
||||
};
|
||||
function error(pText) {
|
||||
write(pText, 'error');
|
||||
}
|
||||
|
||||
this.error = function(pText) {
|
||||
log(pText, 'error');
|
||||
};
|
||||
|
||||
function log(msg, status) {
|
||||
var ret;
|
||||
function write(msg, status) {
|
||||
var isContain;
|
||||
|
||||
if (msg) {
|
||||
Buffer[status] += msg;
|
||||
ret = Util.isContainStr(Buffer[status], '\n');
|
||||
isContain = Util.isContainStr(Buffer[status], '\n');
|
||||
|
||||
if (jqconsole && ret) {
|
||||
if (jqconsole && isContain) {
|
||||
jqconsole.Write(Buffer[status], status + '-msg');
|
||||
Notify.send(Buffer[status]);
|
||||
Buffer[status] = '';
|
||||
|
|
@ -137,13 +138,15 @@ var CloudCmd, Util, DOM, CloudFunc, $;
|
|||
Util.time(Name + ' load');
|
||||
|
||||
var lDir = CloudCmd.LIBDIRCLIENT + 'console/',
|
||||
cssPath = lDir + 'css/',
|
||||
jsPath = lDir + 'lib/',
|
||||
lCSS = [
|
||||
lDir + 'jqconsole.css',
|
||||
lDir + 'ansi.css'
|
||||
cssPath + 'jqconsole.css',
|
||||
cssPath + 'ansi.css'
|
||||
],
|
||||
lAllCSS = CloudFunc.getCombineURL(lCSS),
|
||||
lAllCSS = CloudFunc.getJoinURL(lCSS),
|
||||
lFiles = [
|
||||
lDir + 'jqconsole.js',
|
||||
jsPath + 'jqconsole.js',
|
||||
lAllCSS
|
||||
];
|
||||
|
||||
|
|
@ -155,24 +158,32 @@ var CloudCmd, Util, DOM, CloudFunc, $;
|
|||
});
|
||||
}
|
||||
|
||||
function listener(pEvent) {
|
||||
var lTRA = Key.TRA,
|
||||
lESC = Key.ESC,
|
||||
lIsBind = Key.isBind(),
|
||||
lKey = pEvent.keyCode;
|
||||
|
||||
switch(lKey) {
|
||||
case lTRA:
|
||||
if (lIsBind) {
|
||||
Console.show();
|
||||
DOM.preventDefault(pEvent);
|
||||
function addListeners(callback) {
|
||||
var options = {
|
||||
'connect' : function() {
|
||||
log(Socket.CONNECTED);
|
||||
},
|
||||
|
||||
'disconnect': function() {
|
||||
error(Socket.DISCONNECTED);
|
||||
}
|
||||
break;
|
||||
case lESC:
|
||||
Console.hide();
|
||||
break;
|
||||
};
|
||||
|
||||
options[CHANNEL] = onMessage;
|
||||
|
||||
Socket.on(options);
|
||||
|
||||
Util.exec(callback);
|
||||
}
|
||||
|
||||
function onMessage(json) {
|
||||
if (json) {
|
||||
Util.log(json);
|
||||
log(json.stdout);
|
||||
error(json.stderr);
|
||||
}
|
||||
|
||||
DOM.Images.hideLoad();
|
||||
}
|
||||
|
||||
init();
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ task 'watch', 'Build and watch the CoffeeScript source files', ->
|
|||
|
||||
task 'build', 'Build minified file with uglify', ->
|
||||
console.log 'building...'
|
||||
exec 'uglifyjs -o jqconsole.min.js lib/jqconsole.js', (err, res)->
|
||||
exec 'uglifyjs -m -o jqconsole.min.js lib/jqconsole.js', (err, res)->
|
||||
if err
|
||||
console.error 'failed with', err
|
||||
else
|
||||
|
|
|
|||
|
|
@ -66,3 +66,19 @@ info.
|
|||
|
||||
2013.1.26 Version 2.7.7
|
||||
* Support for middle click paste on linux. #47.
|
||||
|
||||
2013.8.5 Version 2.7.8
|
||||
* Fix issue #51.
|
||||
|
||||
2014.2.3 Version 2.8.0
|
||||
* Remove browser sniffing. Fix #37 #42 #53
|
||||
* Update android support. Fix #41
|
||||
* Add wrapper div for padding/styling of console. Fix #40
|
||||
|
||||
2014.2.8 Version 2.9.0
|
||||
* Fixed `GetColumn` method to return the column number up until the cursor.
|
||||
* `Dump`: Add header contents in console dump and remove extra spaces.
|
||||
|
||||
2014.2.8 Version 2.10.0
|
||||
* Add `Clear` method to clear the console
|
||||
|
||||
|
|
|
|||
|
|
@ -15,10 +15,11 @@ queueing.
|
|||
The plugin has been tested on the following browsers:
|
||||
|
||||
* IE 9+
|
||||
* Chrome 10+
|
||||
* Firefox 4+
|
||||
* Opera 11+
|
||||
* iOS 4+
|
||||
* Chrome
|
||||
* Firefox
|
||||
* Opera
|
||||
* iOS Safari and Chrome
|
||||
* Android Chrome
|
||||
|
||||
|
||||
##Getting Started
|
||||
|
|
@ -134,8 +135,7 @@ Takes two arguments:
|
|||
Sets the number of spaces inserted when indenting and removed when unindenting.
|
||||
Takes one argument:
|
||||
|
||||
* __int__ *width*: The code of the key pressing which (when Ctrl is held) will
|
||||
trigger this shortcut.
|
||||
* __int__ *width*: The number of spaces in each indentation level.
|
||||
|
||||
|
||||
Example:
|
||||
|
|
@ -469,6 +469,9 @@ Resets the character matching configuration.
|
|||
Resets the shortcut configuration.
|
||||
|
||||
|
||||
###jqconsole.Clear
|
||||
Clears the console's content excluding the current prompt
|
||||
|
||||
##Default Key Config
|
||||
|
||||
The console responds to the followind keys and key combinations by default:
|
||||
|
|
@ -720,4 +723,8 @@ further customization.
|
|||
##Contributors
|
||||
|
||||
[Max Shawabkeh](http://max99x.com/)
|
||||
[Amjad Masad](http://twitter.com/amjad_masad)
|
||||
[Amjad Masad](http://twitter.com/amasad)
|
||||
|
||||
## License
|
||||
|
||||
[MIT](http://opensource.org/licenses/MIT)
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
.jqconsole-ansi-bold {
|
||||
/* font-weight: bold; */
|
||||
}
|
||||
|
|
@ -121,10 +120,10 @@
|
|||
color: red;
|
||||
}
|
||||
.jqconsole-ansi-color-green {
|
||||
color: #65b04b;
|
||||
color: green;
|
||||
}
|
||||
.jqconsole-ansi-color-yellow {
|
||||
color: #fed563;
|
||||
color: yellow;
|
||||
}
|
||||
.jqconsole-ansi-color-blue {
|
||||
color: rgb(49,123,249);
|
||||
|
|
@ -146,10 +145,10 @@
|
|||
background-color: red;
|
||||
}
|
||||
.jqconsole-ansi-background-color-green {
|
||||
background-color: #65b04b;
|
||||
background-color: green;
|
||||
}
|
||||
.jqconsole-ansi-background-color-yellow {
|
||||
background-color: #fed563;
|
||||
background-color: yellow;
|
||||
}
|
||||
.jqconsole-ansi-background-color-blue {
|
||||
background-color: blue;
|
||||
78
lib/client/console/demos/echo.html
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html, body {
|
||||
background-color: #333;
|
||||
color: white;
|
||||
font-family: monospace;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
/* The console container element */
|
||||
#console {
|
||||
height: 400px;
|
||||
width: 750px;
|
||||
position:relative;
|
||||
background-color: black;
|
||||
border: 2px solid #CCC;
|
||||
margin: 0 auto;
|
||||
margin-top: 50px;
|
||||
}
|
||||
/* The inner console element. */
|
||||
.jqconsole {
|
||||
padding: 10px;
|
||||
}
|
||||
/* The cursor. */
|
||||
.jqconsole-cursor {
|
||||
background-color: gray;
|
||||
}
|
||||
/* The cursor color when the console looses focus. */
|
||||
.jqconsole-blurred .jqconsole-cursor {
|
||||
background-color: #666;
|
||||
}
|
||||
/* The current prompt text color */
|
||||
.jqconsole-prompt {
|
||||
color: #0d0;
|
||||
}
|
||||
/* The command history */
|
||||
.jqconsole-old-prompt {
|
||||
color: #0b0;
|
||||
font-weight: normal;
|
||||
}
|
||||
/* The text color when in input mode. */
|
||||
.jqconsole-input {
|
||||
color: #dd0;
|
||||
}
|
||||
/* Previously entered input. */
|
||||
.jqconsole-old-input {
|
||||
color: #bb0;
|
||||
font-weight: normal;
|
||||
}
|
||||
/* The text color of the output. */
|
||||
.jqconsole-output {
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="console"></div>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../lib/jqconsole.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script>
|
||||
$(function () {
|
||||
var jqconsole = $('#console').jqconsole('Hi\n', '>>> ');
|
||||
var startPrompt = function () {
|
||||
// Start the prompt with history enabled.
|
||||
jqconsole.Prompt(true, function (input) {
|
||||
// Output input with the class jqconsole-output.
|
||||
jqconsole.Write(input + '\n', 'jqconsole-output');
|
||||
// Restart the prompt.
|
||||
startPrompt();
|
||||
});
|
||||
};
|
||||
startPrompt();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
120
lib/client/console/demos/index.html
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="../css/ansi.css" type="text/css" media="all" />
|
||||
<style>
|
||||
html, body {
|
||||
background-color: #333;
|
||||
color: white;
|
||||
font-family: monospace;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#console {
|
||||
height: 400px;
|
||||
width: 750px;
|
||||
position:relative;
|
||||
background-color: black;
|
||||
border: 2px solid #CCC;
|
||||
margin: 0 auto;
|
||||
margin-top: 50px;
|
||||
}
|
||||
.jqconsole {
|
||||
padding: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.jqconsole-cursor {
|
||||
background-color: #999;
|
||||
}
|
||||
.jqconsole-blurred .jqconsole-cursor {
|
||||
background-color: #666;
|
||||
}
|
||||
.jqconsole-prompt {
|
||||
color: #0d0;
|
||||
}
|
||||
.jqconsole-old-prompt {
|
||||
color: #0b0;
|
||||
font-weight: normal;
|
||||
}
|
||||
.jqconsole-input {
|
||||
color: #dd0;
|
||||
}
|
||||
.jqconsole-old-input {
|
||||
color: #bb0;
|
||||
font-weight: normal;
|
||||
}
|
||||
.brace {
|
||||
color: #00FFFF;
|
||||
}
|
||||
.paran {
|
||||
color: #FF00FF;
|
||||
}
|
||||
.bracket {
|
||||
color: #FFFF00;
|
||||
}
|
||||
.jqconsole-composition {
|
||||
background-color: red;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="console"></div>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../lib/jqconsole.js"></script>
|
||||
<script>
|
||||
$(function() {
|
||||
// Creating the console.
|
||||
var header = 'Welcome to JQConsole!\n' +
|
||||
'Use jqconsole.Write() to write and ' +
|
||||
'jqconsole.Input() to read.\n';
|
||||
window.jqconsole = $('#console').jqconsole(header, 'JS> ');
|
||||
|
||||
// Abort prompt on Ctrl+Z.
|
||||
jqconsole.RegisterShortcut('Z', function() {
|
||||
jqconsole.AbortPrompt();
|
||||
handler();
|
||||
});
|
||||
// Move to line start Ctrl+A.
|
||||
jqconsole.RegisterShortcut('A', function() {
|
||||
jqconsole.MoveToStart();
|
||||
handler();
|
||||
});
|
||||
// Move to line end Ctrl+E.
|
||||
jqconsole.RegisterShortcut('E', function() {
|
||||
jqconsole.MoveToEnd();
|
||||
handler();
|
||||
});
|
||||
jqconsole.RegisterMatching('{', '}', 'brace');
|
||||
jqconsole.RegisterMatching('(', ')', 'paran');
|
||||
jqconsole.RegisterMatching('[', ']', 'bracket');
|
||||
// Handle a command.
|
||||
var handler = function(command) {
|
||||
if (command) {
|
||||
try {
|
||||
jqconsole.Write('==> ' + window.eval(command) + '\n');
|
||||
} catch (e) {
|
||||
jqconsole.Write('ERROR: ' + e.message + '\n');
|
||||
}
|
||||
}
|
||||
jqconsole.Prompt(true, handler, function(command) {
|
||||
// Continue line if can't compile the command.
|
||||
try {
|
||||
Function(command);
|
||||
} catch (e) {
|
||||
if (/[\[\{\(]$/.test(command)) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
// Initiate the first prompt.
|
||||
handler();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "jqconsole",
|
||||
"version": "2.7.7",
|
||||
"version": "2.8.0",
|
||||
"title": "Feature complete web terminal.",
|
||||
"author": {
|
||||
"name": "Amjad Masad",
|
||||
|
|
@ -19,4 +19,4 @@
|
|||
"dependencies": {
|
||||
"jquery": ">=1.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
33
lib/client/console/karma.conf.js
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
module.exports = function(config) {
|
||||
config.set({
|
||||
frameworks: ["mocha"],
|
||||
files: [
|
||||
'test/vendor/*.js',
|
||||
'lib/*.js',
|
||||
'test/setup.coffee',
|
||||
'test/ansi-test.coffee',
|
||||
'test/jqconsole-test.coffee',
|
||||
'test/prompt-test.coffee',
|
||||
'test/shortcuts-test.coffee',
|
||||
'test/history-test.coffee',
|
||||
'test/matching-test.coffee',
|
||||
'test/misc-test.coffee',
|
||||
],
|
||||
browsers: ['Chrome'],
|
||||
reporters: ['dots', 'coverage'],
|
||||
preprocessors: {
|
||||
'lib/*.js': ['coverage'],
|
||||
'test/*.coffee': ['coffee']
|
||||
},
|
||||
coffeePreprocessor: {
|
||||
// options passed to the coffee compiler
|
||||
options: {
|
||||
bare: false,
|
||||
}
|
||||
},
|
||||
coverageReporter: {
|
||||
type : 'html',
|
||||
dir : 'coverage/'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
@ -1,72 +1,9 @@
|
|||
// Generated by CoffeeScript 1.3.3
|
||||
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
/*
|
||||
Copyrights 2011, the repl.it project.
|
||||
Licensed under the MIT license
|
||||
*/
|
||||
// Limit scope pollution from any deprecated API
|
||||
(function() {
|
||||
|
||||
var matched, browser;
|
||||
|
||||
// Use of jQuery.browser is frowned upon.
|
||||
// More details: http://api.jquery.com/jQuery.browser
|
||||
// jQuery.uaMatch maintained for back-compat
|
||||
jQuery.uaMatch = function( ua ) {
|
||||
ua = ua.toLowerCase();
|
||||
|
||||
var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
|
||||
/(webkit)[ \/]([\w.]+)/.exec( ua ) ||
|
||||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
|
||||
/(msie) ([\w.]+)/.exec( ua ) ||
|
||||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
|
||||
[];
|
||||
|
||||
return {
|
||||
browser: match[ 1 ] || "",
|
||||
version: match[ 2 ] || "0"
|
||||
};
|
||||
};
|
||||
|
||||
matched = jQuery.uaMatch( navigator.userAgent );
|
||||
browser = {};
|
||||
|
||||
if ( matched.browser ) {
|
||||
browser[ matched.browser ] = true;
|
||||
browser.version = matched.version;
|
||||
}
|
||||
|
||||
// Chrome is Webkit, but Webkit is also Safari.
|
||||
if ( browser.chrome ) {
|
||||
browser.webkit = true;
|
||||
} else if ( browser.webkit ) {
|
||||
browser.safari = true;
|
||||
}
|
||||
|
||||
jQuery.browser = browser;
|
||||
|
||||
jQuery.sub = function() {
|
||||
function jQuerySub( selector, context ) {
|
||||
return new jQuerySub.fn.init( selector, context );
|
||||
}
|
||||
jQuery.extend( true, jQuerySub, this );
|
||||
jQuerySub.superclass = this;
|
||||
jQuerySub.fn = jQuerySub.prototype = this();
|
||||
jQuerySub.fn.constructor = jQuerySub;
|
||||
jQuerySub.sub = this.sub;
|
||||
jQuerySub.fn.init = function init( selector, context ) {
|
||||
if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
|
||||
context = jQuerySub( context );
|
||||
}
|
||||
|
||||
return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
|
||||
};
|
||||
jQuerySub.fn.init.prototype = jQuerySub.fn;
|
||||
var rootjQuerySub = jQuerySub(document);
|
||||
return jQuerySub;
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
(function() {
|
||||
var $, Ansi, CLASS_ANSI, CLASS_BLURRED, CLASS_CURSOR, CLASS_HEADER, CLASS_INPUT, CLASS_OLD_PROMPT, CLASS_PREFIX, CLASS_PROMPT, DEFAULT_INDENT_WIDTH, DEFAULT_PROMPT_CONINUE_LABEL, DEFAULT_PROMPT_LABEL, EMPTY_DIV, EMPTY_SELECTOR, EMPTY_SPAN, ESCAPE_CHAR, ESCAPE_SYNTAX, E_KEYPRESS, JQConsole, KEY_BACKSPACE, KEY_DELETE, KEY_DOWN, KEY_END, KEY_ENTER, KEY_HOME, KEY_LEFT, KEY_PAGE_DOWN, KEY_PAGE_UP, KEY_RIGHT, KEY_TAB, KEY_UP, NEWLINE, STATE_INPUT, STATE_OUTPUT, STATE_PROMPT, spanHtml,
|
||||
|
|
@ -142,24 +79,16 @@ Licensed under the MIT license
|
|||
ESCAPE_SYNTAX = /\[(\d*)(?:;(\d*))*m/;
|
||||
|
||||
Ansi = (function() {
|
||||
|
||||
Ansi.prototype.COLORS = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'];
|
||||
|
||||
function Ansi() {
|
||||
this.stylize = __bind(this.stylize, this);
|
||||
|
||||
this._closeSpan = __bind(this._closeSpan, this);
|
||||
|
||||
this._openSpan = __bind(this._openSpan, this);
|
||||
|
||||
this.getClasses = __bind(this.getClasses, this);
|
||||
|
||||
this._style = __bind(this._style, this);
|
||||
|
||||
this._color = __bind(this._color, this);
|
||||
|
||||
this._remove = __bind(this._remove, this);
|
||||
|
||||
this._append = __bind(this._append, this);
|
||||
this.klasses = [];
|
||||
}
|
||||
|
|
@ -344,27 +273,16 @@ Licensed under the MIT license
|
|||
};
|
||||
|
||||
JQConsole = (function() {
|
||||
|
||||
function JQConsole(container, header, prompt_label, prompt_continue_label) {
|
||||
this.container = container;
|
||||
function JQConsole(outer_container, header, prompt_label, prompt_continue_label) {
|
||||
this._HideComposition = __bind(this._HideComposition, this);
|
||||
|
||||
this._ShowComposition = __bind(this._ShowComposition, this);
|
||||
|
||||
this._UpdateComposition = __bind(this._UpdateComposition, this);
|
||||
|
||||
this._EndComposition = __bind(this._EndComposition, this);
|
||||
|
||||
this._StartComposition = __bind(this._StartComposition, this);
|
||||
|
||||
this._CheckComposition = __bind(this._CheckComposition, this);
|
||||
|
||||
this._ProcessMatch = __bind(this._ProcessMatch, this);
|
||||
|
||||
this._HandleKey = __bind(this._HandleKey, this);
|
||||
|
||||
this._HandleChar = __bind(this._HandleChar, this);
|
||||
|
||||
this.isMobile = !!navigator.userAgent.match(/iPhone|iPad|iPod|Android/i);
|
||||
this.isIos = !!navigator.userAgent.match(/iPhone|iPad|iPod/i);
|
||||
this.isAndroid = !!navigator.userAgent.match(/Android/i);
|
||||
|
|
@ -382,26 +300,41 @@ Licensed under the MIT license
|
|||
this.history_new = '';
|
||||
this.history_active = false;
|
||||
this.shortcuts = {};
|
||||
this.$console = $('<pre class="jqconsole"/>').appendTo(this.container);
|
||||
this.$container = $('<div/>').appendTo(outer_container);
|
||||
this.$container.css({
|
||||
'top': 0,
|
||||
'left': 0,
|
||||
'right': 0,
|
||||
'bottom': 0,
|
||||
'position': 'absolute',
|
||||
'overflow': 'auto'
|
||||
});
|
||||
this.$console = $('<pre class="jqconsole"/>').appendTo(this.$container);
|
||||
this.$console.css({
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
left: 0,
|
||||
margin: 0,
|
||||
overflow: 'auto'
|
||||
'margin': 0,
|
||||
'position': 'relative',
|
||||
'min-height': '100%',
|
||||
'box-sizing': 'border-box',
|
||||
'-moz-box-sizing': 'border-box',
|
||||
'-webkit-box-sizing': 'border-box'
|
||||
});
|
||||
this.$console_focused = true;
|
||||
this.$input_container = $(EMPTY_DIV).appendTo(this.container);
|
||||
this.$input_container = $(EMPTY_DIV).appendTo(this.$container);
|
||||
this.$input_container.css({
|
||||
position: 'relative',
|
||||
position: 'absolute',
|
||||
width: 1,
|
||||
height: 0,
|
||||
overflow: 'hidden'
|
||||
});
|
||||
this.$input_source = $('<textarea/>');
|
||||
this.$input_source.attr('wrap', 'off').css({
|
||||
this.$input_source = this.isAndroid ? $('<input/>') : $('<textarea/>');
|
||||
this.$input_source.attr({
|
||||
wrap: 'off',
|
||||
autocapitalize: 'off',
|
||||
autocorrect: 'off',
|
||||
spellcheck: 'false',
|
||||
autocomplete: 'off'
|
||||
});
|
||||
this.$input_source.css({
|
||||
position: 'absolute',
|
||||
width: 2
|
||||
});
|
||||
|
|
@ -421,7 +354,7 @@ Licensed under the MIT license
|
|||
this._InitPrompt();
|
||||
this._SetupEvents();
|
||||
this.Write(this.header, CLASS_HEADER);
|
||||
$(this.container).data('jqconsole', this);
|
||||
$(outer_container).data('jqconsole', this);
|
||||
}
|
||||
|
||||
JQConsole.prototype.ResetHistory = function() {
|
||||
|
|
@ -455,7 +388,7 @@ Licensed under the MIT license
|
|||
this.$input_container.detach();
|
||||
this.$console.html('');
|
||||
this.$prompt.appendTo(this.$console);
|
||||
this.$input_container.appendTo(this.container);
|
||||
this.$input_container.appendTo(this.$container);
|
||||
this.Write(this.header, CLASS_HEADER);
|
||||
return void 0;
|
||||
};
|
||||
|
|
@ -469,8 +402,7 @@ Licensed under the MIT license
|
|||
return this.history_index = this.history.length;
|
||||
};
|
||||
|
||||
/*------------------------ Shortcut Methods -----------------------------
|
||||
*/
|
||||
/*------------------------ Shortcut Methods -----------------------------*/
|
||||
|
||||
|
||||
JQConsole.prototype._CheckKeyCode = function(key_code) {
|
||||
|
|
@ -529,15 +461,16 @@ Licensed under the MIT license
|
|||
return void 0;
|
||||
};
|
||||
|
||||
/*---------------------- END Shortcut Methods ---------------------------
|
||||
*/
|
||||
/*---------------------- END Shortcut Methods ---------------------------*/
|
||||
|
||||
|
||||
JQConsole.prototype.GetColumn = function() {
|
||||
var lines;
|
||||
this.$prompt_right.detach();
|
||||
this.$prompt_cursor.text('');
|
||||
lines = this.$console.text().split(NEWLINE);
|
||||
this.$prompt_cursor.html(' ');
|
||||
this.$prompt_cursor.after(this.$prompt_right);
|
||||
return lines[lines.length - 1].length;
|
||||
};
|
||||
|
||||
|
|
@ -728,7 +661,7 @@ Licensed under the MIT license
|
|||
|
||||
JQConsole.prototype.Dump = function() {
|
||||
var $elems, elem;
|
||||
$elems = this.$console.find("." + CLASS_HEADER).nextUntil("." + CLASS_PROMPT);
|
||||
$elems = this.$console.find("." + CLASS_HEADER).nextUntil("." + CLASS_PROMPT).addBack();
|
||||
return ((function() {
|
||||
var _i, _len, _results;
|
||||
_results = [];
|
||||
|
|
@ -741,7 +674,7 @@ Licensed under the MIT license
|
|||
}
|
||||
}
|
||||
return _results;
|
||||
})()).join(' ');
|
||||
})()).join('');
|
||||
};
|
||||
|
||||
JQConsole.prototype.GetState = function() {
|
||||
|
|
@ -777,8 +710,13 @@ Licensed under the MIT license
|
|||
return void 0;
|
||||
};
|
||||
|
||||
/*------------------------ Private Methods -------------------------------
|
||||
*/
|
||||
JQConsole.prototype.Clear = function() {
|
||||
this.$console.find("." + CLASS_HEADER).nextUntil("." + CLASS_PROMPT).addBack().text('');
|
||||
this.$prompt_cursor.detach();
|
||||
return this.$prompt_after.before(this.$prompt_cursor);
|
||||
};
|
||||
|
||||
/*------------------------ Private Methods -------------------------------*/
|
||||
|
||||
|
||||
JQConsole.prototype._CheckInputQueue = function() {
|
||||
|
|
@ -811,8 +749,7 @@ Licensed under the MIT license
|
|||
};
|
||||
|
||||
JQConsole.prototype._SetupEvents = function() {
|
||||
var cb, paste_event,
|
||||
_this = this;
|
||||
var _this = this;
|
||||
if (this.isMobile) {
|
||||
this.$console.click(function(e) {
|
||||
e.preventDefault();
|
||||
|
|
@ -865,8 +802,7 @@ Licensed under the MIT license
|
|||
};
|
||||
return setTimeout(addClass, 100);
|
||||
});
|
||||
paste_event = $.browser.opera ? 'input' : 'paste';
|
||||
this.$input_source.bind(paste_event, function() {
|
||||
this.$input_source.bind('paste', function() {
|
||||
var handlePaste;
|
||||
handlePaste = function() {
|
||||
if (_this.in_composition) {
|
||||
|
|
@ -881,21 +817,17 @@ Licensed under the MIT license
|
|||
this.$input_source.keypress(this._HandleChar);
|
||||
this.$input_source.keydown(this._HandleKey);
|
||||
this.$input_source.keydown(this._CheckComposition);
|
||||
if ($.browser.mozilla != null) {
|
||||
this.$input_source.bind('compositionstart', this._StartComposition);
|
||||
this.$input_source.bind('compositionend', this._EndCommposition);
|
||||
this.$input_source.bind('text', this._UpdateComposition);
|
||||
}
|
||||
if ($.browser.opera != null) {
|
||||
cb = function() {
|
||||
if (_this.in_composition) {
|
||||
return;
|
||||
}
|
||||
if (_this.$input_source.val().length) {
|
||||
return _this._StartComposition();
|
||||
}
|
||||
};
|
||||
return setInterval(cb, 200);
|
||||
this.$input_source.bind('compositionstart', this._StartComposition);
|
||||
this.$input_source.bind('compositionend', function(e) {
|
||||
return setTimeout((function() {
|
||||
return _this._EndComposition(e);
|
||||
}), 0);
|
||||
});
|
||||
if (this.isAndroid) {
|
||||
this.$input_source.bind('input', this._StartComposition);
|
||||
return this.$input_source.bind('input', this._UpdateComposition);
|
||||
} else {
|
||||
return this.$input_source.bind('text', this._UpdateComposition);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -908,16 +840,6 @@ Licensed under the MIT license
|
|||
if (char_code === 8 || char_code === 9 || char_code === 13) {
|
||||
return false;
|
||||
}
|
||||
if ($.browser.mozilla) {
|
||||
if (event.keyCode || event.altKey) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if ($.browser.opera) {
|
||||
if (event.altKey) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
this.$prompt_left.text(this.$prompt_left.text() + String.fromCharCode(char_code));
|
||||
this._ScrollToEnd();
|
||||
return false;
|
||||
|
|
@ -949,10 +871,10 @@ Licensed under the MIT license
|
|||
this._MoveDown();
|
||||
break;
|
||||
case KEY_PAGE_UP:
|
||||
this._ScrollUp();
|
||||
this._ScrollPage('up');
|
||||
break;
|
||||
case KEY_PAGE_DOWN:
|
||||
this._ScrollDown();
|
||||
this._ScrollPage('down');
|
||||
break;
|
||||
default:
|
||||
return true;
|
||||
|
|
@ -991,10 +913,10 @@ Licensed under the MIT license
|
|||
this.MoveToEnd(false);
|
||||
break;
|
||||
case KEY_PAGE_UP:
|
||||
this._ScrollUp();
|
||||
this._ScrollPage('up');
|
||||
break;
|
||||
case KEY_PAGE_DOWN:
|
||||
this._ScrollDown();
|
||||
this._ScrollPage('down');
|
||||
break;
|
||||
default:
|
||||
return true;
|
||||
|
|
@ -1048,6 +970,7 @@ Licensed under the MIT license
|
|||
JQConsole.prototype._HandleEnter = function(shift) {
|
||||
var continuation, text,
|
||||
_this = this;
|
||||
this._EndComposition();
|
||||
if (shift) {
|
||||
return this._InsertNewLine(true);
|
||||
} else {
|
||||
|
|
@ -1301,52 +1224,49 @@ Licensed under the MIT license
|
|||
return _results;
|
||||
};
|
||||
|
||||
JQConsole.prototype._ScrollUp = function() {
|
||||
JQConsole.prototype._ScrollPage = function(dir) {
|
||||
var target;
|
||||
target = this.$console[0].scrollTop - this.$console.height();
|
||||
return this.$console.stop().animate({
|
||||
scrollTop: target
|
||||
}, 'fast');
|
||||
};
|
||||
|
||||
JQConsole.prototype._ScrollDown = function() {
|
||||
var target;
|
||||
target = this.$console[0].scrollTop + this.$console.height();
|
||||
return this.$console.stop().animate({
|
||||
target = this.$container[0].scrollTop;
|
||||
if (dir === 'up') {
|
||||
target -= this.$container.height();
|
||||
} else {
|
||||
target += this.$container.height();
|
||||
}
|
||||
return this.$container.stop().animate({
|
||||
scrollTop: target
|
||||
}, 'fast');
|
||||
};
|
||||
|
||||
JQConsole.prototype._ScrollToEnd = function() {
|
||||
var cont,
|
||||
_this = this;
|
||||
this.$console.scrollTop(this.$console[0].scrollHeight);
|
||||
cont = function() {
|
||||
var doc_height, line_height, optimal_pos, pos, rel_pos, screen_left, screen_top;
|
||||
line_height = _this.$prompt_cursor.height();
|
||||
screen_top = _this.$window.scrollTop();
|
||||
screen_left = _this.$window.scrollLeft();
|
||||
doc_height = document.documentElement.clientHeight;
|
||||
pos = _this.$prompt_cursor.offset();
|
||||
rel_pos = _this.$prompt_cursor.position();
|
||||
_this.$input_container.css({
|
||||
left: rel_pos.left,
|
||||
top: rel_pos.top
|
||||
});
|
||||
optimal_pos = pos.top - (2 * line_height);
|
||||
if (_this.isMobile && (typeof orientation !== "undefined" && orientation !== null)) {
|
||||
if (screen_top < pos.top || screen_top > pos.top) {
|
||||
return _this.$window.scrollTop(optimal_pos);
|
||||
}
|
||||
} else {
|
||||
if (screen_top + doc_height < pos.top) {
|
||||
return _this.$window.scrollTop(pos.top - doc_height + line_height);
|
||||
} else if (screen_top > optimal_pos) {
|
||||
return _this.$window.scrollTop(pos.top);
|
||||
}
|
||||
var pos;
|
||||
this.$container.scrollTop(this.$container[0].scrollHeight);
|
||||
pos = this.$prompt_cursor.position();
|
||||
this.$input_container.css({
|
||||
left: pos.left,
|
||||
top: pos.top
|
||||
});
|
||||
return setTimeout(this.ScrollWindowToPrompt.bind(this), 50);
|
||||
};
|
||||
|
||||
JQConsole.prototype.ScrollWindowToPrompt = function() {
|
||||
var doc_height, line_height, optimal_pos, pos, screen_left, screen_top;
|
||||
line_height = this.$prompt_cursor.height();
|
||||
screen_top = this.$window.scrollTop();
|
||||
screen_left = this.$window.scrollLeft();
|
||||
doc_height = document.documentElement.clientHeight;
|
||||
pos = this.$prompt_cursor.offset();
|
||||
optimal_pos = pos.top - (2 * line_height);
|
||||
if (this.isMobile && (typeof orientation !== "undefined" && orientation !== null)) {
|
||||
if (screen_top < pos.top || screen_top > pos.top) {
|
||||
return this.$window.scrollTop(optimal_pos);
|
||||
}
|
||||
};
|
||||
return setTimeout(cont, 0);
|
||||
} else {
|
||||
if (screen_top + doc_height < pos.top) {
|
||||
return this.$window.scrollTop(pos.top - doc_height + line_height);
|
||||
} else if (screen_top > optimal_pos) {
|
||||
return this.$window.scrollTop(pos.top);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
JQConsole.prototype._SelectPromptLabel = function(continuation) {
|
||||
|
|
@ -1365,14 +1285,6 @@ Licensed under the MIT license
|
|||
}
|
||||
};
|
||||
|
||||
JQConsole.prototype._outerHTML = function($elem) {
|
||||
if (document.body.outerHTML) {
|
||||
return $elem.get(0).outerHTML;
|
||||
} else {
|
||||
return $(EMPTY_DIV).append($elem.eq(0).clone()).html();
|
||||
}
|
||||
};
|
||||
|
||||
JQConsole.prototype._Wrap = function($elem, index, cls) {
|
||||
var html, text;
|
||||
text = $elem.html();
|
||||
|
|
@ -1511,9 +1423,6 @@ Licensed under the MIT license
|
|||
JQConsole.prototype._CheckComposition = function(e) {
|
||||
var key;
|
||||
key = e.keyCode || e.which;
|
||||
if (($.browser.opera != null) && this.in_composition) {
|
||||
this._UpdateComposition();
|
||||
}
|
||||
if (key === 229) {
|
||||
if (this.in_composition) {
|
||||
return this._UpdateComposition();
|
||||
|
|
@ -1524,17 +1433,23 @@ Licensed under the MIT license
|
|||
};
|
||||
|
||||
JQConsole.prototype._StartComposition = function() {
|
||||
this.$input_source.bind(E_KEYPRESS, this._EndComposition);
|
||||
if (this.in_composition) {
|
||||
return;
|
||||
}
|
||||
this.in_composition = true;
|
||||
this._ShowComposition();
|
||||
return setTimeout(this._UpdateComposition, 0);
|
||||
};
|
||||
|
||||
JQConsole.prototype._EndComposition = function() {
|
||||
this.$input_source.unbind(E_KEYPRESS, this._EndComposition);
|
||||
this.in_composition = false;
|
||||
if (!this.in_composition) {
|
||||
return;
|
||||
}
|
||||
this._HideComposition();
|
||||
return this.$input_source.val('');
|
||||
this.$prompt_left.text(this.$prompt_left.text() + this.$composition.text());
|
||||
this.$composition.text('');
|
||||
this.$input_source.val('');
|
||||
return this.in_composition = false;
|
||||
};
|
||||
|
||||
JQConsole.prototype._UpdateComposition = function(e) {
|
||||
41
lib/client/console/package.json
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"name": "jq-console",
|
||||
"version": "2.10.0",
|
||||
"description": "Feature complete web terminal",
|
||||
"main": "jqconsole.min.js",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "./node_modules/karma/bin/karma start --single-run"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/replit/jq-console.git"
|
||||
},
|
||||
"keywords": [
|
||||
"jq-consoel",
|
||||
"console",
|
||||
"terminal"
|
||||
],
|
||||
"author": "amasad",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/replit/jq-console/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"karma-coffee-preprocessor": "~0.1.2",
|
||||
"mocha": "~1.17.1",
|
||||
"karma-script-launcher": "~0.1.0",
|
||||
"karma-chrome-launcher": "~0.1.2",
|
||||
"karma-firefox-launcher": "~0.1.3",
|
||||
"karma-html2js-preprocessor": "~0.1.0",
|
||||
"karma-jasmine": "~0.1.5",
|
||||
"requirejs": "~2.1.10",
|
||||
"karma-requirejs": "~0.2.1",
|
||||
"karma-phantomjs-launcher": "~0.1.1",
|
||||
"karma": "~0.10.9",
|
||||
"karma-mocha": "~0.1.1",
|
||||
"karma-coverage": "~0.1.5"
|
||||
}
|
||||
}
|
||||
|
|
@ -48,21 +48,21 @@ DEFAULT_PROMPT_CONINUE_LABEL = '... '
|
|||
# The default number of spaces inserted when indenting.
|
||||
DEFAULT_INDENT_WIDTH = 2
|
||||
|
||||
CLASS_ANSI = "#{CLASS_PREFIX}ansi-"
|
||||
CLASS_ANSI = "#{CLASS_PREFIX}ansi-"
|
||||
ESCAPE_CHAR = '\x1B'
|
||||
ESCAPE_SYNTAX = /\[(\d*)(?:;(\d*))*m/
|
||||
|
||||
class Ansi
|
||||
COLORS: ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white']
|
||||
|
||||
|
||||
constructor: ->
|
||||
@klasses = [];
|
||||
|
||||
|
||||
_append: (klass) =>
|
||||
klass = "#{CLASS_ANSI}#{klass}"
|
||||
if @klasses.indexOf(klass) is -1
|
||||
@klasses.push klass
|
||||
|
||||
|
||||
_remove: (klasses...) =>
|
||||
for klass in klasses
|
||||
if klass in ['fonts', 'color', 'background-color']
|
||||
|
|
@ -70,15 +70,15 @@ class Ansi
|
|||
else
|
||||
klass = "#{CLASS_ANSI}#{klass}"
|
||||
@klasses = (cls for cls in @klasses when cls isnt klass)
|
||||
|
||||
|
||||
_color: (i) => @COLORS[i]
|
||||
|
||||
|
||||
_style: (code) =>
|
||||
code = 0 if code == ''
|
||||
code = parseInt code
|
||||
|
||||
|
||||
return if isNaN code
|
||||
|
||||
|
||||
switch code
|
||||
when 0 then @klasses = []
|
||||
when 1 then @_append 'bold'
|
||||
|
|
@ -113,27 +113,27 @@ class Ansi
|
|||
when 53 then @_append 'overline'
|
||||
when 54 then @_remove 'framed'
|
||||
when 55 then @_remove 'overline'
|
||||
|
||||
|
||||
getClasses: => @klasses.join ' '
|
||||
|
||||
|
||||
_openSpan: (text) => "<span class=\"#{@getClasses()}\">#{text}"
|
||||
_closeSpan: (text) => "#{text}</span>"
|
||||
|
||||
|
||||
stylize: (text) =>
|
||||
text = @_openSpan text
|
||||
|
||||
|
||||
i = 0
|
||||
while (i = text.indexOf(ESCAPE_CHAR ,i)) and i isnt -1
|
||||
if d = text[i...].match ESCAPE_SYNTAX
|
||||
@_style code for code in d[1...]
|
||||
text = @_closeSpan(text[0...i]) + @_openSpan text[i + 1 + d[0].length...]
|
||||
else i++
|
||||
|
||||
return @_closeSpan text
|
||||
|
||||
|
||||
return @_closeSpan text
|
||||
|
||||
# Helper functions
|
||||
spanHtml = (klass, content) -> "<span class=\"#{klass}\">#{content or ''}</span>"
|
||||
|
||||
|
||||
class JQConsole
|
||||
# Creates a console.
|
||||
# @arg container: The DOM element into which the console is inserted.
|
||||
|
|
@ -143,21 +143,21 @@ class JQConsole
|
|||
# Defaults to DEFAULT_PROMPT_LABEL.
|
||||
# @arg prompt_continue: The label to show before continuation lines of the
|
||||
# command prompt. Optional. Defaults to DEFAULT_PROMPT_CONINUE_LABEL.
|
||||
constructor: (@container, header, prompt_label, prompt_continue_label) ->
|
||||
constructor: (outer_container, header, prompt_label, prompt_continue_label) ->
|
||||
# Mobile devices supported sniff.
|
||||
@isMobile = !!navigator.userAgent.match /iPhone|iPad|iPod|Android/i
|
||||
@isIos = !!navigator.userAgent.match /iPhone|iPad|iPod/i
|
||||
@isAndroid = !!navigator.userAgent.match /Android/i
|
||||
|
||||
|
||||
@$window = $(window)
|
||||
|
||||
|
||||
# The header written when the console is reset.
|
||||
@header = header or ''
|
||||
|
||||
# The prompt label used by Prompt().
|
||||
@prompt_label_main = if typeof prompt_label == 'string'
|
||||
prompt_label
|
||||
else
|
||||
prompt_label
|
||||
else
|
||||
DEFAULT_PROMPT_LABEL
|
||||
@prompt_label_continue = (prompt_continue_label or
|
||||
DEFAULT_PROMPT_CONINUE_LABEL)
|
||||
|
|
@ -193,60 +193,76 @@ class JQConsole
|
|||
|
||||
# A table of custom shortcuts, mapping character codes to callbacks.
|
||||
@shortcuts = {}
|
||||
|
||||
|
||||
@$container = $('<div/>').appendTo outer_container
|
||||
@$container.css
|
||||
'top': 0
|
||||
'left': 0
|
||||
'right': 0
|
||||
'bottom': 0
|
||||
'position': 'absolute'
|
||||
'overflow': 'auto'
|
||||
|
||||
# The main console area. Everything else happens inside this.
|
||||
@$console = $('<pre class="jqconsole"/>').appendTo @container
|
||||
@$console.css
|
||||
position: 'absolute'
|
||||
top: 0
|
||||
bottom: 0
|
||||
right: 0
|
||||
left: 0
|
||||
margin: 0
|
||||
overflow: 'auto'
|
||||
@$console = $('<pre class="jqconsole"/>').appendTo @$container
|
||||
@$console.css
|
||||
'margin': 0
|
||||
'position': 'relative'
|
||||
'min-height': '100%'
|
||||
'box-sizing': 'border-box'
|
||||
'-moz-box-sizing': 'border-box'
|
||||
'-webkit-box-sizing': 'border-box'
|
||||
|
||||
# Whether the console currently has focus.
|
||||
@$console_focused = true
|
||||
|
||||
|
||||
# On screen somehow invisible textbox for input.
|
||||
# Copied from codemirror2, this works for both mobile and desktop browsers.
|
||||
@$input_container = $(EMPTY_DIV).appendTo @container
|
||||
@$input_container = $(EMPTY_DIV).appendTo @$container
|
||||
@$input_container.css
|
||||
position: 'relative'
|
||||
position: 'absolute'
|
||||
width: 1
|
||||
height: 0
|
||||
overflow: 'hidden'
|
||||
@$input_source = $('<textarea/>')
|
||||
@$input_source.attr('wrap', 'off').css
|
||||
|
||||
# On android autocapitlize works for input.
|
||||
@$input_source = if @isAndroid then $('<input/>') else $('<textarea/>')
|
||||
@$input_source.attr
|
||||
wrap: 'off'
|
||||
autocapitalize: 'off'
|
||||
autocorrect: 'off'
|
||||
spellcheck: 'false'
|
||||
autocomplete: 'off'
|
||||
@$input_source.css
|
||||
position: 'absolute'
|
||||
width: 2
|
||||
@$input_source.appendTo @$input_container
|
||||
|
||||
|
||||
@$composition = $(EMPTY_DIV)
|
||||
@$composition.addClass "#{CLASS_PREFIX}composition"
|
||||
@$composition.css
|
||||
display: 'inline'
|
||||
position: 'relative'
|
||||
|
||||
|
||||
# Hash containing all matching settings
|
||||
# openings/closings[char] = matching_config
|
||||
# openings/closings[char] = matching_config
|
||||
# where char is the opening/closing character.
|
||||
# clss is an array of classes for fast unhighlighting
|
||||
# for matching_config see Match method
|
||||
@matchings =
|
||||
@matchings =
|
||||
openings: {}
|
||||
closings: {}
|
||||
clss: []
|
||||
|
||||
|
||||
@ansi = new Ansi()
|
||||
|
||||
|
||||
# Prepare console for interaction.
|
||||
@_InitPrompt()
|
||||
@_SetupEvents()
|
||||
@Write @header, CLASS_HEADER
|
||||
|
||||
|
||||
# Save this instance to be accessed if lost.
|
||||
$(@container).data 'jqconsole', this
|
||||
$(outer_container).data 'jqconsole', this
|
||||
|
||||
#### Reset methods
|
||||
|
||||
|
|
@ -260,7 +276,7 @@ class JQConsole
|
|||
|
||||
# Resets the matching configuration.
|
||||
ResetMatchings: ->
|
||||
@matchings =
|
||||
@matchings =
|
||||
openings: {}
|
||||
closings: {}
|
||||
clss: []
|
||||
|
|
@ -279,23 +295,23 @@ class JQConsole
|
|||
@$input_container.detach()
|
||||
@$console.html ''
|
||||
@$prompt.appendTo @$console
|
||||
@$input_container.appendTo @container
|
||||
@$input_container.appendTo @$container
|
||||
@Write @header, CLASS_HEADER
|
||||
return undefined
|
||||
|
||||
|
||||
#### History Methods
|
||||
|
||||
|
||||
# Get the current history
|
||||
GetHistory: ->
|
||||
@history
|
||||
|
||||
|
||||
# Set the history
|
||||
SetHistory: (history) ->
|
||||
@history = history.slice()
|
||||
@history_index = @history.length
|
||||
|
||||
|
||||
###------------------------ Shortcut Methods -----------------------------###
|
||||
|
||||
|
||||
# Checks the type/value of key codes passed in for registering/unregistering
|
||||
# shortcuts and handles accordingly.
|
||||
_CheckKeyCode: (key_code) ->
|
||||
|
|
@ -306,16 +322,16 @@ class JQConsole
|
|||
|
||||
if not (0 < key_code < 256) or isNaN key_code
|
||||
throw new Error 'Key code must be a number between 0 and 256 exclusive.'
|
||||
|
||||
|
||||
return key_code
|
||||
|
||||
|
||||
# A helper function responsible for calling the register/unregister callback
|
||||
# twice passing in both the upper and lower case letters.
|
||||
_LetterCaseHelper: (key_code, callback)->
|
||||
callback key_code
|
||||
if 65 <= key_code <= 90 then callback key_code + 32
|
||||
if 97 <= key_code <= 122 then callback key_code - 32
|
||||
|
||||
|
||||
# Registers a Ctrl+Key shortcut.
|
||||
# @arg key_code: The code of the key pressing which (when Ctrl is held) will
|
||||
# trigger this shortcut. If a string is provided, the character code of
|
||||
|
|
@ -330,10 +346,10 @@ class JQConsole
|
|||
addShortcut = (key) =>
|
||||
if key not of @shortcuts then @shortcuts[key] = []
|
||||
@shortcuts[key].push callback
|
||||
|
||||
|
||||
@_LetterCaseHelper key_code, addShortcut
|
||||
return undefined
|
||||
|
||||
|
||||
# Removes a Ctrl+Key shortcut from shortcut registry.
|
||||
# @arg key_code: The code of the key pressing which (when Ctrl is held) will
|
||||
# trigger this shortcut. If a string is provided, the character code of
|
||||
|
|
@ -343,24 +359,26 @@ class JQConsole
|
|||
# would be removed.
|
||||
UnRegisterShortcut: (key_code, handler) ->
|
||||
key_code = @_CheckKeyCode key_code
|
||||
|
||||
|
||||
removeShortcut = (key)=>
|
||||
if key of @shortcuts
|
||||
if handler
|
||||
@shortcuts[key].splice @shortcuts[key].indexOf(handler), 1
|
||||
else
|
||||
delete @shortcuts[key]
|
||||
|
||||
|
||||
@_LetterCaseHelper key_code, removeShortcut
|
||||
return undefined
|
||||
|
||||
|
||||
###---------------------- END Shortcut Methods ---------------------------###
|
||||
|
||||
|
||||
# Returns the 0-based number of the column on which the cursor currently is.
|
||||
GetColumn: ->
|
||||
@$prompt_right.detach()
|
||||
@$prompt_cursor.text ''
|
||||
lines = @$console.text().split NEWLINE
|
||||
@$prompt_cursor.html ' '
|
||||
@$prompt_cursor.after @$prompt_right
|
||||
return lines[lines.length - 1].length
|
||||
|
||||
# Returns the 0-based number of the line on which the cursor currently is.
|
||||
|
|
@ -415,7 +433,7 @@ class JQConsole
|
|||
@_AppendPromptText text
|
||||
@_ScrollToEnd()
|
||||
return undefined
|
||||
|
||||
|
||||
# Replaces the main prompt label.
|
||||
# @arg main_label: The new main label for the next prompt.
|
||||
# @arg continue_label: The new continuation label for the next prompt. Optional.
|
||||
|
|
@ -431,12 +449,12 @@ class JQConsole
|
|||
Write: (text, cls, escape=true) ->
|
||||
if escape
|
||||
text = @ansi.stylize $(EMPTY_SPAN).text(text).html()
|
||||
|
||||
|
||||
span = $(EMPTY_SPAN).html text
|
||||
if cls? then span.addClass cls
|
||||
@Append span
|
||||
|
||||
# Adds a dom node, where any text would have been inserted
|
||||
|
||||
# Adds a dom node, where any text would have been inserted
|
||||
# @arg node: The node to insert.
|
||||
Append: (node) ->
|
||||
$node = $(node).insertBefore @$prompt
|
||||
|
|
@ -444,7 +462,7 @@ class JQConsole
|
|||
# Force reclaculation of the cursor's position.
|
||||
@$prompt_cursor.detach().insertAfter @$prompt_left
|
||||
return $node
|
||||
|
||||
|
||||
# Starts an input operation. If another input or prompt operation is currently
|
||||
# underway, the new input operation is enqueued and will be called when the
|
||||
# current operation and all previously enqueued operations finish.
|
||||
|
|
@ -536,26 +554,29 @@ class JQConsole
|
|||
# @arg open: the openning character
|
||||
# @arg close: the closing character
|
||||
# @arg cls: the html class to add to the matched characters
|
||||
RegisterMatching: (open, close, cls) ->
|
||||
match_config =
|
||||
RegisterMatching: (open, close, cls) ->
|
||||
match_config =
|
||||
opening_char: open
|
||||
closing_char: close
|
||||
cls: cls
|
||||
|
||||
|
||||
@matchings.clss.push(cls)
|
||||
@matchings.openings[open] = match_config
|
||||
@matchings.closings[close] = match_config
|
||||
|
||||
|
||||
# Unregisters a character matching. cls is optional.
|
||||
UnRegisterMatching: (open, close) ->
|
||||
cls = @matchings.openings[open].cls
|
||||
delete @matchings.openings[open]
|
||||
delete @matchings.closings[close]
|
||||
@matchings.clss.splice @matchings.clss.indexOf(cls), 1
|
||||
|
||||
|
||||
# Dumps the content of the console before the current prompt.
|
||||
Dump: ->
|
||||
$elems = @$console.find(".#{CLASS_HEADER}").nextUntil(".#{CLASS_PROMPT}")
|
||||
$elems = @$console
|
||||
.find(".#{CLASS_HEADER}")
|
||||
.nextUntil(".#{CLASS_PROMPT}")
|
||||
.addBack()
|
||||
|
||||
return (
|
||||
for elem in $elems
|
||||
|
|
@ -563,7 +584,7 @@ class JQConsole
|
|||
$(elem).text().replace /^\s+/, '>>> '
|
||||
else
|
||||
$(elem).text()
|
||||
).join ' '
|
||||
).join ''
|
||||
|
||||
# Gets the current prompt state.
|
||||
GetState: ->
|
||||
|
|
@ -573,12 +594,12 @@ class JQConsole
|
|||
'output'
|
||||
else
|
||||
'prompt'
|
||||
|
||||
|
||||
# Disables focus and input on the console.
|
||||
Disable: ->
|
||||
@$input_source.attr 'disabled', on
|
||||
@$input_source.blur();
|
||||
|
||||
|
||||
# Enables focus and input on the console.
|
||||
Enable: ->
|
||||
@$input_source.attr 'disabled', off
|
||||
|
|
@ -599,6 +620,17 @@ class JQConsole
|
|||
@_MoveTo all_lines, false
|
||||
return undefined
|
||||
|
||||
# Clear the console keeping only the prompt.
|
||||
Clear: ->
|
||||
@$console
|
||||
.find(".#{CLASS_HEADER}")
|
||||
.nextUntil(".#{CLASS_PROMPT}")
|
||||
.addBack()
|
||||
.text ''
|
||||
# Bug in Chrome were the cursor's position is not recalculated
|
||||
@$prompt_cursor.detach()
|
||||
@$prompt_after.before @$prompt_cursor
|
||||
|
||||
###------------------------ Private Methods -------------------------------###
|
||||
|
||||
_CheckInputQueue: ->
|
||||
|
|
@ -660,7 +692,7 @@ class JQConsole
|
|||
|
||||
# Binds all the required input and focus events.
|
||||
_SetupEvents: ->
|
||||
|
||||
|
||||
# Click to focus.
|
||||
if @isMobile
|
||||
@$console.click (e) =>
|
||||
|
|
@ -672,14 +704,14 @@ class JQConsole
|
|||
# paste on linux desktop.
|
||||
if e.which == 2
|
||||
@Focus()
|
||||
else
|
||||
else
|
||||
fn = =>
|
||||
if not window.getSelection().toString()
|
||||
e.preventDefault()
|
||||
@Focus()
|
||||
# Force selection update.
|
||||
setTimeout fn, 0
|
||||
|
||||
|
||||
# Mark the console with a style when it loses focus.
|
||||
@$input_source.focus =>
|
||||
@_ScrollToEnd()
|
||||
|
|
@ -691,17 +723,16 @@ class JQConsole
|
|||
hideTextInput = =>
|
||||
if @isIos and @$console_focused then @$input_source.hide()
|
||||
setTimeout hideTextInput, 500
|
||||
|
||||
|
||||
@$input_source.blur =>
|
||||
@$console_focused = false
|
||||
if @isIos then @$input_source.show()
|
||||
addClass = =>
|
||||
if not @$console_focused then @$console.addClass CLASS_BLURRED
|
||||
setTimeout addClass, 100
|
||||
|
||||
|
||||
# Intercept pasting.
|
||||
paste_event = if $.browser.opera then 'input' else 'paste'
|
||||
@$input_source.bind paste_event, =>
|
||||
@$input_source.bind 'paste', =>
|
||||
handlePaste = =>
|
||||
# Opera fires input on composition end.
|
||||
return if @in_composition
|
||||
|
|
@ -715,24 +746,25 @@ class JQConsole
|
|||
@$input_source.keypress @_HandleChar
|
||||
@$input_source.keydown @_HandleKey
|
||||
@$input_source.keydown @_CheckComposition
|
||||
|
||||
|
||||
# Firefox don't fire any key event for composition characters, so we listen
|
||||
# for the unstandard composition-events.
|
||||
if $.browser.mozilla?
|
||||
@$input_source.bind 'compositionstart', @_StartComposition
|
||||
@$input_source.bind 'compositionend', @_EndCommposition
|
||||
@$input_source.bind 'compositionstart', @_StartComposition
|
||||
@$input_source.bind 'compositionend', (e) =>
|
||||
# Wait for the input element to update so we don't rely on buggy e.data
|
||||
setTimeout((=> @_EndComposition(e)), 0)
|
||||
|
||||
# Android has an out of screen text input for autocorrect and autocomplete
|
||||
# and since it doesn't allow disabling we use composition events and more
|
||||
# hacks to get input to work.
|
||||
if @isAndroid
|
||||
# Text is handled via composition events but for things like spaces
|
||||
# we need to emulate a composition start.
|
||||
@$input_source.bind 'input', @_StartComposition
|
||||
@$input_source.bind 'input', @_UpdateComposition
|
||||
else
|
||||
@$input_source.bind 'text', @_UpdateComposition
|
||||
|
||||
# There is no way to detect compositionstart in opera so we poll for it.
|
||||
if $.browser.opera?
|
||||
cb = =>
|
||||
return if @in_composition
|
||||
# if there was characters that actually escaped to the input source
|
||||
# then its most probably a multibyte char.
|
||||
if @$input_source.val().length
|
||||
@_StartComposition()
|
||||
setInterval cb, 200
|
||||
|
||||
|
||||
# Handles a character key press.
|
||||
# @arg event: The jQuery keyboard Event object to handle.
|
||||
_HandleChar: (event) =>
|
||||
|
|
@ -741,26 +773,17 @@ class JQConsole
|
|||
# Allow alt key to pass through for unicode & multibyte characters.
|
||||
if @state == STATE_OUTPUT or event.metaKey or event.ctrlKey
|
||||
return true
|
||||
|
||||
|
||||
# IE & Chrome capture non-control characters and Enter.
|
||||
# Mozilla and Opera capture everything.
|
||||
|
||||
# This is the most reliable cross-browser; charCode/keyCode break on Opera.
|
||||
char_code = event.which
|
||||
|
||||
# Skip Enter on IE and Chrome and Tab & backspace on Opera.
|
||||
# Skip Enter on IE and Chrome and Tab & backspace on Opera.
|
||||
# These are handled in _HandleKey().
|
||||
if char_code in [8, 9, 13] then return false
|
||||
|
||||
# Pass control characters which are captured on Mozilla/Safari.
|
||||
if $.browser.mozilla
|
||||
if event.keyCode or event.altKey
|
||||
return true
|
||||
# Pass control characters which are captured on Opera.
|
||||
if $.browser.opera
|
||||
if event.altKey
|
||||
return true
|
||||
|
||||
|
||||
@$prompt_left.text @$prompt_left.text() + String.fromCharCode char_code
|
||||
@_ScrollToEnd()
|
||||
return false
|
||||
|
|
@ -770,12 +793,12 @@ class JQConsole
|
|||
_HandleKey: (event) =>
|
||||
# We let the browser take over during output mode.
|
||||
if @state == STATE_OUTPUT then return true
|
||||
|
||||
|
||||
key = event.keyCode or event.which
|
||||
|
||||
|
||||
# Check for char matching next time the callstack unwinds.
|
||||
setTimeout $.proxy(@_CheckMatchings, this), 0
|
||||
|
||||
|
||||
# Don't care about alt-modifier.
|
||||
if event.altKey
|
||||
return true
|
||||
|
|
@ -789,8 +812,8 @@ class JQConsole
|
|||
when KEY_TAB then @_Unindent()
|
||||
when KEY_UP then @_MoveUp()
|
||||
when KEY_DOWN then @_MoveDown()
|
||||
when KEY_PAGE_UP then @_ScrollUp()
|
||||
when KEY_PAGE_DOWN then @_ScrollDown()
|
||||
when KEY_PAGE_UP then @_ScrollPage 'up'
|
||||
when KEY_PAGE_DOWN then @_ScrollPage 'down'
|
||||
# Allow other Shift shortcuts to pass through to the browser.
|
||||
else return true
|
||||
return false
|
||||
|
|
@ -807,8 +830,8 @@ class JQConsole
|
|||
when KEY_DOWN then @_HistoryNext()
|
||||
when KEY_HOME then @MoveToStart false
|
||||
when KEY_END then @MoveToEnd false
|
||||
when KEY_PAGE_UP then @_ScrollUp()
|
||||
when KEY_PAGE_DOWN then @_ScrollDown()
|
||||
when KEY_PAGE_UP then @_ScrollPage 'up'
|
||||
when KEY_PAGE_DOWN then @_ScrollPage 'down'
|
||||
# Let any other key continue its way to keypress.
|
||||
else return true
|
||||
return false
|
||||
|
|
@ -839,6 +862,7 @@ class JQConsole
|
|||
# Handles the user pressing the Enter key.
|
||||
# @arg shift: Whether the shift key is held.
|
||||
_HandleEnter: (shift) ->
|
||||
@_EndComposition()
|
||||
if shift
|
||||
@_InsertNewLine true
|
||||
else
|
||||
|
|
@ -863,7 +887,7 @@ class JQConsole
|
|||
@input_callback = null
|
||||
if callback then callback text
|
||||
@_CheckInputQueue()
|
||||
|
||||
|
||||
if @multiline_callback
|
||||
if @async_multiline
|
||||
@multiline_callback text, continuation
|
||||
|
|
@ -871,8 +895,8 @@ class JQConsole
|
|||
continuation @multiline_callback text
|
||||
else
|
||||
continuation false
|
||||
|
||||
|
||||
|
||||
|
||||
# Returns the appropriate variables for usage in methods that depends on the
|
||||
# direction of the interaction with the console.
|
||||
_GetDirectionals: (back) ->
|
||||
|
|
@ -882,11 +906,11 @@ class JQConsole
|
|||
$prompt_rel_opposite = if back then @$prompt_after else @$prompt_before
|
||||
MoveToLimit = if back
|
||||
$.proxy @MoveToStart, @
|
||||
else
|
||||
else
|
||||
$.proxy @MoveToEnd, @
|
||||
MoveDirection = if back
|
||||
$.proxy @_MoveLeft, @
|
||||
else
|
||||
$.proxy @_MoveLeft, @
|
||||
else
|
||||
$.proxy @_MoveRight, @
|
||||
which_end = if back then 'last' else 'first'
|
||||
where_append = if back then 'prependTo' else 'appendTo'
|
||||
|
|
@ -900,7 +924,7 @@ class JQConsole
|
|||
which_end
|
||||
where_append
|
||||
}
|
||||
|
||||
|
||||
# Moves the cursor vertically in the current prompt,
|
||||
# in the same column. (Used by _MoveUp, _MoveDown)
|
||||
_VerticalMove: (up) ->
|
||||
|
|
@ -911,7 +935,7 @@ class JQConsole
|
|||
MoveToLimit
|
||||
MoveDirection
|
||||
} = @_GetDirectionals(up)
|
||||
|
||||
|
||||
if $prompt_relative.is EMPTY_SELECTOR then return
|
||||
pos = @$prompt_left.text().length
|
||||
MoveToLimit()
|
||||
|
|
@ -919,8 +943,8 @@ class JQConsole
|
|||
text = $prompt_which.text()
|
||||
$prompt_opposite.text if up then text[pos..] else text[...pos]
|
||||
$prompt_which.text if up then text[...pos] else text[pos..]
|
||||
|
||||
|
||||
|
||||
|
||||
# Moves the cursor to the line above the current one, in the same column.
|
||||
_MoveUp: ->
|
||||
@_VerticalMove true
|
||||
|
|
@ -928,7 +952,7 @@ class JQConsole
|
|||
# Moves the cursor to the line below the current one, in the same column.
|
||||
_MoveDown: ->
|
||||
@_VerticalMove()
|
||||
|
||||
|
||||
# Moves the cursor horizontally in the current prompt.
|
||||
# Used by _MoveLeft, _MoveRight
|
||||
_HorizontalMove: (whole_word, back) ->
|
||||
|
|
@ -941,7 +965,7 @@ class JQConsole
|
|||
where_append
|
||||
} = @_GetDirectionals(back)
|
||||
regexp = if back then /\w*\W*$/ else /^\w*\W*/
|
||||
|
||||
|
||||
text = $prompt_which.text()
|
||||
if text
|
||||
if whole_word
|
||||
|
|
@ -960,12 +984,12 @@ class JQConsole
|
|||
$which_line = $(EMPTY_SPAN)[where_append] $prompt_rel_opposite
|
||||
$which_line.append $(EMPTY_SPAN).text @$prompt_label.text()
|
||||
$which_line.append $(EMPTY_SPAN).text $prompt_opposite.text()
|
||||
|
||||
|
||||
$opposite_line = $prompt_relative.children()[which_end]().detach()
|
||||
@$prompt_label.text $opposite_line.children().first().text()
|
||||
$prompt_which.text $opposite_line.children().last().text()
|
||||
$prompt_opposite.text ''
|
||||
|
||||
|
||||
# Moves the cursor to the left.
|
||||
# @arg whole_word: Whether to move by a whole word rather than a character.
|
||||
_MoveLeft: (whole_word) ->
|
||||
|
|
@ -975,7 +999,7 @@ class JQConsole
|
|||
# @arg whole_word: Whether to move by a whole word rather than a character.
|
||||
_MoveRight: (whole_word) ->
|
||||
@_HorizontalMove whole_word
|
||||
|
||||
|
||||
# Moves the cursor either to the start or end of the current prompt line(s).
|
||||
_MoveTo: (all_lines, back) ->
|
||||
{
|
||||
|
|
@ -985,7 +1009,7 @@ class JQConsole
|
|||
MoveToLimit
|
||||
MoveDirection
|
||||
} = @_GetDirectionals(back)
|
||||
|
||||
|
||||
if all_lines
|
||||
# Warning! FF 3.6 hangs on is(EMPTY_SELECTOR)
|
||||
until $prompt_relative.is(EMPTY_SELECTOR) and $prompt_which.text() == ''
|
||||
|
|
@ -1069,57 +1093,58 @@ class JQConsole
|
|||
@_InsertNewLine()
|
||||
@$prompt_left.text line
|
||||
|
||||
# Scrolls the console area up one page (with animation).
|
||||
_ScrollUp: ->
|
||||
target = @$console[0].scrollTop - @$console.height()
|
||||
@$console.stop().animate {scrollTop: target}, 'fast'
|
||||
|
||||
# Scrolls the console area down one page (with animation).
|
||||
_ScrollDown: ->
|
||||
target = @$console[0].scrollTop + @$console.height()
|
||||
@$console.stop().animate {scrollTop: target}, 'fast'
|
||||
# Scrolls the console area down/up one page (with animation).
|
||||
_ScrollPage: (dir) ->
|
||||
target = @$container[0].scrollTop
|
||||
if dir == 'up'
|
||||
target -= @$container.height()
|
||||
else
|
||||
target += @$container.height()
|
||||
@$container.stop().animate {scrollTop: target}, 'fast'
|
||||
|
||||
# Scrolls the console area to its bottom;
|
||||
# Scrolls the window to the cursor vertical position.
|
||||
# Called with every input/output to the console.
|
||||
_ScrollToEnd: ->
|
||||
# Scroll console to the bottom.
|
||||
@$console.scrollTop @$console[0].scrollHeight
|
||||
|
||||
# The cursor's top position is effected by the scroll-top of the console
|
||||
# so we need to this asynchronously to give the browser a chance to
|
||||
@$container.scrollTop @$container[0].scrollHeight
|
||||
|
||||
# Move the input element to the cursor position.
|
||||
pos = @$prompt_cursor.position()
|
||||
@$input_container.css
|
||||
left: pos.left
|
||||
top: pos.top
|
||||
|
||||
# Give time for mobile browsers to zoom in on textarea
|
||||
setTimeout @ScrollWindowToPrompt.bind(@), 50
|
||||
|
||||
ScrollWindowToPrompt: ->
|
||||
# The cursor's top position is effected by the scroll-top of the console
|
||||
# so we need to this asynchronously to give the browser a chance to
|
||||
# reflow and recaluclate the cursor's possition.
|
||||
cont = =>
|
||||
line_height = @$prompt_cursor.height()
|
||||
screen_top = @$window.scrollTop()
|
||||
screen_left = @$window.scrollLeft()
|
||||
doc_height = document.documentElement.clientHeight
|
||||
pos = @$prompt_cursor.offset()
|
||||
rel_pos = @$prompt_cursor.position()
|
||||
|
||||
# Move the input element to the cursor position.
|
||||
@$input_container.css
|
||||
left: rel_pos.left
|
||||
top: rel_pos.top
|
||||
|
||||
optimal_pos = pos.top - (2 * line_height)
|
||||
# Follow the cursor vertically on mobile and desktop.
|
||||
if @isMobile and orientation?
|
||||
# Since the keyboard takes up most of the screen, we don't care about how
|
||||
# far the the cursor position from the screen top is. We just follow it.
|
||||
if screen_top < pos.top or screen_top > pos.top
|
||||
@$window.scrollTop optimal_pos
|
||||
else
|
||||
if screen_top + doc_height < pos.top
|
||||
# Scroll just to a place where the cursor is in the view port.
|
||||
@$window.scrollTop pos.top - doc_height + line_height
|
||||
else if screen_top > optimal_pos
|
||||
# If the window is scrolled beyond the cursor, scroll to the cursor's
|
||||
# position and give two line to the top.
|
||||
@$window.scrollTop pos.top
|
||||
|
||||
setTimeout cont, 0
|
||||
|
||||
line_height = @$prompt_cursor.height()
|
||||
screen_top = @$window.scrollTop()
|
||||
screen_left = @$window.scrollLeft()
|
||||
doc_height = document.documentElement.clientHeight
|
||||
pos = @$prompt_cursor.offset()
|
||||
|
||||
optimal_pos = pos.top - (2 * line_height)
|
||||
|
||||
# Follow the cursor vertically on mobile and desktop.
|
||||
if @isMobile and orientation?
|
||||
# Since the keyboard takes up most of the screen, we don't care about how
|
||||
# far the the cursor position from the screen top is. We just follow it.
|
||||
if screen_top < pos.top or screen_top > pos.top
|
||||
@$window.scrollTop optimal_pos
|
||||
else
|
||||
if screen_top + doc_height < pos.top
|
||||
# Scroll just to a place where the cursor is in the view port.
|
||||
@$window.scrollTop pos.top - doc_height + line_height
|
||||
else if screen_top > optimal_pos
|
||||
# If the window is scrolled beyond the cursor, scroll to the cursor's
|
||||
# position and give two line to the top.
|
||||
@$window.scrollTop pos.top
|
||||
|
||||
# Selects the prompt label appropriate to the current mode.
|
||||
# @arg continuation: If true, returns the continuation prompt rather than
|
||||
# the main one.
|
||||
|
|
@ -1128,14 +1153,7 @@ class JQConsole
|
|||
return if continuation then (' \n' + @prompt_label_continue) else @prompt_label_main
|
||||
else
|
||||
return if continuation then '\n ' else ' '
|
||||
|
||||
# Cross-browser outerHTML
|
||||
_outerHTML: ($elem) ->
|
||||
if document.body.outerHTML
|
||||
return $elem.get(0).outerHTML
|
||||
else
|
||||
return $(EMPTY_DIV).append($elem.eq(0).clone()).html()
|
||||
|
||||
|
||||
# Wraps a single character in an element with a <span> having a class
|
||||
# @arg $elem: The JqDom element in question
|
||||
# @arg index: the index of the character to be wrapped
|
||||
|
|
@ -1146,7 +1164,7 @@ class JQConsole
|
|||
spanHtml(cls, text[index])+
|
||||
text[index + 1...]
|
||||
$elem.html html
|
||||
|
||||
|
||||
# Walks a string of characters incrementing current_count each time a char is found
|
||||
# and decrementing each time an opposing char is found.
|
||||
# @arg text: the text in question
|
||||
|
|
@ -1170,11 +1188,11 @@ class JQConsole
|
|||
current_count++
|
||||
else if ch is opposing_char
|
||||
current_count--
|
||||
if current_count is 0
|
||||
if current_count is 0
|
||||
return {index: index, current_count: current_count}
|
||||
|
||||
return {index: -1, current_count: current_count}
|
||||
|
||||
|
||||
_ProcessMatch: (config, back, before_char) =>
|
||||
[char, opposing_char] = if back
|
||||
[
|
||||
|
|
@ -1187,7 +1205,7 @@ class JQConsole
|
|||
config['closing_char']
|
||||
]
|
||||
{$prompt_which, $prompt_relative} = @_GetDirectionals(back)
|
||||
|
||||
|
||||
current_count = 1
|
||||
found = false
|
||||
# check current line first
|
||||
|
|
@ -1210,40 +1228,40 @@ class JQConsole
|
|||
text = $elem.html()
|
||||
{index, current_count} = @_WalkCharacters text, char, opposing_char, current_count, back
|
||||
if index > -1
|
||||
# When checking for matchings ona different line going forward we must decrement
|
||||
# When checking for matchings ona different line going forward we must decrement
|
||||
# the index since the current char is not included
|
||||
if !back then index--
|
||||
@_Wrap $elem, index, config.cls
|
||||
found = true
|
||||
return false
|
||||
|
||||
|
||||
return found
|
||||
|
||||
|
||||
# Unrwaps all prevoisly matched characters.
|
||||
# Checks if the cursor's current character is one to be matched, then walks
|
||||
# the following/preceeding characters to look for the opposing character that
|
||||
# would satisfy the match. If found both characters would be wrapped with a
|
||||
# would satisfy the match. If found both characters would be wrapped with a
|
||||
# span and applied the html class that was found in the match_config.
|
||||
_CheckMatchings: (before_char) ->
|
||||
current_char = if before_char then @$prompt_left.text()[@$prompt_left.text().length - 1...] else @$prompt_right.text()[0]
|
||||
# on every move unwrap all matched elements
|
||||
# TODO(amasad): cache previous matched elements since this must be costly
|
||||
$('.' + cls, @$console).contents().unwrap() for cls in @matchings.clss
|
||||
|
||||
|
||||
if config = @matchings.closings[current_char]
|
||||
found = @_ProcessMatch config, true, before_char
|
||||
else if config = @matchings.openings[current_char]
|
||||
found = @_ProcessMatch config, false, before_char
|
||||
else if not before_char
|
||||
@_CheckMatchings true
|
||||
|
||||
|
||||
if before_char
|
||||
@_Wrap @$prompt_left, @$prompt_left.html().length - 1, config.cls if found
|
||||
else
|
||||
# Wrap current element when a matching was found
|
||||
@_Wrap @$prompt_right, 0, config.cls if found
|
||||
|
||||
|
||||
|
||||
|
||||
# Sets the prompt to the previous history item.
|
||||
_HistoryPrevious: ->
|
||||
if not @history_active then return
|
||||
|
|
@ -1261,48 +1279,48 @@ class JQConsole
|
|||
@SetPromptText @history_new
|
||||
else
|
||||
@SetPromptText @history[++@history_index]
|
||||
|
||||
|
||||
# Check if this could be the start of a composition or an update to it.
|
||||
_CheckComposition: (e) =>
|
||||
key = e.keyCode or e.which
|
||||
if $.browser.opera? and @in_composition
|
||||
@_UpdateComposition()
|
||||
if key == 229
|
||||
if @in_composition then @_UpdateComposition() else @_StartComposition()
|
||||
|
||||
|
||||
# Starts a multibyte character composition.
|
||||
_StartComposition: =>
|
||||
@$input_source.bind E_KEYPRESS, @_EndComposition
|
||||
return if @in_composition
|
||||
@in_composition = true
|
||||
@_ShowComposition()
|
||||
setTimeout @_UpdateComposition, 0
|
||||
|
||||
|
||||
# Ends a multibyte character composition.
|
||||
_EndComposition: =>
|
||||
@$input_source.unbind E_KEYPRESS, @_EndComposition
|
||||
@in_composition = false
|
||||
return if not @in_composition
|
||||
@_HideComposition()
|
||||
@$prompt_left.text @$prompt_left.text() + @$composition.text()
|
||||
@$composition.text ''
|
||||
@$input_source.val ''
|
||||
|
||||
@in_composition = false
|
||||
|
||||
# Updates a multibyte character composition.
|
||||
_UpdateComposition: (e) =>
|
||||
cb = =>
|
||||
return if not @in_composition
|
||||
@$composition.text @$input_source.val()
|
||||
setTimeout cb, 0
|
||||
|
||||
|
||||
# Shows a multibyte character composition.
|
||||
_ShowComposition: =>
|
||||
@$composition.css 'height', @$prompt_cursor.height()
|
||||
@$composition.empty()
|
||||
@$composition.appendTo @$prompt_left
|
||||
|
||||
|
||||
# Hides a multibyte character composition.
|
||||
_HideComposition: =>
|
||||
# We just detach the element because by now the text value of this element
|
||||
# is already extracted and has been put on the left of the prompt.
|
||||
@$composition.detach()
|
||||
|
||||
|
||||
$.fn.jqconsole = (header, prompt_main, prompt_continue) ->
|
||||
new JQConsole this, header, prompt_main, prompt_continue
|
||||
|
||||
50
lib/client/console/test/ansi-test.coffee
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
Ansi = $().jqconsole.Ansi
|
||||
|
||||
CssPre = 'jqconsole-ansi-'
|
||||
|
||||
describe 'Ansi', ->
|
||||
describe '#_style', ->
|
||||
ansi = null
|
||||
beforeEach -> ansi = new Ansi()
|
||||
|
||||
it 'applies and replaces font', ->
|
||||
ansi._style '11'
|
||||
assert.deepEqual ["#{CssPre}fonts-1"], ansi.klasses
|
||||
ansi._style '19'
|
||||
assert.deepEqual ["#{CssPre}fonts-9"], ansi.klasses
|
||||
|
||||
it 'applies and replaces color', ->
|
||||
ansi._style '31'
|
||||
assert.deepEqual ["#{CssPre}color-#{ansi._color(1)}"], ansi.klasses
|
||||
ansi._style '37'
|
||||
assert.deepEqual ["#{CssPre}color-#{ansi._color(7)}"], ansi.klasses
|
||||
ansi._style '39'
|
||||
assert.deepEqual [], ansi.klasses
|
||||
|
||||
it 'applies and replaces background-color', ->
|
||||
ansi._style '41'
|
||||
assert.deepEqual ["#{CssPre}background-color-#{ansi._color(1)}"], ansi.klasses
|
||||
ansi._style '47'
|
||||
assert.deepEqual ["#{CssPre}background-color-#{ansi._color(7)}"], ansi.klasses
|
||||
ansi._style '49'
|
||||
assert.deepEqual [], ansi.klasses
|
||||
|
||||
describe '#stylize', ->
|
||||
ansi = null
|
||||
beforeEach -> ansi = new Ansi()
|
||||
|
||||
it 'appends a styles', ->
|
||||
html = ansi.stylize 'test\x1B[1mhello'
|
||||
assert.equal html, "<span class=\"\">test</span><span class=\"#{CssPre}bold\">hello</span>"
|
||||
|
||||
it 'appends multiple styles', ->
|
||||
html = ansi.stylize 'test\x1B[1;2mhello'
|
||||
assert.equal html, "<span class=\"\">test</span><span class=\"#{CssPre}bold #{CssPre}lighter\">hello</span>"
|
||||
|
||||
it 'appends multiple styles at different intervals', ->
|
||||
html = ansi.stylize 'test\x1B[1mhello\x1B[2mhi'
|
||||
assert.equal html, "<span class=\"\">test</span><span class=\"#{CssPre}bold\">hello</span><span class=\"#{CssPre}bold #{CssPre}lighter\">hi</span>"
|
||||
html = ansi.stylize 'a\x1B[53mb\x1B[21mc\x1B[md'
|
||||
assert.equal html, "<span class=\"#{CssPre}bold #{CssPre}lighter\">a</span><span class=\"#{CssPre}bold #{CssPre}lighter #{CssPre}overline\">b</span><span class=\"#{CssPre}overline\">c</span><span class=\"\">d</span>"
|
||||
|
||||
|
||||
49
lib/client/console/test/history-test.coffee
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
{jqconsole, typer: {typeA, keyDown, type}} = jqconsoleSetup()
|
||||
|
||||
describe 'History', ->
|
||||
describe '#GetHistory', ->
|
||||
it 'gets the history', ->
|
||||
jqconsole.Prompt true, ->
|
||||
typeA()
|
||||
keyDown 13
|
||||
deepEqual ['a'], jqconsole.GetHistory()
|
||||
|
||||
describe '#SetHistory', ->
|
||||
it 'sets history', ->
|
||||
h = ['a', 'b']
|
||||
jqconsole.SetHistory h
|
||||
deepEqual h, jqconsole.GetHistory()
|
||||
|
||||
describe '#ResetHistory', ->
|
||||
it 'resets the history', ->
|
||||
jqconsole.ResetHistory()
|
||||
deepEqual jqconsole.history, []
|
||||
|
||||
describe 'History interaction in the prompt', ->
|
||||
it 'gets the prev history item', ->
|
||||
jqconsole.Prompt true, ->
|
||||
type 'foo'
|
||||
equal jqconsole.GetPromptText(), 'foo'
|
||||
keyDown 13
|
||||
jqconsole.Prompt true, ->
|
||||
equal jqconsole.GetPromptText(), ''
|
||||
keyDown 38
|
||||
equal jqconsole.GetPromptText(), 'foo'
|
||||
jqconsole.AbortPrompt()
|
||||
|
||||
it 'gets the next history item', ->
|
||||
jqconsole.Prompt true, ->
|
||||
type 'foo'
|
||||
keyDown 13
|
||||
jqconsole.Prompt true, ->
|
||||
type 'bar'
|
||||
keyDown 13
|
||||
jqconsole.Prompt true, ->
|
||||
keyDown 38
|
||||
equal jqconsole.GetPromptText(), 'bar'
|
||||
keyDown 38
|
||||
equal jqconsole.GetPromptText(), 'foo'
|
||||
keyDown 40
|
||||
equal jqconsole.GetPromptText(), 'bar'
|
||||
keyDown 40
|
||||
equal jqconsole.GetPromptText(), ''
|
||||
37
lib/client/console/test/index.html
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Mocha</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="vendor/mocha.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="mocha"></div>
|
||||
<script src="vendor/mocha.js"></script>
|
||||
<script>mocha.setup('bdd')</script>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../lib/jqconsole.js"></script>
|
||||
<script src="vendor/assert.js"></script>
|
||||
<script src="vendor/coffeescript.js"></script>
|
||||
|
||||
<script>
|
||||
var tests = [ 'setup.coffee'
|
||||
, 'ansi-test.coffee'
|
||||
, 'jqconsole-test.coffee'
|
||||
, 'prompt-test.coffee'
|
||||
, 'shortcuts-test.coffee'
|
||||
, 'history-test.coffee'
|
||||
]
|
||||
tests.forEach(function (test) {
|
||||
CoffeeScript.load(test, start)
|
||||
})
|
||||
function start () {
|
||||
if (start.called == null) start.called = 0;
|
||||
if (++start.called == tests.length) {
|
||||
mocha.run();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
29
lib/client/console/test/jqconsole-test.coffee
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
describe 'JQConsole', ->
|
||||
{$container, jqconsole} = jqconsoleSetup()
|
||||
describe '#constructor', ->
|
||||
|
||||
it 'instantiates', ->
|
||||
equal jqconsole.header, 'header'
|
||||
equal jqconsole.prompt_label_main, 'prompt_label'
|
||||
equal jqconsole.prompt_label_continue, 'prompt_continue'
|
||||
equal jqconsole.indent_width, 2
|
||||
equal jqconsole.GetState(), 'output'
|
||||
deepEqual jqconsole.input_queue, []
|
||||
deepEqual jqconsole.history, []
|
||||
ok jqconsole.$console.length
|
||||
ok jqconsole.$console instanceof jQuery
|
||||
equal $container.text().trim(), 'header'
|
||||
strictEqual $container.data('jqconsole'), jqconsole
|
||||
ok jqconsole.$prompt.length
|
||||
ok jqconsole.$input_source.length
|
||||
|
||||
it 'setup events', (done)->
|
||||
counter = 0
|
||||
jqconsole.$input_source.focus ->
|
||||
counter++
|
||||
jqconsole.$console.mouseup()
|
||||
fn = ->
|
||||
ok counter
|
||||
done()
|
||||
setTimeout fn, 10
|
||||
29
lib/client/console/test/matching-test.coffee
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{jqconsole, createScroll, typer: {typeA, keyDown, type}} = jqconsoleSetup()
|
||||
|
||||
describe 'Matching', ->
|
||||
afterEach ->
|
||||
jqconsole.AbortPrompt()
|
||||
|
||||
describe '#RegisterMatching', ->
|
||||
it 'Adds matching', (done) ->
|
||||
jqconsole.Prompt true, ->
|
||||
jqconsole.RegisterMatching '(', ')', 'parens'
|
||||
type 'foo ( bar )'
|
||||
keyDown 39
|
||||
check = ->
|
||||
$parens = jqconsole.$prompt.find '.parens'
|
||||
assert.equal $parens.first().text(), '('
|
||||
assert.equal $parens.last().text(), ')'
|
||||
done()
|
||||
setTimeout check, 0
|
||||
|
||||
describe '#UnRegsiterMatching', ->
|
||||
it 'Removes matching', (done) ->
|
||||
jqconsole.Prompt true, ->
|
||||
jqconsole.UnRegisterMatching '(', ')'
|
||||
type 'foo ( bar )'
|
||||
keyDown 39
|
||||
check = ->
|
||||
assert.ok !jqconsole.$prompt.find('.parens').length
|
||||
done()
|
||||
setTimeout check, 0
|
||||
65
lib/client/console/test/misc-test.coffee
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
jqconsole = type = keyDown = null
|
||||
|
||||
describe 'Misc methods', ->
|
||||
beforeEach ->
|
||||
{jqconsole, typer: {keyDown, type}} = jqconsoleSetup()
|
||||
jqconsole.Prompt true, ->
|
||||
|
||||
describe '#GetColumn', ->
|
||||
it 'should get the column number of the cursor', ->
|
||||
label_length = 'headerprompt_label'.length
|
||||
assert.equal jqconsole.GetColumn(), label_length
|
||||
type ' '
|
||||
assert.equal jqconsole.GetColumn(), label_length + 3
|
||||
|
||||
describe '#GetLine', ->
|
||||
it 'should get the line number of the cursor', ->
|
||||
assert.equal jqconsole.GetLine(), 0
|
||||
keyDown 13, shiftKey: on
|
||||
assert.equal jqconsole.GetLine(), 1
|
||||
keyDown 13, shiftKey: on
|
||||
assert.equal jqconsole.GetLine(), 2
|
||||
|
||||
describe '#SetIndentWidth', ->
|
||||
it 'changes the indent width', ->
|
||||
l = jqconsole.GetColumn()
|
||||
jqconsole.SetIndentWidth 10
|
||||
keyDown 9
|
||||
assert.equal jqconsole.GetColumn(), l + 10
|
||||
|
||||
describe '#GetIndentWidth', ->
|
||||
it 'gets the indent width', ->
|
||||
jqconsole.SetIndentWidth 20
|
||||
assert.equal jqconsole.GetIndentWidth(), 20
|
||||
|
||||
describe '#Dump', ->
|
||||
it 'dumps the console content', ->
|
||||
type 'foo'
|
||||
keyDown 13
|
||||
jqconsole.Write('wat')
|
||||
assert.equal jqconsole.Dump(), 'headerprompt_labelfoo\nwat'
|
||||
|
||||
describe '#SetPromptLabel', ->
|
||||
it 'Sets the prompt label for the next prompt', ->
|
||||
jqconsole.SetPromptLabel 'foobar123', 'shitmang'
|
||||
keyDown 13, shiftKey: on
|
||||
jqconsole.AbortPrompt()
|
||||
jqconsole.Prompt true, ->
|
||||
assert.ok jqconsole.Dump().indexOf 'foobar123' > -1
|
||||
assert.ok jqconsole.Dump().indexOf 'shitmang' > -1
|
||||
|
||||
describe '#Disable', ->
|
||||
it 'disables the console', ->
|
||||
jqconsole.Disable()
|
||||
assert.ok jqconsole.$input_source.attr 'disabled'
|
||||
|
||||
describe '#Enable', ->
|
||||
it 'enables the console', ->
|
||||
jqconsole.Disable()
|
||||
jqconsole.Enable()
|
||||
assert.ok not jqconsole.$input_source.attr 'disabled'
|
||||
|
||||
describe '#Clear', ->
|
||||
it 'clears the console', ->
|
||||
jqconsole.Clear()
|
||||
assert.equal jqconsole.Dump(), ''
|
||||
400
lib/client/console/test/prompt-test.coffee
Normal file
|
|
@ -0,0 +1,400 @@
|
|||
{jqconsole, createScroll, typer: {typeA, keyDown, type}} = jqconsoleSetup()
|
||||
|
||||
describe 'Prompt Interaction', ->
|
||||
describe '#Prompt', ->
|
||||
after ->
|
||||
jqconsole.AbortPrompt()
|
||||
|
||||
it 'inits prompt and auto-focuses', ->
|
||||
counter = 0
|
||||
jqconsole.$input_source.focus ->
|
||||
counter++
|
||||
resultCb = ->
|
||||
jqconsole.Prompt true, resultCb
|
||||
equal jqconsole.GetState(), 'prompt'
|
||||
ok counter
|
||||
ok jqconsole.history_active
|
||||
strictEqual jqconsole.input_callback, resultCb
|
||||
equal jqconsole.$prompt.text().trim(), 'prompt_label'
|
||||
|
||||
describe '#AbortPrompt', ->
|
||||
it 'aborts the prompt', ->
|
||||
jqconsole.Prompt true, ->
|
||||
jqconsole.AbortPrompt()
|
||||
equal jqconsole.$prompt.text().trim(), ''
|
||||
|
||||
it 'restarts queued prompts', ->
|
||||
aCb = ->
|
||||
jqconsole.Prompt false, aCb
|
||||
bCb = ->
|
||||
jqconsole.Prompt true, bCb
|
||||
strictEqual jqconsole.input_callback, aCb
|
||||
strictEqual jqconsole.history_active, false
|
||||
jqconsole.AbortPrompt()
|
||||
strictEqual jqconsole.input_callback, bCb
|
||||
strictEqual jqconsole.history_active, true
|
||||
jqconsole.AbortPrompt()
|
||||
|
||||
describe '#GetPromptText', ->
|
||||
beforeEach -> jqconsole.Prompt true, ->
|
||||
afterEach -> jqconsole.AbortPrompt()
|
||||
|
||||
it 'gets the current prompt text', ->
|
||||
type 'foo'
|
||||
equal jqconsole.$prompt.text().trim(), 'prompt_labelfoo'
|
||||
equal jqconsole.GetPromptText(), 'foo'
|
||||
|
||||
it 'gets the current prompt text with the label', ->
|
||||
type 'foo'
|
||||
equal jqconsole.$prompt.text().trim(), 'prompt_labelfoo'
|
||||
equal jqconsole.GetPromptText(true), 'prompt_labelfoo'
|
||||
|
||||
describe '#ClearPromptText', ->
|
||||
beforeEach -> jqconsole.Prompt true, ->
|
||||
afterEach -> jqconsole.AbortPrompt()
|
||||
|
||||
it 'Clears the current prompt text', ->
|
||||
type 'foo'
|
||||
equal jqconsole.GetPromptText(), 'foo'
|
||||
jqconsole.ClearPromptText()
|
||||
equal jqconsole.GetPromptText(), ''
|
||||
|
||||
it 'Clears prompt text with label', ->
|
||||
type 'foo'
|
||||
equal jqconsole.GetPromptText(), 'foo'
|
||||
jqconsole.ClearPromptText true
|
||||
equal jqconsole.GetPromptText(true), ''
|
||||
|
||||
describe '#SetPromptText', ->
|
||||
beforeEach -> jqconsole.Prompt true, ->
|
||||
afterEach -> jqconsole.AbortPrompt()
|
||||
|
||||
it 'sets the current prompt text', ->
|
||||
type 'bar'
|
||||
jqconsole.SetPromptText('foo')
|
||||
equal jqconsole.GetPromptText(), 'foo'
|
||||
|
||||
describe 'Moving', ->
|
||||
beforeEach -> jqconsole.Prompt true, ->
|
||||
afterEach -> jqconsole.AbortPrompt()
|
||||
|
||||
it 'moves to the left', ->
|
||||
type 'xyz'
|
||||
keyDown 37
|
||||
equal jqconsole.$prompt_left.text().trim(), 'xy'
|
||||
keyDown 37
|
||||
equal jqconsole.$prompt_left.text().trim(), 'x'
|
||||
keyDown 37
|
||||
equal jqconsole.$prompt_left.text().trim(), ''
|
||||
keyDown 37
|
||||
equal jqconsole.$prompt_left.text().trim(), ''
|
||||
|
||||
it 'moves to the right', ->
|
||||
type 'xyz'
|
||||
keyDown 37
|
||||
keyDown 37
|
||||
equal jqconsole.$prompt_left.text().trim(), 'x'
|
||||
keyDown 39
|
||||
equal jqconsole.$prompt_left.text().trim(), 'xy'
|
||||
keyDown 39
|
||||
equal jqconsole.$prompt_left.text().trim(), 'xyz'
|
||||
keyDown 39
|
||||
equal jqconsole.$prompt_left.text().trim(), 'xyz'
|
||||
|
||||
it 'moves to the prev line when at the first char of the line moving left', ->
|
||||
type 'xyz'
|
||||
keyDown 13, shiftKey: on
|
||||
type 'abc'
|
||||
equal jqconsole.$prompt_left.text().trim(), 'abc'
|
||||
keyDown 37
|
||||
keyDown 37
|
||||
keyDown 37
|
||||
keyDown 37
|
||||
equal jqconsole.$prompt_left.text().trim(), 'xyz'
|
||||
|
||||
it 'moves to the next line when at the last char of the line moving right', ->
|
||||
type 'xyz'
|
||||
keyDown 13, shiftKey: on
|
||||
type 'abc'
|
||||
equal jqconsole.$prompt_left.text().trim(), 'abc'
|
||||
keyDown 37
|
||||
keyDown 37
|
||||
keyDown 37
|
||||
keyDown 37
|
||||
equal jqconsole.$prompt_left.text().trim(), 'xyz'
|
||||
keyDown 39
|
||||
equal jqconsole.$prompt_right.text().trim(), 'abc'
|
||||
|
||||
it 'moves to the start of the word', ->
|
||||
type 'xyz abc'
|
||||
keyDown 37, metaKey: on
|
||||
equal jqconsole.$prompt_right.text().trim(), 'abc'
|
||||
equal jqconsole.$prompt_left.text().trim(), 'xyz'
|
||||
|
||||
it 'moves to the end of the word', ->
|
||||
type 'xyz abc'
|
||||
keyDown 37, metaKey: on
|
||||
keyDown 37, metaKey: on
|
||||
keyDown 39, metaKey: on
|
||||
equal jqconsole.$prompt_right.text().trim(), 'abc'
|
||||
equal jqconsole.$prompt_left.text().trim(), 'xyz'
|
||||
|
||||
it 'moves to the end of the word', ->
|
||||
type 'xyz abc'
|
||||
keyDown 37, metaKey: on
|
||||
keyDown 37, metaKey: on
|
||||
keyDown 39, metaKey: on
|
||||
equal jqconsole.$prompt_right.text().trim(), 'abc'
|
||||
equal jqconsole.$prompt_left.text().trim(), 'xyz'
|
||||
|
||||
it 'moves to the start of the line', ->
|
||||
type 'xyz abc'
|
||||
keyDown 36
|
||||
equal jqconsole.$prompt_right.text().trim(), 'xyz abc'
|
||||
|
||||
it 'moves to the end of the line', ->
|
||||
type 'xyz abc'
|
||||
keyDown 36
|
||||
equal jqconsole.$prompt_right.text().trim(), 'xyz abc'
|
||||
keyDown 35
|
||||
equal jqconsole.$prompt_right.text().trim(), ''
|
||||
equal jqconsole.$prompt_left.text().trim(), 'xyz abc'
|
||||
|
||||
it 'moves to the start of the prompt', ->
|
||||
type 'xyz abc'
|
||||
keyDown 13, shiftKey: on
|
||||
type 'hafm olim'
|
||||
keyDown 36, metaKey: on
|
||||
equal jqconsole.$prompt_right.text().trim(), 'xyz abc'
|
||||
equal jqconsole.$prompt_after.text().trim(), 'prompt_continuehafm olim'
|
||||
|
||||
it 'moves to the end of the prompt', ->
|
||||
type 'xyz abc'
|
||||
keyDown 13, shiftKey: on
|
||||
type 'hafm olim'
|
||||
keyDown 36, metaKey: on
|
||||
equal jqconsole.$prompt_right.text().trim(), 'xyz abc'
|
||||
equal jqconsole.$prompt_after.text().trim(), 'prompt_continuehafm olim'
|
||||
keyDown 35, metaKey: on
|
||||
equal jqconsole.$prompt_left.text().trim(), 'hafm olim'
|
||||
equal jqconsole.$prompt_before.text().trim(), 'prompt_labelxyz abc'
|
||||
|
||||
it 'moves up one line', ->
|
||||
type 'xyz'
|
||||
keyDown 13, shiftKey: on
|
||||
type 'a'
|
||||
keyDown 38, shiftKey: on
|
||||
equal jqconsole.$prompt_right.text().trim(), 'yz'
|
||||
|
||||
it 'moves down one line', ->
|
||||
type 'xyz'
|
||||
keyDown 13, shiftKey: on
|
||||
type 'a'
|
||||
# Meta key also works.
|
||||
keyDown 38, metaKey: on
|
||||
equal jqconsole.$prompt_right.text().trim(), 'yz'
|
||||
keyDown 40, metaKey: on
|
||||
equal jqconsole.$prompt_right.text().trim(), ''
|
||||
|
||||
it 'respects the column when moving vertically', ->
|
||||
type 'xyz'
|
||||
keyDown 13, shiftKey: on
|
||||
type 'ab'
|
||||
keyDown 38, shiftKey: on
|
||||
equal jqconsole.$prompt_right.text().trim(), 'z'
|
||||
keyDown 40, shiftKey: on
|
||||
keyDown 37
|
||||
keyDown 37
|
||||
equal jqconsole.$prompt_right.text().trim(), 'ab'
|
||||
keyDown 38, shiftKey: on
|
||||
equal jqconsole.$prompt_right.text().trim(), 'xyz'
|
||||
|
||||
describe 'Control Keys', ->
|
||||
beforeEach -> jqconsole.Prompt true, ->
|
||||
afterEach -> jqconsole.AbortPrompt()
|
||||
|
||||
it 'handles enter', ->
|
||||
jqconsole.AbortPrompt()
|
||||
counter = 0
|
||||
jqconsole.Prompt true, -> counter++
|
||||
typeA()
|
||||
keyDown 13
|
||||
ok counter
|
||||
equal jqconsole.$console.find('.jqconsole-old-prompt').last().text().trim(), 'prompt_labela'
|
||||
# Restart the prompt for other tests.
|
||||
jqconsole.Prompt true, ->
|
||||
|
||||
it 'handles shift+enter', ->
|
||||
keyDown 13, shiftKey: on
|
||||
equal jqconsole.$prompt.text().trim(), 'prompt_label \nprompt_continue'
|
||||
|
||||
it 'handles tab', ->
|
||||
typeA()
|
||||
keyDown 9
|
||||
equal jqconsole.$prompt.text().trim(), 'prompt_label a'
|
||||
|
||||
it 'handles shift+tab', ->
|
||||
typeA()
|
||||
keyDown 9, shiftKey: on
|
||||
equal jqconsole.$prompt.text().trim(), 'prompt_labela'
|
||||
|
||||
it 'backspace', ->
|
||||
typeA()
|
||||
keyDown 8
|
||||
equal jqconsole.$prompt.text().trim(), 'prompt_label'
|
||||
|
||||
it 'cntrl+backspace', ->
|
||||
typeA()
|
||||
typeA()
|
||||
keyDown 8, metaKey: on
|
||||
equal jqconsole.$prompt.text().trim(), 'prompt_label'
|
||||
|
||||
it 'deletes a char', ->
|
||||
type 'xyz'
|
||||
keyDown 37
|
||||
equal jqconsole.$prompt_right.text().trim(), 'z'
|
||||
keyDown 46
|
||||
equal jqconsole.$prompt_right.text().trim(), ''
|
||||
|
||||
it 'deletes a word', ->
|
||||
type 'xyz abc'
|
||||
keyDown 37
|
||||
keyDown 37
|
||||
keyDown 37
|
||||
equal jqconsole.$prompt_right.text().trim(), 'abc'
|
||||
keyDown 46, metaKey: on
|
||||
equal jqconsole.$prompt_right.text().trim(), ''
|
||||
|
||||
describe 'scrolling', ->
|
||||
console_height = null
|
||||
_fast = null
|
||||
|
||||
before ->
|
||||
jQuery.fx.speeds.fast = 10
|
||||
after ->
|
||||
jQuery.fx.speeds.fast = _fast
|
||||
|
||||
beforeEach ->
|
||||
jqconsole.Reset()
|
||||
jqconsole.Prompt true, ->
|
||||
# Make sure the console has a scroll.
|
||||
{ console_height } = createScroll()
|
||||
|
||||
it 'scrolls up', (done) ->
|
||||
before = jqconsole.$container[0].scrollTop
|
||||
keyDown 33
|
||||
cb = ->
|
||||
equal jqconsole.$container[0].scrollTop, before - console_height
|
||||
done()
|
||||
# * 2 is some arbitrary number otherwise it fails.
|
||||
setTimeout cb, jQuery.fx.speeds.fast * 2
|
||||
|
||||
it 'scrolls up twice', (done) ->
|
||||
before = jqconsole.$container[0].scrollTop
|
||||
keyDown 33
|
||||
cb = ->
|
||||
keyDown 33
|
||||
cb = ->
|
||||
equal jqconsole.$container[0].scrollTop, before - (console_height * 2)
|
||||
done()
|
||||
setTimeout cb, jQuery.fx.speeds.fast * 2
|
||||
# * 2 is some arbitrary number otherwise it fails.
|
||||
setTimeout cb, jQuery.fx.speeds.fast * 2
|
||||
|
||||
it 'scrolls down', (done) ->
|
||||
before = jqconsole.$container[0].scrollTop
|
||||
keyDown 33
|
||||
cb = ->
|
||||
keyDown 34
|
||||
cb = ->
|
||||
equal jqconsole.$container[0].scrollTop, before
|
||||
done()
|
||||
setTimeout cb, jQuery.fx.speeds.fast * 2
|
||||
# * 2 is some arbitrary number otherwise it fails.
|
||||
setTimeout cb, jQuery.fx.speeds.fast * 2
|
||||
|
||||
describe 'Typing', ->
|
||||
beforeEach -> jqconsole.Prompt true, ->
|
||||
afterEach -> jqconsole.AbortPrompt()
|
||||
|
||||
it 'handles chars', ->
|
||||
str = ''
|
||||
test = (ch) ->
|
||||
str += ch
|
||||
e = $.Event('keypress')
|
||||
e.which = ch.charCodeAt(0)
|
||||
jqconsole.$input_source.trigger e
|
||||
equal jqconsole.$prompt.text().trim(), 'prompt_label' + str
|
||||
|
||||
test 'a'
|
||||
test 'Z'
|
||||
test '$'
|
||||
test 'ƒ'
|
||||
|
||||
it 'scrolls all the way down when typing', (done) ->
|
||||
createScroll()
|
||||
keyDown 33
|
||||
cb = ->
|
||||
before = jqconsole.$container[0].scrollTop
|
||||
type('a')
|
||||
cb = ->
|
||||
notEqual jqconsole.$container[0].scrollTop, before
|
||||
done()
|
||||
setTimeout cb, 0
|
||||
setTimeout cb, jQuery.fx.speeds.fast * 2
|
||||
|
||||
describe 'Multiline', ->
|
||||
beforeEach ->
|
||||
if jqconsole.GetState() is 'prompt'
|
||||
jqconsole.AbortPrompt()
|
||||
|
||||
it 'executes multiline callback', (done) ->
|
||||
jqconsole.Prompt true, (-> ), ->
|
||||
done()
|
||||
type('foo')
|
||||
keyDown 13
|
||||
|
||||
it 'indents', ->
|
||||
jqconsole.Prompt true, (-> ), ->
|
||||
return 2
|
||||
type('foo')
|
||||
keyDown 13
|
||||
equal jqconsole.GetState(), 'prompt'
|
||||
equal jqconsole.GetPromptText(), 'foo\n '
|
||||
|
||||
it 'keeps indentation on shift+enter', ->
|
||||
jqconsole.Prompt true, (-> ), ->
|
||||
return 2
|
||||
type('foo')
|
||||
keyDown 9
|
||||
keyDown 13, shiftKey: on
|
||||
equal jqconsole.GetState(), 'prompt'
|
||||
equal jqconsole.GetPromptText(), ' foo\n '
|
||||
|
||||
it 'unindents', ->
|
||||
jqconsole.Prompt true, (-> ), ->
|
||||
return -2
|
||||
type('foo')
|
||||
keyDown 9
|
||||
keyDown 13
|
||||
equal jqconsole.GetPromptText(), ' foo\n'
|
||||
|
||||
it 'skip indent callback', (done) ->
|
||||
jqconsole.Prompt true, done.bind(null, null), ->
|
||||
return false
|
||||
type('foo')
|
||||
keyDown 13
|
||||
|
||||
it 'handles async treatment', (done) ->
|
||||
jqconsole.Prompt true, done.bind(null, null), ((text, cb) -> cb false), on
|
||||
type('foo')
|
||||
keyDown 13
|
||||
|
||||
describe '#Input', ->
|
||||
it 'should enable history', (done) ->
|
||||
jqconsole.Prompt true, done.bind(null, null)
|
||||
jqconsole.Input (text) ->
|
||||
assert.equal text, 'foo'
|
||||
setTimeout (-> keyDown(13)), 0
|
||||
type 'foo'
|
||||
keyDown 13
|
||||
44
lib/client/console/test/setup.coffee
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
window.equal = assert.equal
|
||||
window.notEqual = assert.notEqual
|
||||
window.deepEqual = assert.deepEqual
|
||||
window.strictEqual = assert.strictEqual
|
||||
window.ok = assert.ok
|
||||
JQConsole = $().jqconsole.JQConsole
|
||||
|
||||
window.jqconsoleSetup = ->
|
||||
$container = $('<div/>').css
|
||||
height: '100px'
|
||||
widht: '200px'
|
||||
position: 'relative'
|
||||
$container.appendTo('body')
|
||||
jqconsole = new JQConsole($container, 'header', 'prompt_label', 'prompt_continue')
|
||||
typer =
|
||||
typeA: ->
|
||||
e = $.Event('keypress')
|
||||
e.which = 'a'.charCodeAt(0)
|
||||
jqconsole.$input_source.trigger e
|
||||
|
||||
keyDown: (code, options = {}) ->
|
||||
e = $.Event('keydown')
|
||||
e.which = code
|
||||
e[k] = v for k, v of options
|
||||
jqconsole.$input_source.trigger e
|
||||
|
||||
type: (str) ->
|
||||
type = (chr) ->
|
||||
e = $.Event('keypress')
|
||||
e.which = chr.charCodeAt(0)
|
||||
jqconsole.$input_source.trigger(e)
|
||||
type chr for chr in str
|
||||
|
||||
createScroll = ->
|
||||
line_height = jqconsole.$prompt.height()
|
||||
console_height = jqconsole.$container.height()
|
||||
lines_per_page = Math.ceil(console_height / line_height)
|
||||
for i in [0..lines_per_page * 5]
|
||||
jqconsole.SetPromptText('foo')
|
||||
jqconsole._HandleEnter()
|
||||
jqconsole.Prompt true, ->
|
||||
{line_height, console_height, lines_per_page}
|
||||
|
||||
{$container, jqconsole, typer, createScroll}
|
||||
60
lib/client/console/test/shortcuts-test.coffee
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
{jqconsole, typer: {keyDown}} = jqconsoleSetup()
|
||||
|
||||
describe 'Shortcuts', ->
|
||||
describe '#RegisterShortcut', ->
|
||||
# Fails in v2.7.7
|
||||
it 'throws if callback not function', ->
|
||||
assert.throws ->
|
||||
jqconsole.RegisterShortcut 'b', 'c'
|
||||
|
||||
it 'registers shortcut by string', ->
|
||||
cb = ->
|
||||
jqconsole.RegisterShortcut 'a', cb
|
||||
deepEqual jqconsole.shortcuts['a'.charCodeAt(0)], [cb]
|
||||
deepEqual jqconsole.shortcuts['A'.charCodeAt(0)], [cb]
|
||||
|
||||
it 'registers shortcut by charcode', ->
|
||||
cb = ->
|
||||
jqconsole.RegisterShortcut 'c'.charCodeAt(0), cb
|
||||
deepEqual jqconsole.shortcuts['c'.charCodeAt(0)], [cb]
|
||||
deepEqual jqconsole.shortcuts['C'.charCodeAt(0)], [cb]
|
||||
|
||||
it 'shortcuts must be ascii', ->
|
||||
assert.throws ->
|
||||
jqconsole.RegisterShortcut 'ƒ', ->
|
||||
|
||||
describe '#UnRegisterShortcut', ->
|
||||
|
||||
it 'removes all callback for a shortcut', ->
|
||||
cb = ->
|
||||
jqconsole.RegisterShortcut 'a', cb
|
||||
jqconsole.UnRegisterShortcut 'a'
|
||||
deepEqual jqconsole.shortcuts['a'.charCodeAt(0)], undefined
|
||||
|
||||
it 'removes specific callback', ->
|
||||
aCb = ->
|
||||
bCb = ->
|
||||
jqconsole.RegisterShortcut 'a', aCb
|
||||
jqconsole.RegisterShortcut 'a', bCb
|
||||
jqconsole.UnRegisterShortcut 'a', aCb
|
||||
deepEqual jqconsole.shortcuts['a'.charCodeAt(0)], [bCb]
|
||||
|
||||
describe '#ResetShortcuts', ->
|
||||
|
||||
it 'resets all shortcuts', ->
|
||||
cb1 = ->
|
||||
cb2 = ->
|
||||
jqconsole.RegisterShortcut 'a', cb1
|
||||
jqconsole.RegisterShortcut 'b', cb2
|
||||
jqconsole.ResetShortcuts()
|
||||
deepEqual jqconsole.shortcuts, {}
|
||||
|
||||
describe 'Invoking Shortcuts', ->
|
||||
it 'invokes shortcuts', ->
|
||||
jqconsole.Prompt true, ->
|
||||
counter = 0
|
||||
jqconsole.RegisterShortcut 'a', ->
|
||||
strictEqual this, jqconsole
|
||||
counter++
|
||||
keyDown 'a'.charCodeAt(0), metaKey: on
|
||||
ok counter
|
||||
369
lib/client/console/test/vendor/assert.js
vendored
Normal file
|
|
@ -0,0 +1,369 @@
|
|||
// http://wiki.commonjs.org/wiki/Unit_Testing/1.0
|
||||
//
|
||||
// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8!
|
||||
//
|
||||
// Copyright (c) 2011 Jxck
|
||||
//
|
||||
// Originally from node.js (http://nodejs.org)
|
||||
// Copyright Joyent, Inc.
|
||||
//
|
||||
// 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 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.
|
||||
|
||||
(function(global) {
|
||||
|
||||
// Object.create compatible in IE
|
||||
var create = Object.create || function(p) {
|
||||
if (!p) throw Error('no type');
|
||||
function f() {};
|
||||
f.prototype = p;
|
||||
return new f();
|
||||
};
|
||||
|
||||
// UTILITY
|
||||
var util = {
|
||||
inherits: function(ctor, superCtor) {
|
||||
ctor.super_ = superCtor;
|
||||
ctor.prototype = create(superCtor.prototype, {
|
||||
constructor: {
|
||||
value: ctor,
|
||||
enumerable: false,
|
||||
writable: true,
|
||||
configurable: true
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var pSlice = Array.prototype.slice;
|
||||
|
||||
// 1. The assert module provides functions that throw
|
||||
// AssertionError's when particular conditions are not met. The
|
||||
// assert module must conform to the following interface.
|
||||
|
||||
var assert = ok;
|
||||
|
||||
global['assert'] = assert;
|
||||
|
||||
if (typeof module === 'object' && typeof module.exports === 'object') {
|
||||
module.exports = assert;
|
||||
};
|
||||
|
||||
// 2. The AssertionError is defined in assert.
|
||||
// new assert.AssertionError({ message: message,
|
||||
// actual: actual,
|
||||
// expected: expected })
|
||||
|
||||
assert.AssertionError = function AssertionError(options) {
|
||||
this.name = 'AssertionError';
|
||||
this.message = options.message || (options.actual + ' ' + options.operator + ' ' +options.expected)
|
||||
this.actual = options.actual;
|
||||
this.expected = options.expected;
|
||||
this.operator = options.operator;
|
||||
var stackStartFunction = options.stackStartFunction || fail;
|
||||
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(this, stackStartFunction);
|
||||
}
|
||||
};
|
||||
util.inherits(assert.AssertionError, Error);
|
||||
|
||||
function replacer(key, value) {
|
||||
if (value === undefined) {
|
||||
return '' + value;
|
||||
}
|
||||
if (typeof value === 'number' && (isNaN(value) || !isFinite(value))) {
|
||||
return value.toString();
|
||||
}
|
||||
if (typeof value === 'function' || value instanceof RegExp) {
|
||||
return value.toString();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function truncate(s, n) {
|
||||
if (typeof s == 'string') {
|
||||
return s.length < n ? s : s.slice(0, n);
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
assert.AssertionError.prototype.toString = function() {
|
||||
if (this.message) {
|
||||
return [this.name + ':', this.message].join(' ');
|
||||
} else {
|
||||
return [
|
||||
this.name + ':',
|
||||
truncate(JSON.stringify(this.actual, replacer), 128),
|
||||
this.operator,
|
||||
truncate(JSON.stringify(this.expected, replacer), 128)
|
||||
].join(' ');
|
||||
}
|
||||
};
|
||||
|
||||
// assert.AssertionError instanceof Error
|
||||
|
||||
assert.AssertionError.__proto__ = Error.prototype;
|
||||
|
||||
// At present only the three keys mentioned above are used and
|
||||
// understood by the spec. Implementations or sub modules can pass
|
||||
// other keys to the AssertionError's constructor - they will be
|
||||
// ignored.
|
||||
|
||||
// 3. All of the following functions must throw an AssertionError
|
||||
// when a corresponding condition is not met, with a message that
|
||||
// may be undefined if not provided. All assertion methods provide
|
||||
// both the actual and expected values to the assertion error for
|
||||
// display purposes.
|
||||
|
||||
function fail(actual, expected, message, operator, stackStartFunction) {
|
||||
throw new assert.AssertionError({
|
||||
message: message,
|
||||
actual: actual,
|
||||
expected: expected,
|
||||
operator: operator,
|
||||
stackStartFunction: stackStartFunction
|
||||
});
|
||||
}
|
||||
|
||||
// EXTENSION! allows for well behaved errors defined elsewhere.
|
||||
assert.fail = fail;
|
||||
|
||||
// 4. Pure assertion tests whether a value is truthy, as determined
|
||||
// by !!guard.
|
||||
// assert.ok(guard, message_opt);
|
||||
// This statement is equivalent to assert.equal(true, !!guard,
|
||||
// message_opt);. To test strictly for the value true, use
|
||||
// assert.strictEqual(true, guard, message_opt);.
|
||||
|
||||
function ok(value, message) {
|
||||
if (!!!value) fail(value, true, message, '==', assert.ok);
|
||||
}
|
||||
assert.ok = ok;
|
||||
|
||||
// 5. The equality assertion tests shallow, coercive equality with
|
||||
// ==.
|
||||
// assert.equal(actual, expected, message_opt);
|
||||
|
||||
assert.equal = function equal(actual, expected, message) {
|
||||
if (actual != expected) fail(actual, expected, message, '==', assert.equal);
|
||||
};
|
||||
|
||||
// 6. The non-equality assertion tests for whether two objects are not equal
|
||||
// with != assert.notEqual(actual, expected, message_opt);
|
||||
|
||||
assert.notEqual = function notEqual(actual, expected, message) {
|
||||
if (actual == expected) {
|
||||
fail(actual, expected, message, '!=', assert.notEqual);
|
||||
}
|
||||
};
|
||||
|
||||
// 7. The equivalence assertion tests a deep equality relation.
|
||||
// assert.deepEqual(actual, expected, message_opt);
|
||||
|
||||
assert.deepEqual = function deepEqual(actual, expected, message) {
|
||||
if (!_deepEqual(actual, expected)) {
|
||||
fail(actual, expected, message, 'deepEqual', assert.deepEqual);
|
||||
}
|
||||
};
|
||||
|
||||
function _deepEqual(actual, expected) {
|
||||
// 7.1. All identical values are equivalent, as determined by ===.
|
||||
if (actual === expected) {
|
||||
return true;
|
||||
|
||||
// } else if (Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) {
|
||||
// if (actual.length != expected.length) return false;
|
||||
//
|
||||
// for (var i = 0; i < actual.length; i++) {
|
||||
// if (actual[i] !== expected[i]) return false;
|
||||
// }
|
||||
//
|
||||
// return true;
|
||||
//
|
||||
// 7.2. If the expected value is a Date object, the actual value is
|
||||
// equivalent if it is also a Date object that refers to the same time.
|
||||
} else if (actual instanceof Date && expected instanceof Date) {
|
||||
return actual.getTime() === expected.getTime();
|
||||
|
||||
// 7.3 If the expected value is a RegExp object, the actual value is
|
||||
// equivalent if it is also a RegExp object with the same source and
|
||||
// properties (`global`, `multiline`, `lastIndex`, `ignoreCase`).
|
||||
} else if (actual instanceof RegExp && expected instanceof RegExp) {
|
||||
return actual.source === expected.source &&
|
||||
actual.global === expected.global &&
|
||||
actual.multiline === expected.multiline &&
|
||||
actual.lastIndex === expected.lastIndex &&
|
||||
actual.ignoreCase === expected.ignoreCase;
|
||||
|
||||
// 7.4. Other pairs that do not both pass typeof value == 'object',
|
||||
// equivalence is determined by ==.
|
||||
} else if (typeof actual != 'object' && typeof expected != 'object') {
|
||||
return actual == expected;
|
||||
|
||||
// 7.5 For all other Object pairs, including Array objects, equivalence is
|
||||
// determined by having the same number of owned properties (as verified
|
||||
// with Object.prototype.hasOwnProperty.call), the same set of keys
|
||||
// (although not necessarily the same order), equivalent values for every
|
||||
// corresponding key, and an identical 'prototype' property. Note: this
|
||||
// accounts for both named and indexed properties on Arrays.
|
||||
} else {
|
||||
return objEquiv(actual, expected);
|
||||
}
|
||||
}
|
||||
|
||||
function isUndefinedOrNull(value) {
|
||||
return value === null || value === undefined;
|
||||
}
|
||||
|
||||
function isArguments(object) {
|
||||
return Object.prototype.toString.call(object) == '[object Arguments]';
|
||||
}
|
||||
|
||||
function objEquiv(a, b) {
|
||||
if (isUndefinedOrNull(a) || isUndefinedOrNull(b))
|
||||
return false;
|
||||
// an identical 'prototype' property.
|
||||
if (a.prototype !== b.prototype) return false;
|
||||
//~~~I've managed to break Object.keys through screwy arguments passing.
|
||||
// Converting to array solves the problem.
|
||||
if (isArguments(a)) {
|
||||
if (!isArguments(b)) {
|
||||
return false;
|
||||
}
|
||||
a = pSlice.call(a);
|
||||
b = pSlice.call(b);
|
||||
return _deepEqual(a, b);
|
||||
}
|
||||
try {
|
||||
var ka = Object.keys(a),
|
||||
kb = Object.keys(b),
|
||||
key, i;
|
||||
} catch (e) {//happens when one is a string literal and the other isn't
|
||||
return false;
|
||||
}
|
||||
// having the same number of owned properties (keys incorporates
|
||||
// hasOwnProperty)
|
||||
if (ka.length != kb.length)
|
||||
return false;
|
||||
//the same set of keys (although not necessarily the same order),
|
||||
ka.sort();
|
||||
kb.sort();
|
||||
//~~~cheap key test
|
||||
for (i = ka.length - 1; i >= 0; i--) {
|
||||
if (ka[i] != kb[i])
|
||||
return false;
|
||||
}
|
||||
//equivalent values for every corresponding key, and
|
||||
//~~~possibly expensive deep test
|
||||
for (i = ka.length - 1; i >= 0; i--) {
|
||||
key = ka[i];
|
||||
if (!_deepEqual(a[key], b[key])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 8. The non-equivalence assertion tests for any deep inequality.
|
||||
// assert.notDeepEqual(actual, expected, message_opt);
|
||||
|
||||
assert.notDeepEqual = function notDeepEqual(actual, expected, message) {
|
||||
if (_deepEqual(actual, expected)) {
|
||||
fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual);
|
||||
}
|
||||
};
|
||||
|
||||
// 9. The strict equality assertion tests strict equality, as determined by ===.
|
||||
// assert.strictEqual(actual, expected, message_opt);
|
||||
|
||||
assert.strictEqual = function strictEqual(actual, expected, message) {
|
||||
if (actual !== expected) {
|
||||
fail(actual, expected, message, '===', assert.strictEqual);
|
||||
}
|
||||
};
|
||||
|
||||
// 10. The strict non-equality assertion tests for strict inequality, as
|
||||
// determined by !==. assert.notStrictEqual(actual, expected, message_opt);
|
||||
|
||||
assert.notStrictEqual = function notStrictEqual(actual, expected, message) {
|
||||
if (actual === expected) {
|
||||
fail(actual, expected, message, '!==', assert.notStrictEqual);
|
||||
}
|
||||
};
|
||||
|
||||
function expectedException(actual, expected) {
|
||||
if (!actual || !expected) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (expected instanceof RegExp) {
|
||||
return expected.test(actual);
|
||||
} else if (actual instanceof expected) {
|
||||
return true;
|
||||
} else if (expected.call({}, actual) === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function _throws(shouldThrow, block, expected, message) {
|
||||
var actual;
|
||||
|
||||
if (typeof expected === 'string') {
|
||||
message = expected;
|
||||
expected = null;
|
||||
}
|
||||
|
||||
try {
|
||||
block();
|
||||
} catch (e) {
|
||||
actual = e;
|
||||
}
|
||||
|
||||
message = (expected && expected.name ? ' (' + expected.name + ').' : '.') +
|
||||
(message ? ' ' + message : '.');
|
||||
|
||||
if (shouldThrow && !actual) {
|
||||
fail('Missing expected exception' + message);
|
||||
}
|
||||
|
||||
if (!shouldThrow && expectedException(actual, expected)) {
|
||||
fail('Got unwanted exception' + message);
|
||||
}
|
||||
|
||||
if ((shouldThrow && actual && expected &&
|
||||
!expectedException(actual, expected)) || (!shouldThrow && actual)) {
|
||||
throw actual;
|
||||
}
|
||||
}
|
||||
|
||||
// 11. Expected to throw an error:
|
||||
// assert.throws(block, Error_opt, message_opt);
|
||||
|
||||
assert.throws = function(block, /*optional*/error, /*optional*/message) {
|
||||
_throws.apply(this, [true].concat(pSlice.call(arguments)));
|
||||
};
|
||||
|
||||
// EXTENSION! This is annoying to write outside this module.
|
||||
assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) {
|
||||
_throws.apply(this, [false].concat(pSlice.call(arguments)));
|
||||
};
|
||||
|
||||
assert.ifError = function(err) { if (err) {throw err;}};
|
||||
|
||||
})(this);
|
||||
12
lib/client/console/test/vendor/coffeescript.js
vendored
Normal file
9111
lib/client/console/test/vendor/jquery-2.1.0.js
vendored
Normal file
241
lib/client/console/test/vendor/mocha.css
vendored
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
@charset "utf-8";
|
||||
|
||||
body {
|
||||
font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
padding: 60px 50px;
|
||||
}
|
||||
|
||||
#mocha ul, #mocha li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#mocha ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
#mocha h1, #mocha h2 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#mocha h1 {
|
||||
margin-top: 15px;
|
||||
font-size: 1em;
|
||||
font-weight: 200;
|
||||
}
|
||||
|
||||
#mocha h1 a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#mocha h1 a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#mocha .suite .suite h1 {
|
||||
margin-top: 0;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#mocha h2 {
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#mocha .suite {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
#mocha .test {
|
||||
margin-left: 15px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#mocha .test.pending:hover h2::after {
|
||||
content: '(pending)';
|
||||
font-family: arial;
|
||||
}
|
||||
|
||||
#mocha .test.pass.medium .duration {
|
||||
background: #C09853;
|
||||
}
|
||||
|
||||
#mocha .test.pass.slow .duration {
|
||||
background: #B94A48;
|
||||
}
|
||||
|
||||
#mocha .test.pass::before {
|
||||
content: '✓';
|
||||
font-size: 12px;
|
||||
display: block;
|
||||
float: left;
|
||||
margin-right: 5px;
|
||||
color: #00d6b2;
|
||||
}
|
||||
|
||||
#mocha .test.pass .duration {
|
||||
font-size: 9px;
|
||||
margin-left: 5px;
|
||||
padding: 2px 5px;
|
||||
color: white;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
|
||||
-moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
-ms-border-radius: 5px;
|
||||
-o-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#mocha .test.pass.fast .duration {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#mocha .test.pending {
|
||||
color: #0b97c4;
|
||||
}
|
||||
|
||||
#mocha .test.pending::before {
|
||||
content: '◦';
|
||||
color: #0b97c4;
|
||||
}
|
||||
|
||||
#mocha .test.fail {
|
||||
color: #c00;
|
||||
}
|
||||
|
||||
#mocha .test.fail pre {
|
||||
color: black;
|
||||
}
|
||||
|
||||
#mocha .test.fail::before {
|
||||
content: '✖';
|
||||
font-size: 12px;
|
||||
display: block;
|
||||
float: left;
|
||||
margin-right: 5px;
|
||||
color: #c00;
|
||||
}
|
||||
|
||||
#mocha .test pre.error {
|
||||
color: #c00;
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#mocha .test pre {
|
||||
display: block;
|
||||
float: left;
|
||||
clear: left;
|
||||
font: 12px/1.5 monaco, monospace;
|
||||
margin: 5px;
|
||||
padding: 15px;
|
||||
border: 1px solid #eee;
|
||||
border-bottom-color: #ddd;
|
||||
-webkit-border-radius: 3px;
|
||||
-webkit-box-shadow: 0 1px 3px #eee;
|
||||
-moz-border-radius: 3px;
|
||||
-moz-box-shadow: 0 1px 3px #eee;
|
||||
}
|
||||
|
||||
#mocha .test h2 {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#mocha .test a.replay {
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
right: 0;
|
||||
text-decoration: none;
|
||||
vertical-align: middle;
|
||||
display: block;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
background: #eee;
|
||||
font-size: 15px;
|
||||
-moz-border-radius: 15px;
|
||||
border-radius: 15px;
|
||||
-webkit-transition: opacity 200ms;
|
||||
-moz-transition: opacity 200ms;
|
||||
transition: opacity 200ms;
|
||||
opacity: 0.3;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
#mocha .test:hover a.replay {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#mocha-report.pass .test.fail {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#mocha-report.fail .test.pass {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#mocha-error {
|
||||
color: #c00;
|
||||
font-size: 1.5 em;
|
||||
font-weight: 100;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
#mocha-stats {
|
||||
position: fixed;
|
||||
top: 15px;
|
||||
right: 10px;
|
||||
font-size: 12px;
|
||||
margin: 0;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
#mocha-stats .progress {
|
||||
float: right;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
#mocha-stats em {
|
||||
color: black;
|
||||
}
|
||||
|
||||
#mocha-stats a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#mocha-stats a:hover {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
#mocha-stats li {
|
||||
display: inline-block;
|
||||
margin: 0 5px;
|
||||
list-style: none;
|
||||
padding-top: 11px;
|
||||
}
|
||||
|
||||
code .comment { color: #ddd }
|
||||
code .init { color: #2F6FAD }
|
||||
code .string { color: #5890AD }
|
||||
code .keyword { color: #8A6343 }
|
||||
code .number { color: #2F6FAD }
|
||||
|
||||
@media screen and (max-device-width: 480px) {
|
||||
body {
|
||||
padding: 60px 0px;
|
||||
}
|
||||
|
||||
#stats {
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
5341
lib/client/console/test/vendor/mocha.js
vendored
Normal file
75
lib/client/contact.js
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
var CloudCmd, Util, DOM, olark;
|
||||
|
||||
(function(CloudCmd, Util, DOM) {
|
||||
'use strict';
|
||||
|
||||
CloudCmd.Contact = ContactProto;
|
||||
|
||||
function ContactProto(pCallBack) {
|
||||
var Contact = this,
|
||||
Images = DOM.Images,
|
||||
Key = CloudCmd.Key,
|
||||
Inited = false,
|
||||
DIR = CloudCmd.LIBDIRCLIENT;
|
||||
|
||||
function init(pCallBack) {
|
||||
if (!Inited) {
|
||||
Contact.show = show;
|
||||
Contact.hide = hide;
|
||||
|
||||
load(function() {
|
||||
Inited = true;
|
||||
|
||||
olark('api.box.onExpand', Contact.show);
|
||||
olark('api.box.onShow', Contact.show);
|
||||
olark('api.box.onHide', Contact.hide);
|
||||
olark('api.box.onShrink', Contact.hide);
|
||||
|
||||
DOM.Events.addKey(listener);
|
||||
Util.exec(pCallBack);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function load(callback) {
|
||||
var path = DIR + 'contact/olark.js';
|
||||
|
||||
Images.showLoad({top: true});
|
||||
|
||||
DOM.jsload(path, function() {
|
||||
Util.exec(callback);
|
||||
});
|
||||
}
|
||||
|
||||
function show() {
|
||||
Key.unsetBind();
|
||||
Images.hideLoad();
|
||||
|
||||
if (Inited)
|
||||
olark('api.box.expand');
|
||||
else
|
||||
init(Contact.show);
|
||||
}
|
||||
|
||||
function hide() {
|
||||
Key.setBind();
|
||||
|
||||
if (Inited)
|
||||
olark('api.box.hide');
|
||||
else
|
||||
init(Contact.hide);
|
||||
}
|
||||
|
||||
function listener(pEvent) {
|
||||
var ESC = Key.ESC,
|
||||
isBind = Key.isBind(),
|
||||
key = pEvent.keyCode;
|
||||
|
||||
if (!isBind && key === ESC)
|
||||
Contact.hide();
|
||||
}
|
||||
|
||||
init(pCallBack);
|
||||
}
|
||||
|
||||
})(CloudCmd, Util, DOM);
|
||||
87
lib/client/contact/olark.js
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
window.olark || (function (c) {
|
||||
var f = window,
|
||||
d = document,
|
||||
l = f.location.protocol == "https:" ? "https:" : "http:",
|
||||
z = c.name,
|
||||
r = "load";
|
||||
var nt = function () {
|
||||
f[z] = function () {
|
||||
(a.s = a.s || []).push(arguments);
|
||||
};
|
||||
var a = f[z]._ = {}, q = c.methods.length;
|
||||
while (q--) {
|
||||
(function (n) {
|
||||
f[z][n] = function () {
|
||||
f[z]("call", n, arguments);
|
||||
};
|
||||
})(c.methods[q]);
|
||||
}
|
||||
a.l = c.loader;
|
||||
a.i = nt;
|
||||
a.p = {
|
||||
0: +new Date()
|
||||
};
|
||||
a.P = function (u) {
|
||||
a.p[u] = new Date() - a.p[0];
|
||||
};
|
||||
|
||||
function s() {
|
||||
a.P(r);
|
||||
f[z](r);
|
||||
}
|
||||
f.addEventListener ? f.addEventListener(r, s, false) : f.attachEvent("on" + r, s);
|
||||
var ld = function () {
|
||||
function p(hd) {
|
||||
hd = "head";
|
||||
return ["<", hd, "></", hd, "><", i, ' onl' + 'oad="var d=', g, ";d.getElementsByTagName('head')[0].", j, "(d.", h, "('script')).", k, "='", l, "//", a.l, "'", '"', "></", i, ">"].join("");
|
||||
}
|
||||
var i = "body",
|
||||
m = d[i];
|
||||
if (!m) {
|
||||
return setTimeout(ld, 100);
|
||||
}
|
||||
a.P(1);
|
||||
var j = "appendChild",
|
||||
h = "createElement",
|
||||
k = "src",
|
||||
n = d[h]("div"),
|
||||
v = n[j](d[h](z)),
|
||||
b = d[h]("iframe"),
|
||||
g = "document",
|
||||
e = "domain",
|
||||
o;
|
||||
n.style.display = "none";
|
||||
m.insertBefore(n, m.firstChild).id = z;
|
||||
b.frameBorder = "0";
|
||||
b.id = z + "-loader";
|
||||
if (/MSIE[ ]+6/.test(navigator.userAgent)) {
|
||||
b.src = "javascript:false";
|
||||
}
|
||||
b.allowTransparency = "true";
|
||||
v[j](b);
|
||||
try {
|
||||
b.contentWindow[g].open();
|
||||
} catch (w) {
|
||||
c[e] = d[e];
|
||||
o = "javascript:var d=" + g + ".open();d.domain='" + d.domain + "';";
|
||||
b[k] = o + "void(0);";
|
||||
}
|
||||
try {
|
||||
var t = b.contentWindow[g];
|
||||
t.write(p());
|
||||
t.close();
|
||||
} catch (x) {
|
||||
b[k] = o + 'd.write("' + p().replace(/"/g, String.fromCharCode(92) + '"') + '");d.close();';
|
||||
}
|
||||
a.P(2);
|
||||
};
|
||||
ld();
|
||||
};
|
||||
nt();
|
||||
})({
|
||||
loader: "static.olark.com/jsclient/loader0.js",
|
||||
name: "olark",
|
||||
methods: ["configure", "extend", "declare", "identify"]
|
||||
});
|
||||
/* custom configuration goes here (www.olark.com/documentation) */
|
||||
olark.identify('6216-545-10-4223');
|
||||
1086
lib/client/dom.js
|
|
@ -9,6 +9,7 @@ var CloudCmd, Util, DOM, CloudFunc, ace, DiffProto, diff_match_patch;
|
|||
Loading = false,
|
||||
DIR = CloudCmd.LIBDIRCLIENT + 'edit/',
|
||||
LIBDIR = CloudCmd.LIBDIR,
|
||||
Info = DOM.CurrentInfo,
|
||||
Value,
|
||||
Edit = this,
|
||||
Diff,
|
||||
|
|
@ -22,29 +23,19 @@ var CloudCmd, Util, DOM, CloudFunc, ace, DiffProto, diff_match_patch;
|
|||
Element;
|
||||
|
||||
function init() {
|
||||
var lFunc, lIsFunc = Util.isFunction(CloudCmd.View);
|
||||
|
||||
Loading = true;
|
||||
if (lIsFunc)
|
||||
lFunc = CloudCmd.View;
|
||||
else
|
||||
lFunc = Util.exec;
|
||||
|
||||
Util.loadOnLoad([
|
||||
Edit.show,
|
||||
CloudCmd.View,
|
||||
load,
|
||||
lFunc
|
||||
Edit.show
|
||||
]);
|
||||
|
||||
DOM.Events.addKey(listener);
|
||||
DOM.setButtonKey('f4', Edit.show);
|
||||
}
|
||||
|
||||
this.show = function(pValue) {
|
||||
var lMode,
|
||||
lName = DOM.getCurrentName(),
|
||||
isDir = DOM.isCurrentIsDir(),
|
||||
lExt = Util.getExtension(lName);
|
||||
var lMode, htmlMode, cssMode,
|
||||
lName = Info.name,
|
||||
isDir = Info.isDir,
|
||||
lExt = Info.ext;
|
||||
|
||||
if (!Loading) {
|
||||
Images.showLoad();
|
||||
|
|
@ -63,33 +54,34 @@ var CloudCmd, Util, DOM, CloudFunc, ace, DiffProto, diff_match_patch;
|
|||
initAce();
|
||||
}
|
||||
|
||||
if (!Modelist)
|
||||
Modelist = ace.require('ace/ext/modelist');
|
||||
|
||||
if (isDir)
|
||||
lMode = Modelist.modesByName.json.mode;
|
||||
else
|
||||
lMode = Modelist.getModeForPath(lName).mode;
|
||||
|
||||
htmlMode = Modelist.modesByName.html.mode;
|
||||
cssMode = Modelist.modesByName.css.mode;
|
||||
|
||||
Session.setMode(lMode);
|
||||
|
||||
if (lMode === htmlMode || lMode === cssMode)
|
||||
DOM.jsload(DIR + 'emmet.js', function() {
|
||||
var Emmet = ace.require("ace/ext/emmet");
|
||||
|
||||
Emmet.setCore(window.emmet);
|
||||
Ace.setOption("enableEmmet", true);
|
||||
});
|
||||
|
||||
if (Util.isString(pValue)) {
|
||||
Ace.setValue(pValue);
|
||||
CloudCmd.View.show(Element, focus);
|
||||
Key.unsetBind();
|
||||
} else {
|
||||
DOM.getCurrentData({
|
||||
success : function(pData) {
|
||||
var lValue = '';
|
||||
|
||||
if (pData)
|
||||
lValue = pData.data;
|
||||
|
||||
Value = lValue;
|
||||
Ace.setValue(lValue);
|
||||
CloudCmd.View.show(Element, focus);
|
||||
}
|
||||
} else
|
||||
Info.getData(function(data) {
|
||||
Value = data;
|
||||
Ace.setValue(data);
|
||||
CloudCmd.View.show(Element, focus);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -146,10 +138,18 @@ var CloudCmd, Util, DOM, CloudFunc, ace, DiffProto, diff_match_patch;
|
|||
bindKey : { win: 'Ctrl-S', mac: 'Command-S' },
|
||||
exec : save
|
||||
});
|
||||
|
||||
ace.require('ace/ext/language_tools');
|
||||
Modelist = ace.require('ace/ext/modelist');
|
||||
|
||||
Ace.setOptions({
|
||||
enableBasicAutocompletion : true,
|
||||
enableSnippets : true
|
||||
});
|
||||
}
|
||||
|
||||
function save () {
|
||||
var lPath = DOM.getCurrentPath(),
|
||||
var lPath = Info.path,
|
||||
lValue = Ace.getValue();
|
||||
|
||||
CloudCmd.getConfig(function(config) {
|
||||
|
|
@ -187,10 +187,10 @@ var CloudCmd, Util, DOM, CloudFunc, ace, DiffProto, diff_match_patch;
|
|||
|
||||
function diff(pNewValue, pCallBack) {
|
||||
var libs = [
|
||||
LIBDIR + 'diff.js',
|
||||
LIBDIR + 'diff/diff-match-patch.js'
|
||||
LIBDIR + 'diff/diff-match-patch.js',
|
||||
LIBDIR + 'diff.js'
|
||||
],
|
||||
url = CloudFunc.getCombineURL(libs);
|
||||
url = CloudFunc.getJoinURL(libs);
|
||||
|
||||
DOM.jsload(url, function() {
|
||||
var patch,
|
||||
|
|
@ -203,7 +203,7 @@ var CloudCmd, Util, DOM, CloudFunc, ace, DiffProto, diff_match_patch;
|
|||
patch = Diff.createPatch(Value, pNewValue);
|
||||
Util.exec(pCallBack, patch);
|
||||
}, function(callback) {
|
||||
var path = DOM.getCurrentPath();
|
||||
var path = Info.path;
|
||||
|
||||
DOM.getDataFromStorage(path, function(data) {
|
||||
if (data)
|
||||
|
|
@ -215,42 +215,31 @@ var CloudCmd, Util, DOM, CloudFunc, ace, DiffProto, diff_match_patch;
|
|||
});
|
||||
}
|
||||
|
||||
function load(pCallBack) {
|
||||
function load(callback) {
|
||||
var files = [
|
||||
DIR + 'theme-tomorrow_night_blue.js',
|
||||
DIR + 'ext-language_tools.js',
|
||||
DIR + 'ext-searchbox.js',
|
||||
DIR + 'ext-modelist.js',
|
||||
DIR + 'ext-emmet.js'
|
||||
],
|
||||
|
||||
ace = DIR + 'ace.js',
|
||||
url = CloudFunc.getJoinURL(files);
|
||||
|
||||
Util.time(Name + ' load');
|
||||
|
||||
var lFiles = [
|
||||
DIR + 'theme-tomorrow_night_blue.js',
|
||||
DIR + 'ext-language_tools.js',
|
||||
DIR + 'ext-searchbox.js',
|
||||
DIR + 'ext-modelist.js',
|
||||
],
|
||||
lAce = DIR + 'ace.js',
|
||||
lURL = CloudFunc.getCombineURL(lFiles);
|
||||
|
||||
|
||||
DOM.anyLoadOnLoad([lURL, lAce], function() {
|
||||
DOM.anyLoadOnLoad([ace, url], function() {
|
||||
Util.timeEnd(Name + ' load');
|
||||
Loading = false;
|
||||
Util.exec(pCallBack);
|
||||
Util.exec(callback);
|
||||
});
|
||||
}
|
||||
|
||||
function listener(pEvent) {
|
||||
var lF4, lKey, lIsBind = Key.isBind();
|
||||
|
||||
if (lIsBind) {
|
||||
lF4 = Key.F4,
|
||||
lKey = pEvent.keyCode;
|
||||
|
||||
if(lKey === lF4)
|
||||
Edit.show();
|
||||
}
|
||||
}
|
||||
|
||||
function onSave(text) {
|
||||
var ret,
|
||||
isError = Util.isContainStrAtBegin(text, 'error'),
|
||||
path = DOM.getCurrentPath(),
|
||||
path = Info.path,
|
||||
msg = '\nShould I save file anyway?';
|
||||
|
||||
if (!isError) {
|
||||
|
|
|
|||
13790
lib/client/edit/emmet.js
Normal file
1096
lib/client/edit/ext-emmet.js
Normal file
|
|
@ -11,12 +11,9 @@ var CloudCmd, Util, DOM;
|
|||
|
||||
function init() {
|
||||
Util.loadOnLoad([
|
||||
Help.show,
|
||||
CloudCmd.View,
|
||||
Help.show,
|
||||
]);
|
||||
|
||||
DOM.Events.addKey(listener);
|
||||
DOM.setButtonKey('f1', Help.show);
|
||||
}
|
||||
|
||||
this.show = function() {
|
||||
|
|
@ -35,30 +32,30 @@ var CloudCmd, Util, DOM;
|
|||
});
|
||||
|
||||
DOM.ajax({
|
||||
url: '/HELP.md',
|
||||
success: function (pData) {
|
||||
url : '/HELP.md',
|
||||
success : function (pData) {
|
||||
var lData = {text: pData};
|
||||
|
||||
DOM.ajax({
|
||||
method : 'post',
|
||||
url : 'https://api.github.com/markdown',
|
||||
data : Util.stringifyJSON(lData),
|
||||
success:function(pResult){
|
||||
var lDiv = DOM.anyload({
|
||||
name : 'div',
|
||||
id : 'help',
|
||||
inner : pResult.toString()
|
||||
});
|
||||
success : function(pResult){
|
||||
var div = DOM.anyload({
|
||||
name : 'div',
|
||||
id : 'help',
|
||||
inner : pResult.toString()
|
||||
});
|
||||
|
||||
Images.hideLoad();
|
||||
CloudCmd.View.show(lDiv);
|
||||
CloudCmd.View.show(div);
|
||||
},
|
||||
|
||||
error: Images.showError
|
||||
});
|
||||
},
|
||||
|
||||
error:Images.showError
|
||||
error : Images.showError
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -66,16 +63,6 @@ var CloudCmd, Util, DOM;
|
|||
CloudCmd.View.hide();
|
||||
};
|
||||
|
||||
function listener(pEvent) {
|
||||
var lF1 = Key.F1,
|
||||
lIsBind = Key.isBind(),
|
||||
lKey = pEvent.keyCode;
|
||||
|
||||
/* если клавиши можно обрабатывать */
|
||||
if (lIsBind && lKey === lF1)
|
||||
Help.show();
|
||||
}
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,50 +2,58 @@ var CloudCmd, Util, DOM;
|
|||
(function(CloudCmd, Util, DOM) {
|
||||
'use strict';
|
||||
|
||||
var Chars = [],
|
||||
KEY = {
|
||||
TAB : 9,
|
||||
ENTER : 13,
|
||||
ESC : 27,
|
||||
|
||||
SPACE : 32,
|
||||
PAGE_UP : 33,
|
||||
PAGE_DOWN : 34,
|
||||
END : 35,
|
||||
HOME : 36,
|
||||
UP : 38,
|
||||
DOWN : 40,
|
||||
|
||||
INSERT : 45,
|
||||
DELETE : 46,
|
||||
|
||||
A : 65,
|
||||
|
||||
D : 68,
|
||||
|
||||
G : 71,
|
||||
|
||||
O : 79,
|
||||
Q : 81,
|
||||
R : 82,
|
||||
S : 83,
|
||||
T : 84,
|
||||
|
||||
Z : 90,
|
||||
|
||||
F1 : 112,
|
||||
F2 : 113,
|
||||
F3 : 114,
|
||||
F4 : 115,
|
||||
F5 : 116,
|
||||
F6 : 117,
|
||||
F7 : 118,
|
||||
F8 : 119,
|
||||
F9 : 120,
|
||||
F10 : 121,
|
||||
|
||||
TRA : 192 /* Typewritten Reverse Apostrophe (`) */
|
||||
};
|
||||
var Info = DOM.CurrentInfo,
|
||||
Chars = [],
|
||||
KEY = {
|
||||
BACKSPACE : 8,
|
||||
TAB : 9,
|
||||
ENTER : 13,
|
||||
ESC : 27,
|
||||
|
||||
SPACE : 32,
|
||||
PAGE_UP : 33,
|
||||
PAGE_DOWN : 34,
|
||||
END : 35,
|
||||
HOME : 36,
|
||||
UP : 38,
|
||||
DOWN : 40,
|
||||
|
||||
INSERT : 45,
|
||||
DELETE : 46,
|
||||
|
||||
A : 65,
|
||||
|
||||
D : 68,
|
||||
|
||||
G : 71,
|
||||
|
||||
O : 79,
|
||||
Q : 81,
|
||||
R : 82,
|
||||
S : 83,
|
||||
T : 84,
|
||||
|
||||
Z : 90,
|
||||
|
||||
ASTERISK : 106,
|
||||
PLUS : 107,
|
||||
MINUS : 109,
|
||||
|
||||
F1 : 112,
|
||||
F2 : 113,
|
||||
F3 : 114,
|
||||
F4 : 115,
|
||||
F5 : 116,
|
||||
F6 : 117,
|
||||
F7 : 118,
|
||||
F8 : 119,
|
||||
F9 : 120,
|
||||
F10 : 121,
|
||||
|
||||
SLASH : 191,
|
||||
TRA : 192, /* Typewritten Reverse Apostrophe (`) */
|
||||
BACKSLASH : 220
|
||||
};
|
||||
|
||||
KeyProto.prototype = KEY;
|
||||
CloudCmd.Key = KeyProto;
|
||||
|
|
@ -53,9 +61,9 @@ var CloudCmd, Util, DOM;
|
|||
function KeyProto() {
|
||||
var Key = this,
|
||||
Binded,
|
||||
lTabPanel = {
|
||||
left : 0,
|
||||
right : 0
|
||||
TabPanel = {
|
||||
'js-left' : null,
|
||||
'js-right' : null
|
||||
};
|
||||
|
||||
this.isBind = function() {return Binded;};
|
||||
|
|
@ -74,13 +82,12 @@ var CloudCmd, Util, DOM;
|
|||
function listener(pEvent) {
|
||||
/* получаем выдленный файл*/
|
||||
var i, n,
|
||||
lCurrent = DOM.getCurrentFile(),
|
||||
lKeyCode = pEvent.keyCode,
|
||||
lAlt = pEvent.altKey,
|
||||
lCtrl = pEvent.ctrlKey;
|
||||
ctrl = pEvent.ctrlKey;
|
||||
/* если клавиши можно обрабатывать*/
|
||||
if (Binded) {
|
||||
if (!lAlt && !lCtrl && lKeyCode >= KEY.A && lKeyCode <= KEY.Z)
|
||||
if (!lAlt && !ctrl && lKeyCode >= KEY.A && lKeyCode <= KEY.Z)
|
||||
setCurrentByLetter(lKeyCode);
|
||||
else {
|
||||
Chars = [];
|
||||
|
|
@ -103,15 +110,15 @@ var CloudCmd, Util, DOM;
|
|||
}
|
||||
|
||||
function setCurrentByLetter(pKeyCode) {
|
||||
var i, n, name, isCurrent, isContain, byName, firstByName,
|
||||
var i, n, name, isCurrent, isMatch, byName, firstByName,
|
||||
skipCount = 0,
|
||||
skipN = 0,
|
||||
setted = false,
|
||||
lCurrent = DOM.getCurrentFile(),
|
||||
files = DOM.getFiles(),
|
||||
SMALL = 32,
|
||||
current = Info.element,
|
||||
panel = Info.panel,
|
||||
files = Info.files,
|
||||
char = String.fromCharCode(pKeyCode),
|
||||
charSmall = String.fromCharCode(pKeyCode + SMALL);
|
||||
regExp = new RegExp('^' + char + '.*$', 'i');
|
||||
|
||||
n = Chars.length;
|
||||
for (i = 0; i < n; i++)
|
||||
|
|
@ -126,11 +133,11 @@ var CloudCmd, Util, DOM;
|
|||
|
||||
n = files.length;
|
||||
for (i = 0; i < n; i++) {
|
||||
lCurrent = files[i];
|
||||
name = DOM.getCurrentName(lCurrent);
|
||||
isContain = Util.isContainStrAtBegin(name, [char, charSmall]);
|
||||
current = files[i];
|
||||
name = DOM.getCurrentName(current);
|
||||
isMatch = name.match(regExp);
|
||||
|
||||
if (isContain) {
|
||||
if (isMatch) {
|
||||
byName = DOM.getCurrentFileByName(name);
|
||||
|
||||
if (!skipCount) {
|
||||
|
|
@ -153,91 +160,114 @@ var CloudCmd, Util, DOM;
|
|||
}
|
||||
|
||||
function switchKey(pEvent) {
|
||||
var i, n, lCurrent = DOM.getCurrentFile(),
|
||||
var i, n, id, obj,
|
||||
current = Info.element,
|
||||
panel = Info.panel,
|
||||
path = Info.path,
|
||||
dirPath = Info.dirPath,
|
||||
filesPassive = Info.filesPassive,
|
||||
prev = current.previousSibling,
|
||||
next = current.nextSibling,
|
||||
lKeyCode = pEvent.keyCode,
|
||||
lShift = pEvent.shiftKey,
|
||||
shift = pEvent.shiftKey,
|
||||
lAlt = pEvent.altKey,
|
||||
lCtrl = pEvent.ctrlKey;
|
||||
ctrl = pEvent.ctrlKey;
|
||||
|
||||
switch (lKeyCode) {
|
||||
case Key.TAB:
|
||||
/* changing parent panel of curent-file */
|
||||
var lFirstFileOnList,
|
||||
lPanel = DOM.getPanel(),
|
||||
lId = lPanel.id;
|
||||
id = panel.id;
|
||||
TabPanel[id] = current;
|
||||
|
||||
lTabPanel[lId] = lCurrent;
|
||||
panel = Info.panelPassive;
|
||||
id = panel.id;
|
||||
|
||||
lPanel = DOM.getPanel({active:false});
|
||||
lId = lPanel.id;
|
||||
current = TabPanel[id];
|
||||
|
||||
lCurrent = lTabPanel[lId];
|
||||
if (lCurrent && lCurrent.parentElement)
|
||||
DOM.setCurrentFile(lCurrent);
|
||||
if (current && current.parentElement)
|
||||
DOM.setCurrentFile(current);
|
||||
else {
|
||||
lFirstFileOnList = DOM.getByTag('li', lPanel)[2];
|
||||
|
||||
DOM.setCurrentFile(lFirstFileOnList);
|
||||
current = filesPassive[0];
|
||||
DOM.setCurrentFile(current);
|
||||
}
|
||||
|
||||
DOM.preventDefault(pEvent);//запрет на дальнейшее действие
|
||||
break;
|
||||
|
||||
case Key.INSERT:
|
||||
DOM .toggleSelectedFile(lCurrent)
|
||||
.setCurrentFile(lCurrent.nextSibling);
|
||||
DOM .toggleSelectedFile(current)
|
||||
.setCurrentFile(next);
|
||||
break;
|
||||
|
||||
case Key.DELETE:
|
||||
if (lShift) {
|
||||
var lUrl = DOM.getCurrentPath(lCurrent);
|
||||
if (shift) {
|
||||
if (Info.isDir)
|
||||
path += '?dir';
|
||||
|
||||
if ( DOM.isCurrentIsDir(lCurrent) )
|
||||
lUrl += '?dir';
|
||||
|
||||
DOM.RESTful.delete(lUrl, function() {
|
||||
DOM.deleteCurrent(lCurrent);
|
||||
DOM.RESTful.delete(path, function() {
|
||||
DOM.deleteCurrent(current);
|
||||
});
|
||||
}
|
||||
else
|
||||
DOM.promptDeleteSelected(lCurrent);
|
||||
DOM.promptDeleteSelected(current);
|
||||
break;
|
||||
|
||||
case Key.ASTERISK:
|
||||
DOM.toggleAllSelectedFiles(current);
|
||||
break;
|
||||
|
||||
case Key.PLUS:
|
||||
DOM.expandSelection();
|
||||
break;
|
||||
|
||||
case Key.MINUS:
|
||||
DOM.shrinkSelection();
|
||||
break;
|
||||
|
||||
case Key.SLASH:
|
||||
if (shift) {
|
||||
Util.exec(CloudCmd.Help.show);
|
||||
DOM.preventDefault(pEvent);
|
||||
}
|
||||
break;
|
||||
|
||||
case Key.F1:
|
||||
Util.exec(CloudCmd.Help);
|
||||
Util.exec(CloudCmd.Help.show);
|
||||
DOM.preventDefault(pEvent);
|
||||
break;
|
||||
|
||||
case Key.F2:
|
||||
DOM.renameCurrent(lCurrent);
|
||||
DOM.renameCurrent(current);
|
||||
break;
|
||||
|
||||
case Key.F3:
|
||||
Util.exec(CloudCmd.View);
|
||||
Util.exec(CloudCmd.View.show);
|
||||
DOM.preventDefault(pEvent);
|
||||
break;
|
||||
|
||||
case Key.F4:
|
||||
Util.exec(CloudCmd.Edit);
|
||||
Util.exec(CloudCmd.Edit.show);
|
||||
DOM.preventDefault(pEvent);
|
||||
break;
|
||||
|
||||
case Key.F5:
|
||||
DOM.copyCurrent(lCurrent);
|
||||
DOM.copyCurrent(current);
|
||||
DOM.preventDefault(pEvent);
|
||||
break;
|
||||
|
||||
case Key.F6:
|
||||
DOM.moveCurrent(lCurrent);
|
||||
DOM.moveCurrent(current);
|
||||
DOM.preventDefault(pEvent);
|
||||
break;
|
||||
|
||||
case Key.F7:
|
||||
DOM.promptNewDir();
|
||||
if (shift)
|
||||
DOM.promptNewFile();
|
||||
else
|
||||
DOM.promptNewDir();
|
||||
break;
|
||||
|
||||
case Key.F8:
|
||||
DOM.promptDeleteSelected(lCurrent);
|
||||
DOM.promptDeleteSelected(current);
|
||||
break;
|
||||
|
||||
case Key.F9:
|
||||
|
|
@ -247,29 +277,38 @@ var CloudCmd, Util, DOM;
|
|||
break;
|
||||
|
||||
case Key.F10:
|
||||
Util.exec(CloudCmd.Config);
|
||||
Util.exec(CloudCmd.Config.show);
|
||||
DOM.preventDefault(pEvent);
|
||||
|
||||
break;
|
||||
|
||||
case Key.TRA:
|
||||
DOM.Images.showLoad({top: true});
|
||||
Util.exec(CloudCmd.Console);
|
||||
|
||||
if (shift)
|
||||
obj = CloudCmd.Terminal;
|
||||
else
|
||||
obj = CloudCmd.Console;
|
||||
|
||||
Util.exec(obj.show);
|
||||
DOM.preventDefault(pEvent);
|
||||
|
||||
break;
|
||||
|
||||
case Key.SPACE:
|
||||
var lSelected = DOM.isSelected(lCurrent),
|
||||
lDir = DOM.isCurrentIsDir(lCurrent),
|
||||
lName = DOM.getCurrentName(lCurrent);
|
||||
var lSelected,
|
||||
isDir = Info.isDir,
|
||||
lName = Info.name;
|
||||
|
||||
if (!lDir || lName === '..')
|
||||
lSelected = true;
|
||||
if (!isDir || lName === '..')
|
||||
lSelected = true;
|
||||
else
|
||||
lSelected = Info.isSelected;
|
||||
|
||||
Util.ifExec(lSelected, function() {
|
||||
DOM.toggleSelectedFile(lCurrent);
|
||||
DOM.toggleSelectedFile(current);
|
||||
}, function(pCallBack) {
|
||||
DOM.loadCurrentSize(pCallBack, lCurrent);
|
||||
DOM.loadCurrentSize(pCallBack, current);
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -280,57 +319,56 @@ var CloudCmd, Util, DOM;
|
|||
* если нажали клавишу вверх *
|
||||
* выделяем предыдущую строку */
|
||||
case Key.UP:
|
||||
if (lShift)
|
||||
DOM.toggleSelectedFile(lCurrent);
|
||||
if (shift)
|
||||
DOM.toggleSelectedFile(current);
|
||||
|
||||
DOM.setCurrentFile( lCurrent.previousSibling );
|
||||
DOM.preventDefault( pEvent );
|
||||
DOM.setCurrentFile(prev);
|
||||
DOM.preventDefault(pEvent);
|
||||
break;
|
||||
|
||||
/* если нажали клавишу в низ - выделяем следующую строку */
|
||||
case Key.DOWN:
|
||||
if (lShift)
|
||||
DOM.toggleSelectedFile(lCurrent);
|
||||
if (shift)
|
||||
DOM.toggleSelectedFile(current);
|
||||
|
||||
DOM.setCurrentFile( lCurrent.nextSibling );
|
||||
DOM.preventDefault( pEvent );
|
||||
DOM.setCurrentFile(next);
|
||||
DOM.preventDefault(pEvent);
|
||||
break;
|
||||
|
||||
/* если нажали клавишу Home *
|
||||
* переходим к самому верхнему *
|
||||
* элементу */
|
||||
case Key.HOME:
|
||||
DOM.setCurrentFile( lCurrent.parentElement.firstChild );
|
||||
DOM.setCurrentFile(Info.first);
|
||||
DOM.preventDefault(pEvent);
|
||||
break;
|
||||
|
||||
/* если нажали клавишу End выделяем последний элемент */
|
||||
case Key.END:
|
||||
DOM.setCurrentFile( lCurrent.parentElement.lastElementChild );
|
||||
DOM.preventDefault( pEvent );
|
||||
DOM.setCurrentFile(Info.last);
|
||||
DOM.preventDefault(pEvent);
|
||||
break;
|
||||
|
||||
/* если нажали клавишу page down проматываем экран */
|
||||
case Key.PAGE_DOWN:
|
||||
DOM.scrollByPages( DOM.getPanel(), 1 );
|
||||
DOM.scrollByPages(panel, 1);
|
||||
|
||||
for (i = 0; i < 30; i++) {
|
||||
|
||||
if (!lCurrent.nextSibling)
|
||||
if (!current.nextSibling)
|
||||
break;
|
||||
|
||||
lCurrent = lCurrent.nextSibling;
|
||||
current = current.nextSibling;
|
||||
}
|
||||
DOM.setCurrentFile(lCurrent);
|
||||
DOM.setCurrentFile(current);
|
||||
DOM.preventDefault(pEvent);
|
||||
break;
|
||||
|
||||
/* если нажали клавишу page up проматываем экран */
|
||||
case Key.PAGE_UP:
|
||||
DOM.scrollByPages(DOM.getPanel(), -1);
|
||||
DOM.scrollByPages(panel, -1);
|
||||
|
||||
var lC = lCurrent,
|
||||
tryCatch = function(pCurrentFile) {
|
||||
var tryCatch = function(pCurrentFile) {
|
||||
Util.tryCatch(function() {
|
||||
return pCurrentFile
|
||||
.previousSibling
|
||||
|
|
@ -341,26 +379,39 @@ var CloudCmd, Util, DOM;
|
|||
};
|
||||
|
||||
for (i = 0; i < 30; i++) {
|
||||
if (!lC.previousSibling || tryCatch(lC) ) break;
|
||||
if (!current.previousSibling || tryCatch(current) )
|
||||
break;
|
||||
|
||||
lC = lC.previousSibling;
|
||||
current = current.previousSibling;
|
||||
}
|
||||
|
||||
DOM.setCurrentFile(lC);
|
||||
DOM.setCurrentFile(current);
|
||||
DOM.preventDefault(pEvent);
|
||||
break;
|
||||
|
||||
/* открываем папку*/
|
||||
case Key.ENTER:
|
||||
if (DOM.isCurrentIsDir())
|
||||
Util.exec( CloudCmd.loadDir() );
|
||||
if (Info.isDir)
|
||||
Util.exec(CloudCmd.loadDir());
|
||||
break;
|
||||
|
||||
case Key.BACKSPACE:
|
||||
CloudCmd.goToParentDir();
|
||||
DOM.preventDefault(pEvent);
|
||||
break;
|
||||
|
||||
case Key.BACKSLASH:
|
||||
if (ctrl) {
|
||||
path = '/';
|
||||
Util.exec(CloudCmd.loadDir(path));
|
||||
}
|
||||
break;
|
||||
|
||||
case Key.A:
|
||||
if (pEvent.ctrlKey) {
|
||||
DOM .toggleAllSelectedFiles(lCurrent)
|
||||
if (ctrl)
|
||||
DOM .toggleAllSelectedFiles()
|
||||
.preventDefault(pEvent);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
/*
|
||||
|
|
@ -371,7 +422,7 @@ var CloudCmd, Util, DOM;
|
|||
* (обновляем кэш)
|
||||
*/
|
||||
case Key.R:
|
||||
if (lCtrl) {
|
||||
if (ctrl) {
|
||||
Util.log('<ctrl>+r pressed\n' +
|
||||
'reloading page...\n' +
|
||||
'press <alt>+q to remove all key-handlers');
|
||||
|
|
@ -383,7 +434,7 @@ var CloudCmd, Util, DOM;
|
|||
|
||||
/* чистим кэш */
|
||||
case Key.D:
|
||||
if (lCtrl) {
|
||||
if (ctrl) {
|
||||
Util.log('<ctrl>+d pressed\n' +
|
||||
'clearing Storage...\n' +
|
||||
'press <alt>+q to remove all key-handlers');
|
||||
|
|
|
|||
|
|
@ -5,8 +5,9 @@ var Util, DOM, CloudCmd;
|
|||
|
||||
CloudCmd.Listeners = new ListenersProto(CloudCmd, Util, DOM);
|
||||
|
||||
function ListenersProto(CloudCmd, Util, DOM){
|
||||
var Storage = DOM.Storage,
|
||||
function ListenersProto(CloudCmd, Util, DOM) {
|
||||
var Info = DOM.CurrentInfo,
|
||||
Storage = DOM.Storage,
|
||||
Events = DOM.Events,
|
||||
getConfig = CloudCmd.getConfig;
|
||||
|
||||
|
|
@ -16,12 +17,12 @@ var Util, DOM, CloudCmd;
|
|||
online = config.online;
|
||||
|
||||
if (analytics && online) {
|
||||
Events.addOnce('mousemove', function(){
|
||||
Events.addOnce('mousemove', function() {
|
||||
var FIVE_SECONDS = 5000,
|
||||
lUrl = CloudCmd.LIBDIRCLIENT + 'analytics.js';
|
||||
url = CloudCmd.LIBDIRCLIENT + 'analytics.js';
|
||||
|
||||
setTimeout(function(){
|
||||
DOM.jsload(lUrl);
|
||||
setTimeout(function() {
|
||||
DOM.jsload(url);
|
||||
}, FIVE_SECONDS);
|
||||
});
|
||||
}
|
||||
|
|
@ -38,105 +39,113 @@ var Util, DOM, CloudCmd;
|
|||
};
|
||||
|
||||
this.initKeysPanel = function() {
|
||||
var i, lButton, lEl,
|
||||
lKeysPanel = {},
|
||||
var button, id, func,
|
||||
keysElement = DOM.getByClass('keyspanel');
|
||||
|
||||
lFuncs =[
|
||||
null,
|
||||
CloudCmd.Help, /* f1 */
|
||||
DOM.renameCurrent, /* f2 */
|
||||
CloudCmd.View, /* f3 */
|
||||
CloudCmd.Edit, /* f4 */
|
||||
DOM.copyCurrent, /* f5 */
|
||||
DOM.moveCurrent, /* f6 */
|
||||
DOM.promptNewDir, /* f7 */
|
||||
DOM.promptDeleteSelected, /* f8 */
|
||||
CloudCmd.Menu, /* f9 */
|
||||
CloudCmd.Config, /* f10 */
|
||||
];
|
||||
|
||||
for (i = 1; i <= 10; i++) {
|
||||
lButton = 'f' + i,
|
||||
lEl = DOM.getById('f' + i);
|
||||
lKeysPanel[lButton] = lEl;
|
||||
|
||||
if (i === 1 || i === 3 || i === 4 || i === 9 || i === 10)
|
||||
Events.addOnce('click', lFuncs[i], lEl);
|
||||
else
|
||||
Events.addClick(lFuncs[i], lEl);
|
||||
}
|
||||
|
||||
lButton = '~',
|
||||
lEl = DOM.getById('~');
|
||||
lKeysPanel[lButton] = lEl;
|
||||
Events.addOnce('click', CloudCmd.Console, lEl);
|
||||
|
||||
return lKeysPanel;
|
||||
if (keysElement)
|
||||
Events.addClick(function(event) {
|
||||
var element = event.target,
|
||||
id = element.id,
|
||||
|
||||
clickFuncs = {
|
||||
'f1' : CloudCmd.Help.show,
|
||||
'f3' : CloudCmd.View.show,
|
||||
'f4' : CloudCmd.Edit.show,
|
||||
'f9' : CloudCmd.Menu.show,
|
||||
'f10' : CloudCmd.Config.show,
|
||||
'~' : CloudCmd.Console.show,
|
||||
'contact' : CloudCmd.Contact.show,
|
||||
|
||||
'f2' : DOM.renameCurrent,
|
||||
'f5' : DOM.copyCurrent,
|
||||
'f6' : DOM.moveCurrent,
|
||||
'f7' : DOM.promptNewDir,
|
||||
'f8' : DOM.promptDeleteSelected
|
||||
},
|
||||
|
||||
func = clickFuncs[id];
|
||||
|
||||
Util.exec(func);
|
||||
|
||||
}, keysElement);
|
||||
};
|
||||
|
||||
/**
|
||||
* функция меняет ссыки на ajax-овые
|
||||
* @param pPanelID
|
||||
* @param panelId
|
||||
*/
|
||||
this.changeLinks = function(pPanelID) {
|
||||
/* назначаем кнопку очистить кэш и показываем её */
|
||||
var lClearStorage = DOM.getById('clear-storage');
|
||||
Events.addClick(Storage.clear, lClearStorage);
|
||||
this.changeLinks = function(panelId) {
|
||||
var i, n, a, ai, current, link, loadDir, events, id, isDir,
|
||||
|
||||
/* меняем ссылки на ajax-запросы */
|
||||
var lPanel = DOM.getById(pPanelID),
|
||||
a = DOM.getByTag('a', lPanel),
|
||||
url = CloudCmd.HOST,
|
||||
loadDirOnce = CloudCmd.loadDir(),
|
||||
panel = DOM.getById(panelId),
|
||||
pathElement = DOM.getByClass('js-path', panel),
|
||||
filesElement = DOM.getByClass('files', panel),
|
||||
|
||||
files = filesElement.children,
|
||||
pathLinks = DOM.getByClass('links', pathElement).children,
|
||||
clearStorage = DOM.getByClass('clear-storage', pathElement),
|
||||
refresh = DOM.getByClass('refresh-icon', pathElement),
|
||||
|
||||
fileClick = function (event) {
|
||||
var ctrl = event.ctrlKey;
|
||||
|
||||
if (!ctrl)
|
||||
DOM.preventDefault(event);
|
||||
},
|
||||
|
||||
/* right mouse click function varible */
|
||||
lOnContextMenu_f = function(pEvent){
|
||||
var lReturn_b = true,
|
||||
onContextMenu = function(pEvent) {
|
||||
var target,
|
||||
isFunc = Util.isFunction(CloudCmd.Menu),
|
||||
ret = true,
|
||||
Key = CloudCmd.Key;
|
||||
|
||||
Key && Key.unsetBind();
|
||||
|
||||
/* getting html element
|
||||
* currentTarget - DOM event
|
||||
* target - jquery event
|
||||
*/
|
||||
var lTarget = pEvent.currentTarget || pEvent.target;
|
||||
DOM.setCurrentFile(lTarget);
|
||||
target = pEvent.currentTarget || pEvent.target;
|
||||
DOM.setCurrentFile(target);
|
||||
|
||||
if(Util.isFunction(CloudCmd.Menu)) {
|
||||
if (isFunc) {
|
||||
CloudCmd.Menu({
|
||||
x: pEvent.clientX,
|
||||
y: pEvent.clientY
|
||||
});
|
||||
|
||||
/* disabling browsers menu*/
|
||||
lReturn_b = false;
|
||||
DOM.Images.showLoad();
|
||||
}
|
||||
ret = false;
|
||||
}
|
||||
|
||||
return lReturn_b;
|
||||
return ret;
|
||||
},
|
||||
|
||||
/* drag and drop function varible
|
||||
* download file from browser to descktop
|
||||
* in Chrome (HTML5)
|
||||
*/
|
||||
lOnDragStart_f = function(pEvent) {
|
||||
/* drag and drop function varible
|
||||
* download file from browser to descktop
|
||||
* in Chrome (HTML5)
|
||||
*/
|
||||
onDragStart = function(pEvent) {
|
||||
var lElement = pEvent.target,
|
||||
EXT = 'json',
|
||||
isDir = Info.isDir,
|
||||
lLink = lElement.href,
|
||||
lName = lElement.textContent;
|
||||
|
||||
/* if it's directory - adding json extension */
|
||||
if( DOM.isCurrentIsDir() ){
|
||||
lName += '.json';
|
||||
lLink += '?json';
|
||||
if (isDir) {
|
||||
lName += '.' + EXT;
|
||||
lLink += '?' + EXT;
|
||||
}
|
||||
|
||||
pEvent.dataTransfer.setData("DownloadURL",
|
||||
pEvent.dataTransfer.setData('DownloadURL',
|
||||
'application/octet-stream' + ':' +
|
||||
lName + ':' +
|
||||
lLink);
|
||||
},
|
||||
|
||||
lSetCurrentFile_f = function(pEvent){
|
||||
setCurrentFile = function(pEvent) {
|
||||
var pElement = pEvent.target,
|
||||
lTag = pElement.tagName;
|
||||
|
||||
|
|
@ -147,47 +156,56 @@ var Util, DOM, CloudCmd;
|
|||
} while(lTag !== 'LI');
|
||||
|
||||
DOM.setCurrentFile(pElement);
|
||||
},
|
||||
|
||||
lUrl = CloudCmd.HOST,
|
||||
lLoadDirOnce = CloudCmd.loadDir();
|
||||
};
|
||||
|
||||
/* ставим загрузку гифа на клик*/
|
||||
Events.addClick( CloudCmd.refresh, a[0].parentElement );
|
||||
Events.addClick(CloudCmd.refresh, refresh);
|
||||
Events.addClick(Storage.clear, clearStorage);
|
||||
|
||||
/* start from 1 cous 0 is a refresh and it's setted up */
|
||||
for(var i = 1, n = a.length; i < n ; i++){
|
||||
/* убираем адрес хоста*/
|
||||
var ai = a[i],
|
||||
lLink = Util.removeStr(ai.href, lUrl),
|
||||
lLoadDir = CloudCmd.loadDir(lLink),
|
||||
/* устанавливаем обработчики на строку
|
||||
* на двойное нажатие на левую кнопку мышки */
|
||||
lLi = ai.parentElement.parentElement;
|
||||
n = pathLinks.length;
|
||||
for (i = 0; i < n; i++) {
|
||||
ai = pathLinks[i];
|
||||
link = Util.removeStr(ai.href, url),
|
||||
loadDir = CloudCmd.loadDir(link),
|
||||
|
||||
/* if we in path - set click event */
|
||||
if (lLi.className === 'path')
|
||||
Events.addClick( lLoadDir, ai );
|
||||
else {
|
||||
Events.add({
|
||||
'click' : DOM.preventDefault,
|
||||
'mousedown' : lSetCurrentFile_f,
|
||||
'contextmenu' : lOnContextMenu_f
|
||||
}, lLi);
|
||||
|
||||
Events.add('dragstart', lOnDragStart_f, ai);
|
||||
|
||||
/* если ссылка на папку, а не файл */
|
||||
if(ai.target !== '_blank'){
|
||||
Events.add({
|
||||
'dblclick' : lLoadDirOnce,
|
||||
'touchend' : lLoadDirOnce
|
||||
}, lLi);
|
||||
}
|
||||
|
||||
lLi.id = (ai.title ? ai.title : ai.textContent) +
|
||||
'(' + pPanelID + ')';
|
||||
}
|
||||
Events.addClick(loadDir, ai);
|
||||
}
|
||||
|
||||
a = DOM.getByTag('a', filesElement);
|
||||
|
||||
n = a.length;
|
||||
for (i = 0; i < n ; i++) {
|
||||
current = files[i];
|
||||
isDir = DOM.isCurrentIsDir(current);
|
||||
ai = a[i];
|
||||
|
||||
if (ai.title)
|
||||
id = ai.title;
|
||||
else
|
||||
id = ai.textContent;
|
||||
|
||||
id += '(' + panelId + ')';
|
||||
|
||||
current.id = id;
|
||||
|
||||
if (!isDir)
|
||||
events = {
|
||||
'click' : fileClick
|
||||
};
|
||||
else
|
||||
events = {
|
||||
'dblclick' : loadDirOnce,
|
||||
'touchend' : loadDirOnce,
|
||||
'click' : DOM.preventDefault,
|
||||
};
|
||||
|
||||
Util.copyObj(events, {
|
||||
'mousedown' : setCurrentFile,
|
||||
'contextmenu' : onContextMenu,
|
||||
'dragstart' : onDragStart
|
||||
});
|
||||
|
||||
Events.add(events, current);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -208,13 +226,13 @@ var Util, DOM, CloudCmd;
|
|||
}
|
||||
|
||||
function contextMenu() {
|
||||
Events.addContextMenu(function(pEvent){
|
||||
Events.addContextMenu(function(pEvent) {
|
||||
CloudCmd.Menu.ENABLED || DOM.preventDefault(pEvent);
|
||||
}, document);
|
||||
}
|
||||
|
||||
function dragndrop() {
|
||||
var panels = DOM.getByClass('panel'),
|
||||
var panels = DOM.getByClassAll('panel'),
|
||||
i = 0,
|
||||
n = panels.length,
|
||||
preventDefault = function (event) {
|
||||
|
|
@ -227,8 +245,8 @@ var Util, DOM, CloudCmd;
|
|||
},
|
||||
onDrop = function (event) {
|
||||
var reader, file, files,
|
||||
dir = DOM.getCurrentDirPath(),
|
||||
load = function(file){
|
||||
dir = Info.dirPath,
|
||||
load = function(file) {
|
||||
return function(event) {
|
||||
var path = dir + file.name,
|
||||
data = event.target.result;
|
||||
|
|
@ -268,7 +286,7 @@ var Util, DOM, CloudCmd;
|
|||
Key = CloudCmd.Key,
|
||||
lIsBind = Key && Key.isBind();
|
||||
|
||||
if(!lIsBind) {
|
||||
if (!lIsBind) {
|
||||
DOM.preventDefault(pEvent);
|
||||
lRet = 'Please make sure that you saved all work.';
|
||||
}
|
||||
|
|
@ -292,7 +310,7 @@ var Util, DOM, CloudCmd;
|
|||
}
|
||||
|
||||
function online() {
|
||||
var cssSet = Util.retFunc(DOM.cssSet, {
|
||||
var cssSet = DOM.cssSet.bind(null, {
|
||||
id :'local-droids-font',
|
||||
element : document.head,
|
||||
inner : '@font-face {font-family: "Droid Sans Mono";' +
|
||||
|
|
|
|||
409
lib/client/loader.js
Normal file
|
|
@ -0,0 +1,409 @@
|
|||
var Util, DOM;
|
||||
|
||||
(function (Util, DOMTree) {
|
||||
'use strict';
|
||||
|
||||
var Loader = Util.extendProto(LoaderProto),
|
||||
DOMProto = Object.getPrototypeOf(DOMTree);
|
||||
|
||||
Util.extend(DOMProto, Loader);
|
||||
|
||||
function LoaderProto() {
|
||||
var Images = DOM.Images,
|
||||
Events = DOM.Events,
|
||||
Loader = this;
|
||||
|
||||
/**
|
||||
* Function gets id by src
|
||||
* @param pSrc
|
||||
*
|
||||
* Example: http://domain.com/1.js -> 1_js
|
||||
*/
|
||||
this.getIdBySrc = function(pSrc) {
|
||||
var lRet = Util.isString(pSrc);
|
||||
|
||||
if (lRet) {
|
||||
var lNum = pSrc.lastIndexOf('/') + 1,
|
||||
lSub = pSrc.substr(pSrc, lNum),
|
||||
lID = Util.removeStrOneTime(pSrc, lSub );
|
||||
|
||||
/* убираем точки */
|
||||
while (lID.indexOf('.') > 0)
|
||||
lID = lID.replace('.', '_');
|
||||
|
||||
lRet = lID;
|
||||
}
|
||||
|
||||
return lRet;
|
||||
};
|
||||
|
||||
/**
|
||||
* load file countent via ajax
|
||||
*
|
||||
* @param pParams
|
||||
*/
|
||||
this.ajax = function(params) {
|
||||
var p = params,
|
||||
type = p.type || p.method || 'GET',
|
||||
xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.open(type, p.url, true);
|
||||
|
||||
if (p.responseType)
|
||||
xhr.responseType = p.responseType;
|
||||
|
||||
Events.add('progress', function(event) {
|
||||
var percent, count, msg;
|
||||
|
||||
if (event.lengthComputable) {
|
||||
percent = (event.loaded / event.total) * 100,
|
||||
count = Math.round(percent),
|
||||
msg = type + ' ' + p.url + ': ' + count + '%';
|
||||
Util.log(msg);
|
||||
}
|
||||
|
||||
}, xhr.upload);
|
||||
|
||||
Events.add('readystatechange', function(event) {
|
||||
var TYPE_JSON, type, data, isContain,
|
||||
xhr = event.target;
|
||||
|
||||
if (xhr.readyState === 4 /* Complete */) {
|
||||
TYPE_JSON = 'application/json';
|
||||
type = xhr.getResponseHeader('content-type');
|
||||
|
||||
if (xhr.status === 200 /* OK */) {
|
||||
data = xhr.response;
|
||||
isContain = Util.isContainStr(type, TYPE_JSON);
|
||||
|
||||
if (p.dataType !== 'text')
|
||||
/* If it's json - parse it as json */
|
||||
if (type && isContain)
|
||||
data = Util.parseJSON(xhr.response) || xhr.response;
|
||||
|
||||
Util.exec(p.success, data, xhr.statusText, xhr);
|
||||
}
|
||||
/* file not found or connection lost */
|
||||
else {
|
||||
/* if html given or something like thet
|
||||
* getBack just status of result
|
||||
*/
|
||||
if (type && type.indexOf('text/plain') !== 0)
|
||||
xhr.responseText = xhr.statusText;
|
||||
|
||||
Util.exec(p.error, xhr);
|
||||
}
|
||||
}
|
||||
}, xhr);
|
||||
|
||||
xhr.send(p.data);
|
||||
};
|
||||
|
||||
/**
|
||||
* create elements and load them to DOM-tree
|
||||
* one-by-one
|
||||
*
|
||||
* @param params
|
||||
* @param callback - onload function
|
||||
*/
|
||||
this.anyLoadOnLoad = function(params, callback) {
|
||||
if (Util.isArray(params)) {
|
||||
var param = params.shift(),
|
||||
func = function() {
|
||||
Loader.anyLoadOnLoad(params, callback);
|
||||
};
|
||||
|
||||
if (!param)
|
||||
Util.exec(callback);
|
||||
|
||||
else if (Util.isArray(param))
|
||||
Loader.anyLoadInParallel(param, callback);
|
||||
|
||||
else {
|
||||
if (Util.isString(param))
|
||||
param = {
|
||||
src : param
|
||||
};
|
||||
|
||||
if (!param.func)
|
||||
param.func = func;
|
||||
|
||||
Loader.anyload(param);
|
||||
}
|
||||
}
|
||||
|
||||
return Loader;
|
||||
};
|
||||
|
||||
/**
|
||||
* improve callback of funcs so
|
||||
* we pop number of function and
|
||||
* if it's last we call pCallBack
|
||||
*
|
||||
* @param params
|
||||
* @param callback - onload function
|
||||
*/
|
||||
this.anyLoadInParallel = function(params, callback) {
|
||||
var i, n, param, func,
|
||||
done = [],
|
||||
|
||||
doneFunc = function (func) {
|
||||
Util.exec(func);
|
||||
|
||||
if (!done.pop())
|
||||
Util.exec(callback);
|
||||
};
|
||||
|
||||
if (!Util.isArray(params))
|
||||
params = [params];
|
||||
|
||||
|
||||
n = params.length;
|
||||
for (i = 0; i < n; i++) {
|
||||
param = params.pop();
|
||||
|
||||
if (param) {
|
||||
done.push(i);
|
||||
|
||||
if (Util.isString(param))
|
||||
param = { src : param };
|
||||
else
|
||||
func = param.func;
|
||||
|
||||
param.func = Util.retExec(doneFunc, func);
|
||||
|
||||
Loader.anyload(param);
|
||||
}
|
||||
}
|
||||
|
||||
return Loader;
|
||||
};
|
||||
|
||||
/**
|
||||
* Функция создаёт элемент и загружает файл с src.
|
||||
*
|
||||
* @param pParams_o = {
|
||||
* name, - название тэга
|
||||
* src', - путь к файлу
|
||||
* func, - обьект, содержаий одну из функций
|
||||
* или сразу две onload и onerror
|
||||
* {onload: function() {}, onerror: function();}
|
||||
* style,
|
||||
* id,
|
||||
* element,
|
||||
* async, - true by default
|
||||
* inner: 'id{color:red, },
|
||||
* class,
|
||||
* not_append - false by default
|
||||
* }
|
||||
*/
|
||||
this.anyload = function(pParams_o) {
|
||||
var i, n, lElements_a;
|
||||
|
||||
if (!pParams_o ) return;
|
||||
|
||||
/* if a couple of params was
|
||||
* processing every of params
|
||||
* and quit
|
||||
*/
|
||||
if (Util.isArray(pParams_o)) {
|
||||
lElements_a = [];
|
||||
for(i = 0, n = pParams_o.length; i < n ; i++)
|
||||
lElements_a[i] = this.anyload(pParams_o[i]);
|
||||
|
||||
return lElements_a;
|
||||
}
|
||||
|
||||
var lName = pParams_o.name,
|
||||
lAttr = pParams_o.attribute,
|
||||
lID = pParams_o.id,
|
||||
lClass = pParams_o.className,
|
||||
lSrc = pParams_o.src,
|
||||
lFunc = pParams_o.func,
|
||||
lOnError,
|
||||
lAsync = pParams_o.async,
|
||||
lParent = pParams_o.parent || document.body,
|
||||
lInner = pParams_o.inner,
|
||||
lStyle = pParams_o.style,
|
||||
lNotAppend = pParams_o.not_append;
|
||||
|
||||
if (Util.isObject(lFunc)) {
|
||||
lOnError = lFunc.onerror;
|
||||
lFunc = lFunc.onload;
|
||||
}
|
||||
/* убираем путь к файлу, оставляя только название файла */
|
||||
if (!lID && lSrc)
|
||||
lID = DOM.getIdBySrc(lSrc);
|
||||
|
||||
var lElement = DOMTree.getById(lID);
|
||||
|
||||
/* если скрипт еще не загружен */
|
||||
if (!lElement) {
|
||||
if (!lName && lSrc) {
|
||||
|
||||
var lDot = lSrc.lastIndexOf('.'),
|
||||
lExt = lSrc.substr(lDot);
|
||||
switch(lExt) {
|
||||
case '.js':
|
||||
lName = 'script';
|
||||
break;
|
||||
case '.css':
|
||||
lName = 'link';
|
||||
lParent = document.head;
|
||||
break;
|
||||
default:
|
||||
return {code: -1, text: 'name can not be empty'};
|
||||
}
|
||||
}
|
||||
lElement = document.createElement(lName);
|
||||
|
||||
if (lID)
|
||||
lElement.id = lID;
|
||||
|
||||
if (lClass)
|
||||
lElement.className = lClass;
|
||||
|
||||
if (lSrc) {
|
||||
/* if work with css use href */
|
||||
if (lName === 'link') {
|
||||
lElement.href = lSrc;
|
||||
lElement.rel = 'stylesheet';
|
||||
} else
|
||||
lElement.src = lSrc;
|
||||
|
||||
/*
|
||||
* if passed arguments function
|
||||
* then it's onload by default
|
||||
*
|
||||
* if object - then onload and onerror
|
||||
*/
|
||||
var lLoad = function(pEvent) {
|
||||
Events.remove('load', lLoad, lElement);
|
||||
Events.remove('error', lError, lElement);
|
||||
|
||||
Util.exec(lFunc, pEvent);
|
||||
},
|
||||
|
||||
lError = function() {
|
||||
lParent.removeChild(lElement);
|
||||
|
||||
Images.showError({
|
||||
responseText: 'file ' +
|
||||
lSrc +
|
||||
' could not be loaded',
|
||||
status : 404
|
||||
});
|
||||
|
||||
Util.exec(lOnError);
|
||||
};
|
||||
|
||||
Events.add('load', lLoad, lElement);
|
||||
Events.addError(lError, lElement);
|
||||
}
|
||||
|
||||
if (lAttr)
|
||||
for(i in lAttr)
|
||||
lElement.setAttribute(i, lAttr[i]);
|
||||
|
||||
if (lStyle)
|
||||
lElement.style.cssText = lStyle;
|
||||
|
||||
if (lAsync || lAsync === undefined)
|
||||
lElement.async = true;
|
||||
|
||||
if (!lNotAppend)
|
||||
lParent.appendChild(lElement);
|
||||
|
||||
if (lInner)
|
||||
lElement.innerHTML = lInner;
|
||||
}
|
||||
/* если js-файл уже загружен
|
||||
* запускаем функцию onload
|
||||
*/
|
||||
else
|
||||
Util.exec(lFunc);
|
||||
|
||||
return lElement;
|
||||
},
|
||||
|
||||
/**
|
||||
* Функция загружает js-файл
|
||||
*
|
||||
* @param pSrc
|
||||
* @param pFunc
|
||||
*/
|
||||
this.jsload = function(pSrc, pFunc) {
|
||||
var lRet = Loader.anyload({
|
||||
name : 'script',
|
||||
src : pSrc,
|
||||
func : pFunc
|
||||
});
|
||||
|
||||
return lRet;
|
||||
},
|
||||
|
||||
/**
|
||||
* returns jsload functions
|
||||
*/
|
||||
this.retJSLoad = function(pSrc, pFunc) {
|
||||
var lRet = function() {
|
||||
return Loader.jsload(pSrc, pFunc);
|
||||
};
|
||||
|
||||
return lRet;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Функция создаёт елемент style и записывает туда стили
|
||||
* @param pParams_o - структура параметров, заполняеться таким
|
||||
* образом: {src: ' ',func: '', id: '', element: '', inner: ''}
|
||||
* все параметры опциональны
|
||||
*/
|
||||
this.cssSet = function(pParams_o) {
|
||||
pParams_o.name = 'style';
|
||||
pParams_o.parent = pParams_o.parent || document.head;
|
||||
|
||||
return Loader.anyload(pParams_o);
|
||||
},
|
||||
|
||||
/**
|
||||
* Function loads external css files
|
||||
* @pParams_o - структура параметров, заполняеться таким
|
||||
* образом: {src: ' ',func: '', id: '', element: '', inner: ''}
|
||||
* все параметры опциональны
|
||||
*/
|
||||
this.cssLoad = function(pParams_o) {
|
||||
if (Util.isArray(pParams_o)) {
|
||||
for(var i = 0, n = pParams_o.length; i < n; i++) {
|
||||
pParams_o[i].name = 'link';
|
||||
pParams_o[i].parent = pParams_o.parent || document.head;
|
||||
}
|
||||
|
||||
return Loader.anyload(pParams_o);
|
||||
}
|
||||
|
||||
else if (Util.isString(pParams_o))
|
||||
pParams_o = { src: pParams_o };
|
||||
|
||||
pParams_o.name = 'link';
|
||||
pParams_o.parent = pParams_o.parent || document.head;
|
||||
|
||||
return Loader.anyload(pParams_o);
|
||||
};
|
||||
|
||||
/**
|
||||
* load jquery from google cdn or local copy
|
||||
* @param pParams
|
||||
*/
|
||||
this.jquery = function(pParams) {
|
||||
if (!pParams)
|
||||
pParams = {};
|
||||
/* загружаем jquery: */
|
||||
Loader.jsload('//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js',{
|
||||
onload : pParams.onload,
|
||||
onerror : pParams.onerror
|
||||
});
|
||||
};
|
||||
}
|
||||
})(Util, DOM);
|
||||
|
|
@ -3,13 +3,14 @@
|
|||
*/
|
||||
var CloudCmd, Util, DOM, CloudFunc, $;
|
||||
|
||||
(function(CloudCmd, Util, DOM, CloudFunc){
|
||||
(function(CloudCmd, Util, DOM, CloudFunc) {
|
||||
'use strict';
|
||||
|
||||
CloudCmd.Menu = MenuProto;
|
||||
|
||||
function MenuProto(pPosition) {
|
||||
var Name = 'Menu',
|
||||
Info = DOM.CurrentInfo,
|
||||
Loading = false,
|
||||
Key = CloudCmd.Key,
|
||||
Events = DOM.Events,
|
||||
|
|
@ -26,27 +27,23 @@ var CloudCmd, Util, DOM, CloudFunc, $;
|
|||
Position = pPosition;
|
||||
|
||||
Util.loadOnLoad([
|
||||
Menu.show,
|
||||
setUploadToItemNames,
|
||||
DOM.jqueryLoad,
|
||||
load,
|
||||
DOM.jqueryLoad
|
||||
setUploadToItemNames,
|
||||
Menu.show
|
||||
]);
|
||||
|
||||
Events.addKey( lListener );
|
||||
|
||||
DOM.setButtonKey('f9', function() {
|
||||
var lCurrent = DOM.getCurrentFile();
|
||||
|
||||
Events.dispatch('contextmenu', lCurrent);
|
||||
});
|
||||
Events.addKey(listener);
|
||||
}
|
||||
|
||||
this.show = function() {
|
||||
Key.unsetBind();
|
||||
|
||||
if (!Loading) {
|
||||
set();
|
||||
DOM.Images.hideLoad();
|
||||
|
||||
if(Position && !Position.x )
|
||||
if (Position && !Position.x )
|
||||
Position = undefined;
|
||||
|
||||
$('li').contextMenu(Position);
|
||||
|
|
@ -70,20 +67,24 @@ var CloudCmd, Util, DOM, CloudFunc, $;
|
|||
/**
|
||||
* function get menu item object for Upload To
|
||||
*/
|
||||
function getUploadToItems(pObjectName){
|
||||
function getUploadToItems(pObjectName) {
|
||||
var i, n, lStr, lObj = {};
|
||||
if (Util.isArray(pObjectName)) {
|
||||
|
||||
n = pObjectName.length;
|
||||
for(i = 0; i < n; i++){
|
||||
for (i = 0; i < n; i++) {
|
||||
lStr = pObjectName[i];
|
||||
lObj[lStr] = getUploadToItems( lStr );
|
||||
}
|
||||
}
|
||||
else if (Util.isString(pObjectName)) {
|
||||
lObj = function(key, opt){
|
||||
DOM.getCurrentData(function(pParams) {
|
||||
CloudCmd.execFromModule(pObjectName, 'uploadFile', pParams);
|
||||
lObj = function(key, opt) {
|
||||
Info.getData(function(data) {
|
||||
var name = Info.name;
|
||||
CloudCmd.execFromModule(pObjectName, 'uploadFile', {
|
||||
name: name,
|
||||
data: data
|
||||
});
|
||||
});
|
||||
|
||||
Util.log('Uploading to ' + pObjectName+ '...');
|
||||
|
|
@ -96,20 +97,27 @@ var CloudCmd, Util, DOM, CloudFunc, $;
|
|||
/**
|
||||
* get menu item
|
||||
*/
|
||||
function getItem(pName, pCallBack){
|
||||
var lRet = {
|
||||
name : pName
|
||||
function getItem(pName, pCallBack) {
|
||||
var lRet,
|
||||
name = pName,
|
||||
icon = name && name.toLowerCase();
|
||||
|
||||
icon = Util.removeStr(icon, ['(', ')']);
|
||||
icon = Util.replaceStr(icon, ' ', '-');
|
||||
|
||||
lRet = {
|
||||
name : pName,
|
||||
icon : icon
|
||||
};
|
||||
|
||||
if( Util.isFunction(pCallBack) )
|
||||
if (Util.isFunction(pCallBack))
|
||||
lRet.callback = pCallBack;
|
||||
|
||||
else if ( Util.isObject(pCallBack) ){
|
||||
if(pCallBack.name)
|
||||
else if (Util.isObject(pCallBack))
|
||||
if (pCallBack.name)
|
||||
lRet.items = pCallBack;
|
||||
else
|
||||
lRet.items = getAllItems(pCallBack);
|
||||
}
|
||||
|
||||
return lRet;
|
||||
}
|
||||
|
|
@ -118,13 +126,13 @@ var CloudCmd, Util, DOM, CloudFunc, $;
|
|||
* get all menu items
|
||||
* pItems = [{pName, pFunc}]
|
||||
*/
|
||||
function getAllItems(pItems){
|
||||
function getAllItems(pItems) {
|
||||
var lRet = {},
|
||||
lName,
|
||||
lFunc;
|
||||
|
||||
if(pItems)
|
||||
for(lName in pItems){
|
||||
if (pItems)
|
||||
for (lName in pItems) {
|
||||
lFunc = pItems[lName];
|
||||
lRet[lName] = getItem(lName, lFunc);
|
||||
}
|
||||
|
|
@ -135,30 +143,31 @@ var CloudCmd, Util, DOM, CloudFunc, $;
|
|||
/**
|
||||
* download menu item callback
|
||||
*/
|
||||
function downloadFromMenu(key, opt){
|
||||
function downloadFromMenu(key, opt) {
|
||||
DOM.Images.showLoad();
|
||||
|
||||
var lPath = DOM.getCurrentPath(),
|
||||
var TIME = 1000,
|
||||
lPath = Info.path,
|
||||
lId = DOM.getIdBySrc(lPath),
|
||||
lDir = DOM.isCurrentIsDir() ? '&&json' : '';
|
||||
lDir = Info.isDir ? '&&json' : '';
|
||||
|
||||
Util.log('downloading file ' + lPath +'...');
|
||||
|
||||
lPath = CloudFunc.FS + lPath + '?download' + lDir;
|
||||
|
||||
if (!DOM.getById(lId)){
|
||||
if (!DOM.getById(lId)) {
|
||||
var lDownload = DOM.anyload({
|
||||
name : 'iframe',
|
||||
async : false,
|
||||
className : 'hidden',
|
||||
src : lPath,
|
||||
func : Util.retFunc(DOM.Images.hideLoad)
|
||||
func : DOM.Images.hideLoad
|
||||
});
|
||||
|
||||
DOM.Images.hideLoad();
|
||||
setTimeout(function() {
|
||||
document.body.removeChild(lDownload);
|
||||
}, 10000);
|
||||
}, TIME);
|
||||
}
|
||||
else
|
||||
DOM.Images.showError({
|
||||
|
|
@ -169,30 +178,30 @@ var CloudCmd, Util, DOM, CloudFunc, $;
|
|||
/**
|
||||
* function return configureation for menu
|
||||
*/
|
||||
function getConfig (){
|
||||
function getConfig () {
|
||||
var lRet,
|
||||
lMenuItems = {
|
||||
'View' : Util.retFunc(show, 'View'),
|
||||
'Edit' : Util.retFunc(show, 'Edit'),
|
||||
'Rename' : function(){
|
||||
setTimeout( Util.retFunc(DOM.renameCurrent), 100);
|
||||
'View' : show.bind(null, 'View'),
|
||||
'Edit' : show.bind(null, 'Edit'),
|
||||
'Rename' : function() {
|
||||
setTimeout(DOM.renameCurrent, 100);
|
||||
},
|
||||
'Delete' : Util.retFunc(DOM.promptDeleteSelected),
|
||||
'Delete' : DOM.promptDeleteSelected,
|
||||
'(Un)Select All': DOM.toggleAllSelectedFiles,
|
||||
'Zip file' : DOM.zipFile
|
||||
'Zip file' : DOM.zipFile
|
||||
};
|
||||
|
||||
if (UploadToItemNames.length)
|
||||
lMenuItems['Upload to'] = getUploadToItems(UploadToItemNames);
|
||||
|
||||
lMenuItems.Download = Util.retFunc(downloadFromMenu);
|
||||
lMenuItems.Download = downloadFromMenu;
|
||||
|
||||
lMenuItems.New = {
|
||||
'File' : DOM.promptNewFile,
|
||||
'Dir' : DOM.promptNewDir,
|
||||
'Directory' : DOM.promptNewDir,
|
||||
|
||||
'From Filepicker' : function(){
|
||||
CloudCmd.execFromModule('FilePicker', 'saveFile', function(pName, pData){
|
||||
'From Cloud' : function() {
|
||||
CloudCmd.execFromModule('FilePicker', 'saveFile', function(pName, pData) {
|
||||
var lPath = DOM.getCurrentDirPath() + pName;
|
||||
|
||||
DOM.RESTful.save(lPath, pData, CloudCmd.refresh);
|
||||
|
|
@ -204,23 +213,23 @@ var CloudCmd, Util, DOM, CloudFunc, $;
|
|||
// define which elements trigger this menu
|
||||
selector: 'li',
|
||||
|
||||
callback: function(key, options) {
|
||||
var m = "clicked: " + key;
|
||||
Util.log(m, options);
|
||||
|
||||
Key.setBind();
|
||||
},
|
||||
|
||||
// define the elements of the menu
|
||||
items : getAllItems(lMenuItems),
|
||||
events :{
|
||||
hide: clickProcessing
|
||||
hide: function() {
|
||||
var event = window.event;
|
||||
|
||||
if (!event || !event.keyCode)
|
||||
clickProcessing();
|
||||
else if (event.keyCode)
|
||||
listener(event);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return lRet;
|
||||
|
||||
function show(pName){
|
||||
function show(pName) {
|
||||
var lEditor = CloudCmd[pName],
|
||||
lResult = Util.exec(lEditor);
|
||||
|
||||
|
|
@ -232,22 +241,35 @@ var CloudCmd, Util, DOM, CloudFunc, $;
|
|||
/** function loads css and js of Menu
|
||||
* @param pCallBack
|
||||
*/
|
||||
function load(pCallBack){
|
||||
function load(pCallBack) {
|
||||
Util.time(Name + ' load');
|
||||
|
||||
var lDir = '/lib/client/menu/',
|
||||
lFiles = [
|
||||
lDir + 'contextMenu.js',
|
||||
lDir + 'contextMenu.css'
|
||||
var dir = CloudCmd.LIBDIRCLIENT + 'menu/',
|
||||
files = [
|
||||
dir + 'contextMenu.js',
|
||||
dir + 'contextMenu.css'
|
||||
];
|
||||
|
||||
DOM.anyLoadInParallel(lFiles, function(){
|
||||
DOM.anyLoadInParallel(files, function() {
|
||||
setCSS();
|
||||
Util.timeEnd(Name + ' load');
|
||||
Loading = false;
|
||||
Util.exec(pCallBack);
|
||||
});
|
||||
}
|
||||
|
||||
function setCSS() {
|
||||
DOM.cssSet({
|
||||
id : 'menu-css',
|
||||
inner: '.context-menu-item.icon-edit {' +
|
||||
'background-image: none;' +
|
||||
'}' +
|
||||
'.context-menu-item.icon-delete {' +
|
||||
'background-image: none;' +
|
||||
'}'
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Menu works in some crazy way so need a
|
||||
* little hack to get every thing work out.
|
||||
|
|
@ -260,9 +282,10 @@ var CloudCmd, Util, DOM, CloudFunc, $;
|
|||
* is not going on. All magic happening in
|
||||
* DOM tree.
|
||||
*/
|
||||
function clickProcessing(){
|
||||
function clickProcessing() {
|
||||
var lLayer = DOM.getById('context-menu-layer');
|
||||
if(lLayer){
|
||||
|
||||
if (lLayer && Position) {
|
||||
DOM.hide(lLayer);
|
||||
|
||||
var lElement = document.elementFromPoint(Position.x, Position.y),
|
||||
|
|
@ -289,11 +312,11 @@ var CloudCmd, Util, DOM, CloudFunc, $;
|
|||
}
|
||||
|
||||
|
||||
function set(){
|
||||
if(!MenuSeted){
|
||||
function set() {
|
||||
if (!MenuSeted) {
|
||||
$.contextMenu(getConfig());
|
||||
MenuSeted = true;
|
||||
DOM.Events.add('mousemove', function(pEvent){
|
||||
DOM.Events.add('mousemove', function(pEvent) {
|
||||
Position = {
|
||||
x : pEvent.clientX,
|
||||
y : pEvent.clientY
|
||||
|
|
@ -302,22 +325,21 @@ var CloudCmd, Util, DOM, CloudFunc, $;
|
|||
}
|
||||
}
|
||||
|
||||
function lListener(pEvent){
|
||||
var lCurrent,
|
||||
lF9 = Key.F9,
|
||||
lESC = Key.ESC,
|
||||
lKey = pEvent.keyCode,
|
||||
lIsBind = Key.isBind();
|
||||
function listener(event) {
|
||||
var current,
|
||||
F9 = Key.F9,
|
||||
ESC = Key.ESC,
|
||||
key = event.keyCode,
|
||||
isBind = Key.isBind();
|
||||
|
||||
if (isBind && key === F9) {
|
||||
current = DOM.getCurrentFile();
|
||||
$(current).contextmenu();
|
||||
|
||||
if (lIsBind && lKey === lF9) {
|
||||
lCurrent = DOM.getCurrentFile();
|
||||
$(lCurrent).contextmenu();
|
||||
|
||||
DOM.preventDefault(pEvent);
|
||||
}
|
||||
else if (lKey === lESC)
|
||||
DOM.preventDefault(event);
|
||||
} else if (key === ESC)
|
||||
Key.setBind();
|
||||
}
|
||||
}
|
||||
|
||||
init();
|
||||
}
|
||||
|
|
|
|||
63
lib/client/notify.js
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
var Util, DOM, CloudCmd;
|
||||
|
||||
(function(Util, DOM) {
|
||||
'use strict';
|
||||
|
||||
var Notify = Util.extendProto(NotifyProto),
|
||||
DOMProto = Object.getPrototypeOf(DOM);
|
||||
|
||||
Util.extend(DOMProto, {
|
||||
Notify: Notify
|
||||
});
|
||||
|
||||
function NotifyProto() {
|
||||
var Events = DOM.Events,
|
||||
Show, Allow,
|
||||
Notify = this,
|
||||
Notification = window.Notification;
|
||||
|
||||
Events.add({
|
||||
'blur' :function() {
|
||||
Show = true;
|
||||
},
|
||||
'focus': function() {
|
||||
Show = false;
|
||||
}
|
||||
}, window);
|
||||
|
||||
this.send = function(msg) {
|
||||
CloudCmd.getConfig(function(config) {
|
||||
var notify,
|
||||
notifications = config.notifications,
|
||||
focus = window.focus.bind(window),
|
||||
granted = Notify.check();
|
||||
|
||||
if (notifications && granted && Show) {
|
||||
notify = new Notification(msg, {
|
||||
icon: '/img/favicon/favicon-notify.png',
|
||||
});
|
||||
|
||||
Events.addClick(focus, notify);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.check = function () {
|
||||
var ret,
|
||||
Not = Notification,
|
||||
perm = Not && Not.permission;
|
||||
|
||||
if (perm === 'granted')
|
||||
ret = true;
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
this.request = function () {
|
||||
var Not = Notification;
|
||||
|
||||
if (Not)
|
||||
Not.requestPermission();
|
||||
};
|
||||
}
|
||||
})(Util, DOM);
|
||||
|
|
@ -1,20 +1,20 @@
|
|||
var Util, DOM, jQuery;
|
||||
|
||||
(function(Util, DOM, $){
|
||||
(function(Util, DOM, $) {
|
||||
'use strict';
|
||||
|
||||
if(!window.XMLHttpRequest || !document.head)
|
||||
if (!window.XMLHttpRequest || !document.head)
|
||||
DOM.ajax = $.ajax;
|
||||
|
||||
/* setting head ie6 - ie8 */
|
||||
if(!document.head){
|
||||
if (!document.head) {
|
||||
document.head = $('head')[0];
|
||||
|
||||
/*
|
||||
{name: '', src: ' ',func: '', style: '', id: '', parent: '',
|
||||
async: false, inner: 'id{color:red, }, class:'', not_append: false}
|
||||
*/
|
||||
DOM.cssSet = function(pParams_o){
|
||||
DOM.cssSet = function(pParams_o) {
|
||||
var lElement = '<style ';
|
||||
|
||||
if (pParams_o.id) lElement += 'id=' + pParams_o.id + ' ';
|
||||
|
|
@ -28,15 +28,24 @@ var Util, DOM, jQuery;
|
|||
.appendTo(pParams_o.parent || document.head);
|
||||
};
|
||||
}
|
||||
|
||||
if (!Function.bind)
|
||||
Function.prototype.bind = function (context) {
|
||||
var aArgs = Util.slice(arguments, 1),
|
||||
fToBind = this,
|
||||
NOP = function () {},
|
||||
fBound = function () {
|
||||
var arr = Util.slice(arguments),
|
||||
args = aArgs.concat(arr);
|
||||
|
||||
return fToBind.apply(context, args);
|
||||
};
|
||||
|
||||
NOP.prototype = this.prototype;
|
||||
fBound.prototype = new NOP();
|
||||
|
||||
/* setting function context (this) */
|
||||
Util.bind = function(pFunction, pContext){
|
||||
var lRet;
|
||||
|
||||
lRet = $.proxy(pFunction, pContext);
|
||||
|
||||
return lRet;
|
||||
};
|
||||
return fBound;
|
||||
};
|
||||
|
||||
/*
|
||||
* typeof callback === "function" should not be used,
|
||||
|
|
@ -51,10 +60,10 @@ var Util, DOM, jQuery;
|
|||
* @param pType
|
||||
* @param pListener
|
||||
*/
|
||||
DOM.addListener = function(pType, pListener, pCapture, pElement){
|
||||
DOM.addListener = function(pType, pListener, pCapture, pElement) {
|
||||
var lRet;
|
||||
|
||||
if(!pElement)
|
||||
if (!pElement)
|
||||
pElement = window;
|
||||
|
||||
lRet = $(pElement).bind(pType, null, pListener);
|
||||
|
|
@ -62,11 +71,11 @@ var Util, DOM, jQuery;
|
|||
return lRet;
|
||||
};
|
||||
|
||||
if(!document.removeEventListener){
|
||||
DOM.removeListener = function(pType, pListener, pCapture, pElement){
|
||||
if (!document.removeEventListener) {
|
||||
DOM.removeListener = function(pType, pListener, pCapture, pElement) {
|
||||
var lRet;
|
||||
|
||||
if(!pElement)
|
||||
if (!pElement)
|
||||
pElement = window;
|
||||
|
||||
$(pElement).unbind(pType, pListener);
|
||||
|
|
@ -75,59 +84,72 @@ var Util, DOM, jQuery;
|
|||
};
|
||||
}
|
||||
|
||||
if(!document.getElementsByClassName){
|
||||
DOM.getByClass = function(pClass, pElement){
|
||||
if (!document.getElementsByClassName) {
|
||||
DOM.getByClassAll = function(pClass, pElement) {
|
||||
var lClass = '.' + pClass,
|
||||
lResult;
|
||||
|
||||
if(pElement)
|
||||
if (pElement)
|
||||
lResult = $(pElement).find(lClass);
|
||||
else lResult = $.find(lClass);
|
||||
else
|
||||
lResult = $.find(lClass);
|
||||
|
||||
return lResult;
|
||||
};
|
||||
}
|
||||
|
||||
DOM.scrollByPages = Util.retFalse;
|
||||
/* function polyfill webkit standart function */
|
||||
DOM.scrollIntoViewIfNeeded = function(pElement, centerIfNeeded){
|
||||
if(!window.getComputedStyle)
|
||||
return;
|
||||
/*
|
||||
https://gist.github.com/2581101
|
||||
*/
|
||||
centerIfNeeded = arguments.length === 0 ? true : !!centerIfNeeded;
|
||||
DOM.scrollByPages = Util.retFalse;
|
||||
/* function polyfill webkit standart function
|
||||
* https://gist.github.com/2581101
|
||||
*/
|
||||
DOM.scrollIntoViewIfNeeded = function(pElement, centerIfNeeded) {
|
||||
var parent,
|
||||
topWidth,
|
||||
leftWidth,
|
||||
parentComputedStyle,
|
||||
parentBorderTopWidth,
|
||||
parentBorderLeftWidth,
|
||||
overTop,
|
||||
overBottom,
|
||||
overLeft,
|
||||
overRight,
|
||||
alignWithTop;
|
||||
|
||||
if (window.getComputedStyle) {
|
||||
if (arguments.length === 1)
|
||||
centerIfNeeded = false;
|
||||
|
||||
var parent = pElement.parentNode,
|
||||
parentComputedStyle = window.getComputedStyle(parent, null),
|
||||
parentBorderTopWidth =
|
||||
parseInt(parentComputedStyle.getPropertyValue('border-top-width'), 10),
|
||||
|
||||
parentBorderLeftWidth =
|
||||
parseInt(parentComputedStyle.getPropertyValue('border-left-width'), 10),
|
||||
|
||||
overTop = pElement.offsetTop - parent.offsetTop < parent.scrollTop,
|
||||
overBottom =
|
||||
(pElement.offsetTop -
|
||||
parent.offsetTop +
|
||||
pElement.clientHeight -
|
||||
parentBorderTopWidth) >
|
||||
(parent.scrollTop + parent.clientHeight),
|
||||
|
||||
overLeft = pElement.offsetLeft -
|
||||
parent.offsetLeft < parent.scrollLeft,
|
||||
|
||||
overRight =
|
||||
(pElement.offsetLeft -
|
||||
parent.offsetLeft +
|
||||
pElement.clientWidth -
|
||||
parentBorderLeftWidth) >
|
||||
(parent.scrollLeft + parent.clientWidth),
|
||||
parent = pElement.parentNode;
|
||||
parentComputedStyle = window.getComputedStyle(parent, null);
|
||||
|
||||
topWidth = parentComputedStyle.getPropertyValue('border-top-width');
|
||||
leftWidth = parentComputedStyle.getPropertyValue('border-left-width');
|
||||
|
||||
parentBorderTopWidth = parseInt(topWidth, 10);
|
||||
parentBorderLeftWidth = parseInt(leftWidth, 10);
|
||||
|
||||
alignWithTop = overTop && !overBottom;
|
||||
overTop = pElement.offsetTop - parent.offsetTop < parent.scrollTop,
|
||||
overBottom =
|
||||
(pElement.offsetTop -
|
||||
parent.offsetTop +
|
||||
pElement.clientHeight -
|
||||
parentBorderTopWidth) >
|
||||
(parent.scrollTop + parent.clientHeight),
|
||||
|
||||
overLeft = pElement.offsetLeft -
|
||||
parent.offsetLeft < parent.scrollLeft,
|
||||
|
||||
overRight =
|
||||
(pElement.offsetLeft -
|
||||
parent.offsetLeft +
|
||||
pElement.clientWidth -
|
||||
parentBorderLeftWidth) >
|
||||
(parent.scrollLeft + parent.clientWidth),
|
||||
|
||||
alignWithTop = overTop && !overBottom;
|
||||
|
||||
if ((overTop || overBottom) && centerIfNeeded)
|
||||
parent.scrollTop =
|
||||
parent.scrollTop =
|
||||
pElement.offsetTop -
|
||||
parent.offsetTop -
|
||||
parent.clientHeight / 2 -
|
||||
|
|
@ -135,55 +157,55 @@ var Util, DOM, jQuery;
|
|||
pElement.clientHeight / 2;
|
||||
|
||||
if ((overLeft || overRight) && centerIfNeeded)
|
||||
parent.scrollLeft =
|
||||
parent.scrollLeft =
|
||||
pElement.offsetLeft -
|
||||
parent.offsetLeft -
|
||||
parent.clientWidth / 2 -
|
||||
parentBorderLeftWidth +
|
||||
pElement.clientWidth / 2;
|
||||
|
||||
if ( (overTop || overBottom || overLeft || overRight) &&
|
||||
!centerIfNeeded)
|
||||
pElement.scrollIntoView(alignWithTop);
|
||||
};
|
||||
if ((overTop || overBottom || overLeft || overRight) && !centerIfNeeded)
|
||||
pElement.scrollIntoView(alignWithTop);
|
||||
}
|
||||
};
|
||||
|
||||
if(!document.body.classList){
|
||||
if (!document.body.classList) {
|
||||
|
||||
DOM.isContainClass = function(pElement, pClass){
|
||||
DOM.isContainClass = function(pElement, pClass) {
|
||||
var lRet,
|
||||
lClassName = pElement && pElement.className;
|
||||
|
||||
if(lClassName)
|
||||
if (lClassName)
|
||||
lRet = lClassName.indexOf(pClass) > 0;
|
||||
|
||||
return lRet;
|
||||
};
|
||||
|
||||
DOM.addClass = function(pElement, pClass){
|
||||
DOM.addClass = function(pElement, pClass) {
|
||||
var lRet,
|
||||
lClassName = pElement && pElement.className,
|
||||
lSpaceChar = lClassName ? ' ' : '';
|
||||
|
||||
lRet = !DOM.isContainClass(pElement, pClass);
|
||||
if( lRet )
|
||||
if ( lRet )
|
||||
pElement.className += lSpaceChar + pClass;
|
||||
|
||||
return lRet;
|
||||
};
|
||||
|
||||
DOM.removeClass = function(pElement, pClass){
|
||||
DOM.removeClass = function(pElement, pClass) {
|
||||
var lClassName = pElement.className;
|
||||
|
||||
if(lClassName.length > pClass.length)
|
||||
if (lClassName.length > pClass.length)
|
||||
pElement.className = lClassName.replace(pClass, '');
|
||||
};
|
||||
}
|
||||
|
||||
if(!window.JSON){
|
||||
if (!window.JSON) {
|
||||
Util.parseJSON = $.parseJSON;
|
||||
|
||||
/* https://gist.github.com/754454 */
|
||||
Util.stringifyJSON = function(pObj){
|
||||
Util.stringifyJSON = function(pObj) {
|
||||
var lRet;
|
||||
|
||||
if (!Util.isObject(pObj) || pObj === null) {
|
||||
|
|
@ -213,26 +235,26 @@ var Util, DOM, jQuery;
|
|||
};
|
||||
}
|
||||
|
||||
if(!window.localStorage){
|
||||
var Storage = function(){
|
||||
if (!window.localStorage) {
|
||||
var Storage = function() {
|
||||
/* приватный переключатель возможности работы с кэшем */
|
||||
var StorageAllowed,
|
||||
Data = {};
|
||||
|
||||
/* функция проверяет возможно ли работать с кэшем каким-либо образом */
|
||||
this.isAllowed = function(){
|
||||
this.isAllowed = function() {
|
||||
return StorageAllowed;
|
||||
};
|
||||
|
||||
this.setAllowed = function(pAllowed){
|
||||
this.setAllowed = function(pAllowed) {
|
||||
StorageAllowed = pAllowed;
|
||||
return pAllowed;
|
||||
};
|
||||
|
||||
/** remove element */
|
||||
this.remove = function(pItem){
|
||||
this.remove = function(pItem) {
|
||||
var lRet = this;
|
||||
if(StorageAllowed)
|
||||
if (StorageAllowed)
|
||||
delete Data[pItem];
|
||||
|
||||
return lRet;
|
||||
|
|
@ -242,40 +264,40 @@ var Util, DOM, jQuery;
|
|||
* в нём есть нужная нам директория -
|
||||
* записываем данные в него
|
||||
*/
|
||||
this.set = function(pName, pData){
|
||||
this.set = function(pName, pData) {
|
||||
var lRet = this;
|
||||
|
||||
if(StorageAllowed && pName && pData)
|
||||
if (StorageAllowed && pName && pData)
|
||||
Data[pName] = pData;
|
||||
|
||||
return lRet;
|
||||
},
|
||||
|
||||
/** Если доступен Storage принимаем из него данные*/
|
||||
this.get = function(pName){
|
||||
this.get = function(pName) {
|
||||
var lRet = false;
|
||||
|
||||
if(StorageAllowed)
|
||||
if (StorageAllowed)
|
||||
lRet = Data[pName];
|
||||
|
||||
return lRet;
|
||||
},
|
||||
|
||||
/* get all Storage from local storage */
|
||||
this.getAll = function(){
|
||||
this.getAll = function() {
|
||||
var lRet = null;
|
||||
|
||||
if(StorageAllowed)
|
||||
if (StorageAllowed)
|
||||
lRet = Data;
|
||||
|
||||
return lRet;
|
||||
};
|
||||
|
||||
/** функция чистит весь кэш для всех каталогов*/
|
||||
this.clear = function(){
|
||||
this.clear = function() {
|
||||
var lRet = this;
|
||||
|
||||
if(StorageAllowed)
|
||||
if (StorageAllowed)
|
||||
Data = {};
|
||||
|
||||
return lRet;
|
||||
|
|
|
|||
|
|
@ -1,92 +1,104 @@
|
|||
/* module make possible connectoin thrue socket.io on a client */
|
||||
var CloudCmd, Util, DOM, io;
|
||||
(function(CloudCmd, Util, DOM) {
|
||||
|
||||
var CloudCmd, Util, DOM, CloudFunc, io;
|
||||
|
||||
(function(CloudCmd, Util, DOM, CloudFunc) {
|
||||
'use strict';
|
||||
|
||||
var Messages = [],
|
||||
socket,
|
||||
Terminal,
|
||||
CloudCmd.Socket = SocketProto;
|
||||
|
||||
ERROR_MSG = 'could not connect to socket.io\n'+
|
||||
'npm i socket.io';
|
||||
|
||||
DOM.jsload('/socket.io/lib/socket.io.js', {
|
||||
onerror : Util.retFunc(Util.log, ERROR_MSG),
|
||||
onload : connect
|
||||
});
|
||||
|
||||
function connect() {
|
||||
var FIVE_SECONDS = 5000;
|
||||
|
||||
socket = io.connect(CloudCmd.HOST, {
|
||||
'max reconnection attempts' : Math.pow(2, 32),
|
||||
'reconnection limit' : FIVE_SECONDS
|
||||
});
|
||||
|
||||
CloudCmd.Socket = socket;
|
||||
|
||||
socket.on('connect', function () {
|
||||
outToTerminal({stdout: 'socket connected\n'});
|
||||
});
|
||||
|
||||
socket.on('message', function (msg) {
|
||||
var lMsg = Util.parseJSON(msg);
|
||||
function SocketProto(callback) {
|
||||
var Socket = Util.exec.bind(Util),
|
||||
AllListeners = {},
|
||||
socket,
|
||||
|
||||
outToTerminal(lMsg);
|
||||
|
||||
});
|
||||
CONNECTED = 'socket connected\n',
|
||||
DISCONNECTED = 'socket disconnected\n',
|
||||
|
||||
ERROR_MSG = 'could not connect to socket.io\n'+
|
||||
'npm i socket.io';
|
||||
|
||||
socket.on('disconnect', function () {
|
||||
outToTerminal({stderr: 'socket disconected\n'});
|
||||
});
|
||||
Socket.on = addListener;
|
||||
Socket.addListener = addListener;
|
||||
Socket.removeListener = removeListener;
|
||||
Socket.send = send;
|
||||
Socket.emit = emit;
|
||||
|
||||
socket.on('reconnect_failed', function () {
|
||||
Util.log('Could not reconnect. Reload page.');
|
||||
});
|
||||
}
|
||||
Socket.CONNECTED = CONNECTED;
|
||||
Socket.DISCONNECTED = DISCONNECTED;
|
||||
|
||||
function outToTerminal(pMsg) {
|
||||
var i, n, lResult, lStdout, lStderr,
|
||||
lConsole = CloudCmd.Console;
|
||||
DOM.Images.hideLoad();
|
||||
|
||||
if (Util.isObject(lConsole)) {
|
||||
if (Messages.length) {
|
||||
/* show oll msg from buffer */
|
||||
for (i = 0, n = Messages.length; i < n; i++) {
|
||||
lStdout = Messages[i].stdout;
|
||||
lStderr = Messages[i].stderr;
|
||||
function init(callback) {
|
||||
DOM.jsload('/socket.io/lib/socket.io.js', {
|
||||
onerror : Util.log.bind(Util, ERROR_MSG),
|
||||
onload : function() {
|
||||
Util.exec(callback);
|
||||
|
||||
if (lStdout)
|
||||
lConsole.log(lStdout);
|
||||
|
||||
if (lStderr) {
|
||||
/* if it's object - convert is to string' */
|
||||
if (Util.isObject(lStderr))
|
||||
lStderr = Util.stringifyJSON(lStderr);
|
||||
|
||||
lConsole.error(lStderr);
|
||||
}
|
||||
if (!socket)
|
||||
connect();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addListener(name, func) {
|
||||
CloudFunc.addListener(name, func, AllListeners, socket);
|
||||
}
|
||||
|
||||
function removeListener(name, func) {
|
||||
CloudFunc.removeListener(name, func, AllListeners, socket);
|
||||
}
|
||||
|
||||
function send(data) {
|
||||
if (socket)
|
||||
socket.send(data);
|
||||
}
|
||||
|
||||
function emit(channel, data) {
|
||||
if (socket)
|
||||
socket.emit(channel, data);
|
||||
}
|
||||
|
||||
function setListeners(all, socket) {
|
||||
var i, n, name, func, listeners;
|
||||
|
||||
for (name in all) {
|
||||
listeners = all[name];
|
||||
n = listeners.length;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
func = listeners[i];
|
||||
|
||||
if (func && socket)
|
||||
socket.on(name, func);
|
||||
}
|
||||
Messages = [];
|
||||
}
|
||||
|
||||
lStdout = pMsg.stdout;
|
||||
lStderr = pMsg.stderr;
|
||||
|
||||
if (lStdout)
|
||||
lResult = lConsole.log(lStdout);
|
||||
|
||||
if (lStderr)
|
||||
lResult = lConsole.error(lStderr);
|
||||
}
|
||||
else
|
||||
/* if term not accesable save msg to buffer */
|
||||
Messages.push(pMsg);
|
||||
|
||||
Util.log(pMsg);
|
||||
function connect() {
|
||||
var FIVE_SECONDS = 5000;
|
||||
|
||||
socket = io.connect(CloudCmd.HOST, {
|
||||
'max reconnection attempts' : Math.pow(2, 32),
|
||||
'reconnection limit' : FIVE_SECONDS
|
||||
});
|
||||
|
||||
socket.on('connect', function () {
|
||||
Util.log(CONNECTED);
|
||||
});
|
||||
|
||||
setListeners(AllListeners, socket);
|
||||
|
||||
socket.on('disconnect', function () {
|
||||
Util.log(DISCONNECTED);
|
||||
});
|
||||
|
||||
socket.on('reconnect_failed', function () {
|
||||
Util.log('Could not reconnect. Reload page.');
|
||||
});
|
||||
}
|
||||
|
||||
return lResult;
|
||||
init(callback);
|
||||
|
||||
return Socket;
|
||||
}
|
||||
|
||||
})(CloudCmd, Util, DOM);
|
||||
})(CloudCmd, Util, DOM, CloudFunc);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
var CloudCmd, Util, DOM, Dropbox, cb, Client;
|
||||
var CloudCmd, Util, DOM, CloudFunc, Dropbox, cb, Client;
|
||||
|
||||
(function(CloudCmd, Util, DOM){
|
||||
(function(CloudCmd, Util, DOM, CloudFunc){
|
||||
'use strict';
|
||||
|
||||
CloudCmd.DropBox = DropBoxProto;
|
||||
|
|
@ -10,10 +10,10 @@ var CloudCmd, Util, DOM, Dropbox, cb, Client;
|
|||
|
||||
function init(pCallBack){
|
||||
Util.loadOnLoad([
|
||||
Util.retExec(pCallBack),
|
||||
getUserData,
|
||||
load,
|
||||
DropBoxStore.login,
|
||||
load
|
||||
getUserData,
|
||||
Util.retExec(pCallBack)
|
||||
]);
|
||||
|
||||
}
|
||||
|
|
@ -213,4 +213,4 @@ var CloudCmd, Util, DOM, Dropbox, cb, Client;
|
|||
init(pCallBack);
|
||||
}
|
||||
|
||||
})(CloudCmd, Util, DOM);
|
||||
})(CloudCmd, Util, DOM, CloudFunc);
|
||||
|
|
|
|||
|
|
@ -1,66 +1,68 @@
|
|||
var CloudCmd, Util, DOM, $, filepicker;
|
||||
var CloudCmd, Util, DOM, filepicker;
|
||||
|
||||
(function(CloudCmd, Util, DOM) {
|
||||
'use strict';
|
||||
|
||||
var FilePicker = function(pCallBack) {
|
||||
function init(pCallBack) {
|
||||
CloudCmd.FilePicker = FilePickerProto;
|
||||
|
||||
function FilePickerProto(callback) {
|
||||
|
||||
function init(callback) {
|
||||
Util.loadOnLoad([
|
||||
Util.retExec(pCallBack),
|
||||
load
|
||||
load,
|
||||
callback,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
this.uploadFile = function(pParams, pCallBack) {
|
||||
var lContent = pParams.data,
|
||||
lName = pParams.name;
|
||||
this.uploadFile = function(params) {
|
||||
var content = params.data,
|
||||
name = params.name,
|
||||
log = Util.log.bind(Util);
|
||||
|
||||
filepicker.store(lContent, {
|
||||
filepicker.store(content, {
|
||||
mimetype: '',
|
||||
filename: lName
|
||||
filename: name
|
||||
},
|
||||
function(pFPFile) {
|
||||
Util.log(pFPFile);
|
||||
function(fpFile) {
|
||||
log(fpFile);
|
||||
|
||||
filepicker.exportFile(pFPFile, Util.log, Util.log);
|
||||
filepicker.exportFile(fpFile, log, log);
|
||||
});
|
||||
};
|
||||
|
||||
this.saveFile = function(pCallBack) {
|
||||
filepicker.pick(function(FPFile) {
|
||||
Util.log(FPFile);
|
||||
this.saveFile = function(callback) {
|
||||
filepicker.pick(function(fpFile) {
|
||||
Util.log(fpFile);
|
||||
|
||||
DOM.ajax({
|
||||
url : FPFile.url,
|
||||
url : fpFile.url,
|
||||
responseType :'arraybuffer',
|
||||
success : function(pData) {
|
||||
Util.exec(pCallBack, FPFile.filename, pData);
|
||||
success : function(data) {
|
||||
Util.exec(callback, fpFile.filename, data);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function load(pCallBack) {
|
||||
function load(callback) {
|
||||
Util.time('filepicker load');
|
||||
var lHTTP = document.location.protocol;
|
||||
|
||||
DOM.jsload(lHTTP + '//api.filepicker.io/v1/filepicker.js', function() {
|
||||
CloudCmd.getModules(function(pModules) {
|
||||
var lStorage = Util.findObjByNameInArr(pModules, 'storage'),
|
||||
lFilePicker = Util.findObjByNameInArr(lStorage, 'FilePicker'),
|
||||
lKey = lFilePicker && lFilePicker.key;
|
||||
DOM.jsload('//api.filepicker.io/v1/filepicker.js', function() {
|
||||
CloudCmd.getModules(function(modules) {
|
||||
var storage = Util.findObjByNameInArr(modules, 'storage'),
|
||||
picker = Util.findObjByNameInArr(storage, 'FilePicker'),
|
||||
key = picker && picker.key;
|
||||
|
||||
filepicker.setKey(key);
|
||||
|
||||
filepicker.setKey(lKey);
|
||||
DOM.Images.hideLoad();
|
||||
Util.timeEnd('filepicker loaded');
|
||||
Util.exec(pCallBack);
|
||||
Util.exec(callback);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
init(pCallBack);
|
||||
};
|
||||
|
||||
CloudCmd.FilePicker = new FilePicker();
|
||||
init(callback);
|
||||
}
|
||||
})(CloudCmd, Util, DOM);
|
||||
|
|
|
|||
|
|
@ -99,9 +99,9 @@ var CloudCmd, Util, DOM, gapi;
|
|||
|
||||
|
||||
GDrive.init = function(pCallBack){
|
||||
Util.loadOnLoad([
|
||||
Util.retExec(pCallBack),
|
||||
load
|
||||
Util.loadOnLoad([
|
||||
load,
|
||||
Util.retExec(pCallBack)
|
||||
]);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,159 +1,161 @@
|
|||
var CloudCmd, Util, DOM, $, Github, cb;
|
||||
/* module for work with github */
|
||||
var CloudCmd, Util, DOM, CloudFunc, Github, cb;
|
||||
|
||||
(function(CloudCmd, Util, DOM){
|
||||
(function(CloudCmd, Util, DOM, CloudFunc) {
|
||||
'use strict';
|
||||
|
||||
var Storage = DOM.Storage,
|
||||
GithubLocal,
|
||||
User,
|
||||
GitHubStore = {};
|
||||
CloudCmd.GitHub = GitHubProto;
|
||||
|
||||
/* temporary callback function for work with github */
|
||||
cb = function (err, data){ Util.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();
|
||||
function GitHubProto(callback) {
|
||||
var GitHub = this,
|
||||
Storage = DOM.Storage,
|
||||
|
||||
Util.exec(pCallBack);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
GitHubStore.autorize = function(pCallBack, pCode){
|
||||
var lToken = Storage.get('token');
|
||||
if(lToken){
|
||||
GitHubStore.Login(lToken);
|
||||
Util.exec(pCallBack);
|
||||
}
|
||||
else{
|
||||
var lCode = pCode || window.location.search;
|
||||
if (lCode || Util.isContainStr(lCode, '?code=') )
|
||||
CloudCmd.getConfig(function(pConfig){
|
||||
DOM.ajax({
|
||||
type : 'put',
|
||||
url : pConfig && pConfig.api_url + '/auth',
|
||||
data : Util.removeStr(lCode, '?code='),
|
||||
success : function(pData){
|
||||
if(pData && pData.token){
|
||||
lToken = pData.token;
|
||||
|
||||
GitHubStore.Login(lToken);
|
||||
Storage.set('token', lToken);
|
||||
Util.exec(pCallBack);
|
||||
}
|
||||
else
|
||||
Util.log('Worning: token not getted...');
|
||||
}
|
||||
});
|
||||
});
|
||||
else{
|
||||
var lUrl = '//' + window.location.host + '/auth/github';
|
||||
DOM.openWindow(lUrl);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
GitHubStore.getUserData = function(pCallBack){
|
||||
User.show(null, function(pError, pData){
|
||||
if(!pError){
|
||||
var lName = pData.name;
|
||||
Util.log('Hello ' + lName + ' :)!');
|
||||
}
|
||||
else
|
||||
DOM.Storage.remove('token');
|
||||
});
|
||||
GH,
|
||||
User;
|
||||
|
||||
Util.exec(pCallBack);
|
||||
};
|
||||
|
||||
/* PUBLIC 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'
|
||||
});
|
||||
cb = function (err, data) { Util.log(err || data);};
|
||||
|
||||
User = GithubLocal.getUser();
|
||||
};
|
||||
|
||||
/**
|
||||
* function creates gist
|
||||
*/
|
||||
GitHubStore.uploadFile = function(pParams, pCallBack){
|
||||
var lContent = pParams.data,
|
||||
lName = pParams.name;
|
||||
|
||||
if(lContent){
|
||||
DOM.Images.showLoad();
|
||||
if(!lName)
|
||||
lName = Util.getDate();
|
||||
function init(pCallBack) {
|
||||
Util.loadOnLoad([
|
||||
load,
|
||||
GitHub.autorize,
|
||||
GitHub.getUserData,
|
||||
Util.retExec(pCallBack)
|
||||
]);
|
||||
|
||||
var lGist = GithubLocal.getGist(),
|
||||
lFiles = {},
|
||||
lHost = CloudCmd.HOST,
|
||||
lOptions = {
|
||||
description: 'Uplouded by Cloud Commander from ' + lHost,
|
||||
public: true
|
||||
};
|
||||
|
||||
lFiles[lName] ={
|
||||
content: lContent
|
||||
GitHub.callback = function() {
|
||||
Util.loadOnLoad([
|
||||
GitHub.getUserData,
|
||||
Util.retExec(pCallBack)
|
||||
]);
|
||||
};
|
||||
}
|
||||
|
||||
function load(callback) {
|
||||
var dir = CloudCmd.LIBDIRCLIENT + 'storage/github/',
|
||||
files = [
|
||||
dir + 'lib/underscore.js',
|
||||
dir + 'lib/base64.js',
|
||||
dir + 'github.js'
|
||||
|
||||
],
|
||||
url = CloudFunc.getJoinURL(files);
|
||||
|
||||
lOptions.files = lFiles;
|
||||
Util.time('github');
|
||||
|
||||
lGist.create(lOptions, function(pError, pData){
|
||||
DOM.jsload(url, function() {
|
||||
Util.timeEnd('github');
|
||||
DOM.Images.hideLoad();
|
||||
Util.log(pError || pData);
|
||||
Util.log(pData && pData.html_url);
|
||||
|
||||
Util.exec(pCallBack);
|
||||
Util.exec(callback);
|
||||
});
|
||||
}
|
||||
|
||||
return lContent;
|
||||
};
|
||||
|
||||
GitHubStore.init = function(pCallBack){
|
||||
Util.loadOnLoad([
|
||||
Util.retExec(pCallBack),
|
||||
GitHubStore.getUserData,
|
||||
GitHubStore.autorize,
|
||||
load
|
||||
]);
|
||||
|
||||
GitHubStore.callback = function(){
|
||||
Util.loadOnLoad([
|
||||
Util.retExec(pCallBack),
|
||||
GitHubStore.getUserData,
|
||||
]);
|
||||
GitHub.autorize = function(pCallBack, pCode) {
|
||||
var lCode, lToken = Storage.get('token');
|
||||
|
||||
if (lToken) {
|
||||
GitHub.Login(lToken);
|
||||
Util.exec(pCallBack);
|
||||
}
|
||||
else {
|
||||
lCode = pCode || window.location.search;
|
||||
|
||||
if (lCode || Util.isContainStr(lCode, '?code=') )
|
||||
CloudCmd.getConfig(function(pConfig) {
|
||||
DOM.ajax({
|
||||
type : 'put',
|
||||
url : pConfig && pConfig.apiURL + '/auth',
|
||||
data : Util.removeStr(lCode, '?code='),
|
||||
success : function(pData) {
|
||||
if (pData && pData.token) {
|
||||
lToken = pData.token;
|
||||
|
||||
GitHub.Login(lToken);
|
||||
Storage.set('token', lToken);
|
||||
Util.exec(pCallBack);
|
||||
}
|
||||
else
|
||||
Util.log('Worning: token not getted...');
|
||||
}
|
||||
});
|
||||
});
|
||||
else{
|
||||
var lUrl = '//' + window.location.host + '/auth/github';
|
||||
DOM.openWindow(lUrl);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
CloudCmd.GitHub = GitHubStore;
|
||||
})(CloudCmd, Util, DOM);
|
||||
|
||||
GitHub.getUserData = function(pCallBack) {
|
||||
User.show(null, function(pError, pData) {
|
||||
if (!pError) {
|
||||
var lName = pData.name;
|
||||
Util.log('Hello ' + lName + ' :)!');
|
||||
}
|
||||
else
|
||||
DOM.Storage.remove('token');
|
||||
});
|
||||
|
||||
Util.exec(pCallBack);
|
||||
};
|
||||
|
||||
/* PUBLIC FUNCTIONS */
|
||||
GitHub.basicLogin = function(pUser, pPasswd) {
|
||||
GH = new Github({
|
||||
username: pUser,
|
||||
password: pPasswd,
|
||||
auth : 'basic'
|
||||
});
|
||||
};
|
||||
|
||||
GitHub.Login = function(pToken) {
|
||||
GH = new Github({
|
||||
token : pToken,
|
||||
auth : 'oauth'
|
||||
});
|
||||
|
||||
User = GH.getUser();
|
||||
};
|
||||
|
||||
/**
|
||||
* function creates gist
|
||||
*/
|
||||
GitHub.uploadFile = function(pParams, pCallBack) {
|
||||
var lContent = pParams.data,
|
||||
lName = pParams.name;
|
||||
|
||||
if (lContent) {
|
||||
DOM.Images.showLoad();
|
||||
if (!lName)
|
||||
lName = Util.getDate();
|
||||
|
||||
var lGist = GH.getGist(),
|
||||
lFiles = {},
|
||||
lHost = CloudCmd.HOST,
|
||||
lOptions = {
|
||||
description: 'Uplouded by Cloud Commander from ' + lHost,
|
||||
public: true
|
||||
};
|
||||
|
||||
lFiles[lName] ={
|
||||
content: lContent
|
||||
};
|
||||
|
||||
lOptions.files = lFiles;
|
||||
|
||||
lGist.create(lOptions, function(pError, pData) {
|
||||
DOM.Images.hideLoad();
|
||||
Util.log(pError || pData);
|
||||
Util.log(pData && pData.html_url);
|
||||
|
||||
Util.exec(pCallBack);
|
||||
});
|
||||
}
|
||||
|
||||
return lContent;
|
||||
};
|
||||
|
||||
init(callback);
|
||||
}
|
||||
})(CloudCmd, Util, DOM, CloudFunc);
|
||||
|
|
|
|||
|
|
@ -66,10 +66,10 @@ var CloudCmd, Util, DOM, WL;
|
|||
}
|
||||
|
||||
SkyDrive.init = function(pCallBack){
|
||||
Util.loadOnLoad([
|
||||
Util.retExec(pCallBack),
|
||||
Util.loadOnLoad([
|
||||
load,
|
||||
auth,
|
||||
load
|
||||
Util.retExec(pCallBack)
|
||||
]);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -86,10 +86,10 @@ var CloudCmd, Util, DOM, VK;
|
|||
|
||||
|
||||
VKStorage.init = function(pCallBack){
|
||||
Util.loadOnLoad([
|
||||
Util.retExec(pCallBack),
|
||||
Util.loadOnLoad([
|
||||
load,
|
||||
auth,
|
||||
load
|
||||
Util.retExec(pCallBack)
|
||||
]);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -78,5 +78,3 @@ var Base64 = (function () {
|
|||
|
||||
return obj;
|
||||
})();
|
||||
|
||||
window.Base64 = Base64
|
||||
|
|
|
|||
191
lib/client/terminal.js
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
var CloudCmd, Util, DOM, CloudFunc, Terminal;
|
||||
(function(CloudCmd, Util, DOM, CloudFunc) {
|
||||
'use strict';
|
||||
|
||||
CloudCmd.Terminal = TerminalProto;
|
||||
|
||||
function TerminalProto(CallBack) {
|
||||
var Name = 'Terminal',
|
||||
Loading,
|
||||
Element,
|
||||
MouseBinded,
|
||||
Term,
|
||||
Cell,
|
||||
Socket = CloudCmd.Socket,
|
||||
Key = CloudCmd.Key,
|
||||
ESC = Key.ESC,
|
||||
Images = DOM.Images,
|
||||
Notify = DOM.Notify,
|
||||
|
||||
Size = {
|
||||
cols: 0,
|
||||
rows: 0
|
||||
},
|
||||
|
||||
CHANNEL = CloudFunc.CHANNEL_TERMINAL,
|
||||
CHANNEL_RESIZE = CloudFunc.CHANNEL_TERMINAL_RESIZE,
|
||||
|
||||
CloudTerm = this;
|
||||
|
||||
function init() {
|
||||
Loading = true;
|
||||
|
||||
Util.loadOnLoad([
|
||||
DOM.jqueryLoad,
|
||||
CloudCmd.View,
|
||||
load,
|
||||
CloudCmd.Socket,
|
||||
/* rm view keys, it ruin terminal */
|
||||
function(callback) {
|
||||
CloudCmd.View.rmKeys(),
|
||||
Socket = CloudCmd.Socket;
|
||||
|
||||
Util.exec(callback);
|
||||
},
|
||||
CloudTerm.show,
|
||||
addListeners
|
||||
]);
|
||||
}
|
||||
|
||||
CloudTerm.show = show;
|
||||
CloudTerm.write = write;
|
||||
|
||||
function show(callback) {
|
||||
var options = {
|
||||
onUpdate: onResize,
|
||||
};
|
||||
|
||||
if (!Loading) {
|
||||
Images.showLoad({top:true});
|
||||
|
||||
if (!Element) {
|
||||
Element = DOM.anyload({
|
||||
name : 'div',
|
||||
id : 'terminal',
|
||||
style : 'height :100%'
|
||||
});
|
||||
|
||||
/* hack to determine console size
|
||||
* inspired with
|
||||
*
|
||||
* https://github.com/petethepig/devtools-terminal
|
||||
*/
|
||||
Cell = DOM.anyload({
|
||||
name : 'div',
|
||||
inner : ' ',
|
||||
parent : Element,
|
||||
style : 'position: absolute;' +
|
||||
'top : -1000px;'
|
||||
});
|
||||
|
||||
DOM.cssSet({
|
||||
id : 'terminal-css',
|
||||
inner : '#terminal, .terminal, #view {' +
|
||||
'height' + ': 100%;' +
|
||||
'}' +
|
||||
'.terminal-cursor {' +
|
||||
'background' + ': gray' +
|
||||
'}'
|
||||
});
|
||||
|
||||
Term = new Terminal({
|
||||
screenKeys: true,
|
||||
cursorBlink: false,
|
||||
});
|
||||
|
||||
Term.open(Element);
|
||||
}
|
||||
|
||||
CloudCmd.View.show(Element, function() {
|
||||
Element.focus();
|
||||
Terminal.brokenBold = true;
|
||||
|
||||
Util.exec(callback);
|
||||
}, options);
|
||||
}
|
||||
}
|
||||
|
||||
function write(data) {
|
||||
Term.write(data);
|
||||
}
|
||||
|
||||
function addListeners(callback) {
|
||||
var options = {
|
||||
'connect': function() {
|
||||
write(Socket.CONNECTED + '\r');
|
||||
},
|
||||
'disconnect': function() {
|
||||
write(Socket.DISCONNECTED +'\r');
|
||||
},
|
||||
};
|
||||
|
||||
options[CHANNEL] = write;
|
||||
options[CHANNEL_RESIZE] = function(size) {
|
||||
Term.resize(size.cols, size.rows);
|
||||
};
|
||||
|
||||
Socket.on(options);
|
||||
|
||||
Term.on('data', function(data) {
|
||||
Socket.emit(CHANNEL, data);
|
||||
});
|
||||
|
||||
Term.on('resize', function(size) {
|
||||
Socket.emit(CHANNEL_RESIZE, size);
|
||||
});
|
||||
|
||||
Util.exec(callback);
|
||||
}
|
||||
|
||||
function getSize() {
|
||||
var wSubs = Element.offsetWidth - Element.clientWidth,
|
||||
w = Element.clientWidth - wSubs,
|
||||
|
||||
hSubs = Element.offsetHeight - Element.clientHeight,
|
||||
h = Element.clientHeight - hSubs,
|
||||
|
||||
x = Cell.clientWidth,
|
||||
y = Cell.clientHeight,
|
||||
|
||||
cols = Math.max(Math.floor(w / x), 10),
|
||||
rows = Math.max(Math.floor(h / y), 10),
|
||||
|
||||
size = {
|
||||
cols: cols,
|
||||
rows: rows
|
||||
};
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
function onResize() {
|
||||
var size = getSize(),
|
||||
cols = size.cols,
|
||||
rows = size.rows;
|
||||
|
||||
if (Size.cols !== cols || Size.rows !== rows) {
|
||||
Size = size;
|
||||
|
||||
//Term.resize(size.cols, size.rows);
|
||||
Term.emit('resize', size);
|
||||
}
|
||||
}
|
||||
|
||||
function load(pCallBack) {
|
||||
var dir = CloudCmd.LIBDIRCLIENT + 'terminal/',
|
||||
path = dir + 'term.js';
|
||||
|
||||
Util.time(Name + ' load');
|
||||
|
||||
DOM.jsload(path, function() {
|
||||
Util.timeEnd(Name + ' load');
|
||||
Loading = false;
|
||||
|
||||
Util.exec(pCallBack);
|
||||
});
|
||||
}
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
})(CloudCmd, Util, DOM, CloudFunc);
|
||||
5866
lib/client/terminal/term.js
Normal file
|
|
@ -1,6 +1,6 @@
|
|||
var CloudCmd, Util, DOM, CloudFunc, $;
|
||||
|
||||
(function(CloudCmd, Util, DOM, CloudFunc){
|
||||
(function(CloudCmd, Util, DOM, CloudFunc) {
|
||||
'use strict';
|
||||
|
||||
CloudCmd.View = ViewProto;
|
||||
|
|
@ -8,133 +8,126 @@ var CloudCmd, Util, DOM, CloudFunc, $;
|
|||
function ViewProto(CallBack) {
|
||||
var Name = 'View',
|
||||
Loading = false,
|
||||
Events = DOM.Events,
|
||||
Info = DOM.CurrentInfo,
|
||||
Key = CloudCmd.Key,
|
||||
Images = DOM.Images,
|
||||
View = this,
|
||||
View = Util.exec.bind(Util),
|
||||
Element,
|
||||
|
||||
Config = {
|
||||
beforeShow : function(){
|
||||
beforeShow : function() {
|
||||
Images.hideLoad();
|
||||
Key.unsetBind();
|
||||
},
|
||||
afterShow : function(){
|
||||
Element.focus();
|
||||
},
|
||||
|
||||
beforeClose : Key.setBind,
|
||||
|
||||
loop : false,
|
||||
openEffect : 'none',
|
||||
closeEffect : 'none',
|
||||
autoSize : false,
|
||||
height : window.innerHeight,
|
||||
width : window.innerWidth/0.75,
|
||||
height : '100%',
|
||||
width : '100%',
|
||||
minWidth : 0,
|
||||
minHeight : 0,
|
||||
|
||||
helpers : {
|
||||
overlay : {
|
||||
css : {
|
||||
'background' : 'rgba(255, 255, 255, 0.1)'
|
||||
}
|
||||
}
|
||||
},
|
||||
padding : 0
|
||||
};
|
||||
|
||||
View.show = show;
|
||||
View.hide = hide;
|
||||
View.rmKeys = rmKeys;
|
||||
|
||||
function rmKeys() {
|
||||
/* remove default key binding
|
||||
* which is ruin terminal
|
||||
*/
|
||||
$.fancybox.defaults.keys = null;
|
||||
}
|
||||
|
||||
function init() {
|
||||
var lFunc, lIsFunc, lIsCallBack;
|
||||
var func = CallBack || Util.bind(show, null);
|
||||
|
||||
Loading = true;
|
||||
|
||||
if (CallBack){
|
||||
lIsFunc = Util.isFunction(CallBack);
|
||||
lIsCallBack = Util.isFunction(CallBack.callback);
|
||||
}
|
||||
|
||||
if (lIsFunc)
|
||||
lFunc = CallBack;
|
||||
else if (lIsCallBack)
|
||||
lFunc = CallBack.callback;
|
||||
|
||||
Util.loadOnLoad([
|
||||
lFunc || Util.retExec(View.show, null),
|
||||
DOM.jqueryLoad,
|
||||
load,
|
||||
DOM.jqueryLoad
|
||||
func
|
||||
]);
|
||||
|
||||
DOM.Events.addKey(listener);
|
||||
DOM.setButtonKey('f3', view);
|
||||
Events.addKey(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* function shows FancyBox
|
||||
*/
|
||||
this.show = function(pData, pCallBack, pConfig) {
|
||||
var lPath, lElement, lAfterFunc, lFunc, name,
|
||||
function show(data, callback, newConfig) {
|
||||
var path, element, func, name, isImage,
|
||||
config = {};
|
||||
|
||||
if (!Loading) {
|
||||
Element = $('<div id=view tabindex=0>');
|
||||
if (pData) {
|
||||
lElement = $(Element).append(pData);
|
||||
lAfterFunc = Config.afterShow,
|
||||
lFunc = function(){
|
||||
Util.exec(lAfterFunc);
|
||||
Util.exec(pCallBack);
|
||||
};
|
||||
|
||||
if (data) {
|
||||
element = $(Element).append(data);
|
||||
func = Util.retExec(callback);
|
||||
|
||||
Config.afterShow = lFunc;
|
||||
Config.afterShow = func;
|
||||
|
||||
Util.copyObj(Config, config);
|
||||
Util.copyObj(config, Config);
|
||||
|
||||
for (name in pConfig)
|
||||
config[name] = pConfig[name];
|
||||
for (name in newConfig)
|
||||
config[name] = newConfig[name];
|
||||
|
||||
$.fancybox(lElement, config);
|
||||
$.fancybox(element, config);
|
||||
|
||||
} else {
|
||||
lPath = CloudFunc.FS + DOM.getCurrentPath();
|
||||
if( Util.checkExtension(lPath, ['png','jpg', 'gif','ico']) ) {
|
||||
$.fancybox.open({ href : lPath }, Config);
|
||||
}
|
||||
else
|
||||
DOM.getCurrentData(function(pParams){
|
||||
var data = document.createTextNode(pParams.data);
|
||||
$.fancybox( Element.append(data), Config );
|
||||
path = CloudFunc.FS + Info.path;
|
||||
isImage = $.fancybox.isImage(path);
|
||||
|
||||
if (isImage) {
|
||||
$.fancybox.open({ href : path }, Config);
|
||||
} else
|
||||
Info.getData(function(data) {
|
||||
var element = document.createTextNode(data);
|
||||
/* add margin only for view text documents */
|
||||
Element.css('margin', '2%');
|
||||
|
||||
$.fancybox(Element.append(element), Config);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
this.hide = function() {
|
||||
function hide() {
|
||||
$.fancybox.close();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* function loads css and js of FancyBox
|
||||
* @pParent - this
|
||||
* @pCallBack - executes, when everything loaded
|
||||
* @callback - executes, when everything loaded
|
||||
*/
|
||||
function load(pCallBack) {
|
||||
Util.time(Name + ' load');
|
||||
var lDir = CloudCmd.LIBDIRCLIENT + 'view/fancyBox/source/',
|
||||
lFiles = [ lDir + 'jquery.fancybox.css',
|
||||
lDir + 'jquery.fancybox.js' ];
|
||||
function load(callback) {
|
||||
var dir = CloudCmd.LIBDIRCLIENT + 'view/fancyBox/source/',
|
||||
files = [
|
||||
dir + 'jquery.fancybox.css',
|
||||
dir + 'jquery.fancybox.js'
|
||||
];
|
||||
|
||||
DOM.anyLoadOnLoad([lFiles], function(){
|
||||
Util.time(Name + ' load');
|
||||
|
||||
DOM.anyLoadOnLoad([files], function() {
|
||||
Util.timeEnd(Name + ' load');
|
||||
Loading = false;
|
||||
Util.exec( pCallBack );
|
||||
Util.exec(callback);
|
||||
Images.hideLoad();
|
||||
})
|
||||
.cssSet({id:'view-css',
|
||||
inner : '#view{' +
|
||||
inner : '#view {' +
|
||||
'font-size: 16px;' +
|
||||
'white-space :pre;' +
|
||||
'outline: 0;' +
|
||||
'}' +
|
||||
'#view::selection{' +
|
||||
'#view::selection {' +
|
||||
/*
|
||||
'background: #fe57a1;'
|
||||
'color: #fff;'
|
||||
|
|
@ -146,6 +139,9 @@ var CloudCmd, Util, DOM, CloudFunc, $;
|
|||
'background: none;' +
|
||||
'width: 0;' +
|
||||
'height: 0' +
|
||||
'}' +
|
||||
'.fancybox-overlay {' +
|
||||
'background: rgba(255, 255, 255, 0.1)' +
|
||||
'}'
|
||||
});
|
||||
|
||||
|
|
@ -156,19 +152,18 @@ var CloudCmd, Util, DOM, CloudFunc, $;
|
|||
View.show();
|
||||
}
|
||||
|
||||
function listener(pEvent) {
|
||||
var lF3 = Key.F3,
|
||||
lIsBind = Key.isBind(),
|
||||
lKey = pEvent.keyCode;
|
||||
function listener(event) {
|
||||
var keyCode = event.keyCode,
|
||||
ESC = Key.ESC;
|
||||
|
||||
/* если клавиши можно обрабатывать */
|
||||
if (lIsBind && lKey === lF3) {
|
||||
view();
|
||||
DOM.preventDefault(pEvent);
|
||||
}
|
||||
if (keyCode === ESC)
|
||||
hide();
|
||||
}
|
||||
|
||||
|
||||
init();
|
||||
|
||||
return View;
|
||||
}
|
||||
|
||||
})(CloudCmd, Util, DOM, CloudFunc);
|
||||
|
|
|
|||
277
lib/cloudfunc.js
|
|
@ -1,6 +1,6 @@
|
|||
var Util;
|
||||
|
||||
(function(scope) {
|
||||
(function(scope, Util) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
|
|
@ -11,7 +11,7 @@ var Util;
|
|||
if (scope.window) {
|
||||
scope.CloudFunc = new CloudFuncProto(Util);
|
||||
} else {
|
||||
if(!global.cloudcmd)
|
||||
if (!global.cloudcmd)
|
||||
return console.log(
|
||||
'# cloudfunc.js' + '\n' +
|
||||
'# -----------' + '\n' +
|
||||
|
|
@ -26,53 +26,96 @@ var Util;
|
|||
}
|
||||
|
||||
|
||||
function CloudFuncProto(Util) {
|
||||
function CloudFuncProto(Util) {
|
||||
var CloudFunc = this,
|
||||
FS,
|
||||
COMBINE = '/combine/';
|
||||
|
||||
/* Путь с которым мы сейчас работаем */
|
||||
this.Path = '';
|
||||
JOIN = '/join/';
|
||||
|
||||
/* КОНСТАНТЫ (общие для клиента и сервера)*/
|
||||
|
||||
/* название программы */
|
||||
this.NAME = 'Cloud Commander';
|
||||
this.NAME = 'Cloud Commander';
|
||||
|
||||
/* если в ссылке будет эта строка - в браузере js отключен */
|
||||
this.FS = FS = '/fs';
|
||||
this.FS = FS = '/fs';
|
||||
|
||||
/* название css-класа кнопки обновления файловой структуры*/
|
||||
this.REFRESHICON = 'refresh-icon';
|
||||
this.REFRESHICON = 'refresh-icon';
|
||||
|
||||
/* id панелей с файлами */
|
||||
this.LEFTPANEL = 'left';
|
||||
this.RIGHTPANEL = 'right';
|
||||
this.PANEL_LEFT = 'js-left';
|
||||
this.PANEL_RIGHT = 'js-right';
|
||||
this.CHANNEL_CONSOLE = 'console-data';
|
||||
this.CHANNEL_TERMINAL = 'terminal-data';
|
||||
this.CHANNEL_TERMINAL_RESIZE= 'terminal-resize';
|
||||
|
||||
this.getCombineURL = function(names) {
|
||||
var url,
|
||||
nameStr = names + '';
|
||||
this.addListener = function(name, func, allListeners, socket) {
|
||||
var listeners, obj;
|
||||
|
||||
nameStr = Util.replaceStr(nameStr, ',', ':');
|
||||
url = COMBINE + nameStr;
|
||||
if (Util.isString(name)) {
|
||||
listeners = allListeners[name];
|
||||
|
||||
if (!listeners)
|
||||
listeners = allListeners[name] = [];
|
||||
|
||||
listeners.push(func);
|
||||
|
||||
if (func && socket)
|
||||
socket.on(name, func);
|
||||
|
||||
} else if (Util.isObject(name)) {
|
||||
obj = name;
|
||||
|
||||
for (name in obj) {
|
||||
func = obj[name];
|
||||
CloudFunc.addListener(name, func, allListeners, socket);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.removeListener = function(name, func, allListeners, socket) {
|
||||
var i, n, listeners;
|
||||
|
||||
if (socket)
|
||||
socket.removeListener(name, func);
|
||||
|
||||
listeners = allListeners[name];
|
||||
|
||||
if (listeners) {
|
||||
n = listeners.length;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
if (listeners[i] === func)
|
||||
listeners[i] = null;
|
||||
}
|
||||
};
|
||||
|
||||
this.getJoinURL = function(names) {
|
||||
var url, isContain,
|
||||
regExp = new RegExp(',', 'g'),
|
||||
nameStr = names + '';
|
||||
|
||||
nameStr = nameStr.replace(regExp, ':');
|
||||
nameStr = this.rmFirstSlash(nameStr);
|
||||
url = JOIN + nameStr;
|
||||
|
||||
return url;
|
||||
};
|
||||
|
||||
this.getCombineArray = function(url) {
|
||||
var str = Util.removeStrOneTime(url, COMBINE),
|
||||
this.getJoinArray = function(url) {
|
||||
var str = Util.removeStrOneTime(url, JOIN),
|
||||
names = str.split(':');
|
||||
|
||||
return names;
|
||||
};
|
||||
|
||||
this.isCombineURL = function(url) {
|
||||
var ret = Util.isContainStrAtBegin(url, COMBINE);
|
||||
this.isJoinURL = function(url) {
|
||||
var ret = Util.isContainStrAtBegin(url, JOIN);
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
this.formatMsg = function(pMsg, pName, pStatus) {
|
||||
this.formatMsg = function(pMsg, pName, pStatus) {
|
||||
var status = pStatus || 'ok',
|
||||
name = !pName ? '': '("' + pName + '")',
|
||||
msg = pMsg + ': ' + status + name;
|
||||
|
|
@ -83,23 +126,33 @@ var Util;
|
|||
* Функция убирает последний слеш,
|
||||
* если он - последний символ строки
|
||||
*/
|
||||
this.removeLastSlash = function(pPath) {
|
||||
this.rmLastSlash = function(pPath) {
|
||||
var lRet = pPath,
|
||||
lIsStr = typeof pPath==='string',
|
||||
lIsStr = Util.isString(pPath),
|
||||
lLengh = pPath.length-1,
|
||||
lLastSlash = pPath.lastIndexOf('/');
|
||||
|
||||
if(lIsStr && lLastSlash === lLengh)
|
||||
if (lIsStr && lLastSlash === lLengh)
|
||||
lRet = pPath.substr(pPath, lLengh);
|
||||
|
||||
return lRet;
|
||||
};
|
||||
};
|
||||
|
||||
this.rmFirstSlash = function(str) {
|
||||
var ret = str,
|
||||
isContain = Util.isContainStrAtBegin(str, '/');
|
||||
|
||||
if (isContain)
|
||||
ret = Util.removeStrOneTime(str, '/');
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
/** Функция возвращает заголовок веб страницы
|
||||
* @pPath
|
||||
*/
|
||||
this.getTitle = function(pPath) {
|
||||
if(!CloudFunc.Path)
|
||||
this.getTitle = function(pPath) {
|
||||
if (!CloudFunc.Path)
|
||||
CloudFunc.Path = '/';
|
||||
|
||||
return CloudFunc.NAME + ' - ' + (pPath || CloudFunc.Path);
|
||||
|
|
@ -110,7 +163,7 @@ var Util;
|
|||
* @param pPerm_s - строка с правами доступа
|
||||
* к файлу в 8-миричной системе
|
||||
*/
|
||||
this.getSymbolicPermissions = function(pPerm_s) {
|
||||
this.getSymbolicPermissions = function(pPerm_s) {
|
||||
var lType, lOwner, lGroup, lAll,
|
||||
perms = pPerm_s && pPerm_s.toString(),
|
||||
lPermissions = perms;
|
||||
|
|
@ -160,23 +213,23 @@ var Util;
|
|||
of using an application-specific shorthand).
|
||||
*/
|
||||
/* Переводим в двоичную систему */
|
||||
lOwner = ( perms[0] - 0 ).toString(2),
|
||||
lGroup = ( perms[1] - 0 ).toString(2),
|
||||
lAll = ( perms[2] - 0 ).toString(2),
|
||||
lOwner = (perms[0] - 0).toString(2),
|
||||
lGroup = (perms[1] - 0).toString(2),
|
||||
lAll = (perms[2] - 0).toString(2),
|
||||
|
||||
/* переводим в символьную систему*/
|
||||
lPermissions =
|
||||
( lOwner[0] - 0 > 0 ? 'r' : '-' ) +
|
||||
( lOwner[1] - 0 > 0 ? 'w' : '-' ) +
|
||||
( lOwner[2] - 0 > 0 ? 'x' : '-' ) +
|
||||
(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' : '-' ) +
|
||||
(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' : '-' );
|
||||
(lAll[0]- 0 > 0 ? 'r' : '-') +
|
||||
(lAll[1]- 0 > 0 ? 'w' : '-') +
|
||||
(lAll[2]- 0 > 0 ? 'x' : '-');
|
||||
}
|
||||
|
||||
return lPermissions;
|
||||
|
|
@ -216,7 +269,7 @@ var Util;
|
|||
* гигайбайты и терабайты
|
||||
* @pSize - размер в байтах
|
||||
*/
|
||||
this.getShortSize = function(pSize) {
|
||||
this.getShortSize = function(pSize) {
|
||||
if (pSize === pSize-0) {
|
||||
/* Константы размеров, что используются внутри функции */
|
||||
var l1KB = 1024,
|
||||
|
|
@ -235,47 +288,12 @@ var Util;
|
|||
|
||||
return pSize;
|
||||
};
|
||||
|
||||
/** Функция парсит uid и имена пользователей
|
||||
* из переданного в строке вычитаного файла /etc/passwd
|
||||
* и возвращает массив обьектов имён и uid пользователей
|
||||
* @pPasswd_s - строка, в которой находиться файл /etc/passwd
|
||||
*/
|
||||
this.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 = Util.removeStr(pPasswd_s, lLine);
|
||||
|
||||
/* получаем первое слово строки */
|
||||
var lName = lLine.substr(lLine,lLine.indexOf(':'));
|
||||
lLine = Util.removeStr(lLine, 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 - адрес каталога
|
||||
*/
|
||||
this._getDirPath = function(url) {
|
||||
function getDirPath(url) {
|
||||
var lShortName,
|
||||
folders = [],
|
||||
i;
|
||||
|
|
@ -283,10 +301,10 @@ var Util;
|
|||
do {
|
||||
folders.push(url);
|
||||
url = url.substr(url, url.lastIndexOf('/'));
|
||||
} while(url !== '');
|
||||
} while (url !== '');
|
||||
|
||||
/* Формируем ссылки на каждый каталог в пути */
|
||||
var lHref = '<a class=links href="',
|
||||
var lHref = '<a href="',
|
||||
lTitle = '" title="',
|
||||
_l = '">',
|
||||
lHrefEnd ='</a>',
|
||||
|
|
@ -296,13 +314,13 @@ var Util;
|
|||
'/' + _l + '/' +
|
||||
lHrefEnd;
|
||||
|
||||
for(i = folders.length - 1; i > 0; i--) {
|
||||
for (i = folders.length - 1; i > 0; i--) {
|
||||
var lUrl = folders[i],
|
||||
lSlashIndex = lUrl.lastIndexOf('/') + 1;
|
||||
|
||||
lShortName = Util.removeStr(lUrl, lUrl.substr(lUrl, lSlashIndex) );
|
||||
lShortName = Util.removeStr(lUrl, lUrl.substr(lUrl, lSlashIndex));
|
||||
|
||||
if(i !== 1)
|
||||
if (i !== 1)
|
||||
lHtmlPath += lHref + FS + lUrl +
|
||||
lTitle + lUrl + _l +
|
||||
lShortName + lHrefEnd + '/';
|
||||
|
|
@ -311,40 +329,42 @@ var Util;
|
|||
}
|
||||
|
||||
return lHtmlPath;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Функция строит таблицу файлв из JSON-информации о файлах
|
||||
* @param pJSON - информация о файлах
|
||||
* @param pKeyBinded - если клавиши назначены, выделяем верхний файл
|
||||
* [{path:'путь',size:'dir'},
|
||||
* @param json - информация о файлах
|
||||
*
|
||||
* {name:'имя',size:'размер',mode:'права доступа'}]
|
||||
*/
|
||||
this.buildFromJSON = function(pJSON, pTemplate, pPathTemplate, pLinkTemplate) {
|
||||
var lFile, i, n, type, link, target, size, owner, mode,
|
||||
this.buildFromJSON = function(json, template, pathTemplate, linkTemplate) {
|
||||
var file, i, n, type, target, size, owner, mode,
|
||||
/* ссылка на верхний каталог*/
|
||||
dotDot, link,
|
||||
linkResult,
|
||||
files = pJSON.files,
|
||||
files = json.files,
|
||||
/* сохраняем путь каталога в котором мы сейчас находимся*/
|
||||
lPath = pJSON.path,
|
||||
path = json.path,
|
||||
|
||||
/*
|
||||
* Строим путь каталога в котором мы находимся
|
||||
* со всеми подкаталогами
|
||||
*/
|
||||
lHtmlPath = CloudFunc._getDirPath(lPath),
|
||||
htmlPath = getDirPath(path),
|
||||
|
||||
/* Убираем последний слэш
|
||||
* с пути для кнопки обновить страницу
|
||||
* если он есть
|
||||
*/
|
||||
lRefreshPath = CloudFunc.removeLastSlash(lPath),
|
||||
refreshPath = CloudFunc.rmLastSlash(path),
|
||||
|
||||
lFileTable = Util.render(pPathTemplate, {
|
||||
link: FS + lRefreshPath,
|
||||
path: lHtmlPath
|
||||
fileTable = Util.render(pathTemplate, {
|
||||
link : FS + refreshPath,
|
||||
fullPath : path,
|
||||
path : htmlPath
|
||||
}),
|
||||
|
||||
lHeader = Util.render(pTemplate, {
|
||||
header = Util.render(template, {
|
||||
className : 'fm-header',
|
||||
type : '',
|
||||
name : 'name',
|
||||
|
|
@ -353,34 +373,34 @@ var Util;
|
|||
mode : 'mode'
|
||||
});
|
||||
|
||||
lHeader = Util.replaceStr(lHeader, 'li', 'div');
|
||||
lFileTable += lHeader;
|
||||
header = Util.replaceStr(header, 'li', 'div');
|
||||
header = Util.removeStrOneTime(header, 'draggable ');
|
||||
|
||||
fileTable += header;
|
||||
|
||||
/* сохраняем путь */
|
||||
CloudFunc.Path = lPath;
|
||||
CloudFunc.Path = path;
|
||||
|
||||
lFileTable += '<ul class="files">';
|
||||
fileTable += '<ul class="files">';
|
||||
/* Если мы не в корне */
|
||||
if (lPath !== '/') {
|
||||
/* ссылка на верхний каталог*/
|
||||
var lDotDot, lLink;
|
||||
if (path !== '/') {
|
||||
/* убираем последний слеш и каталог в котором мы сейчас находимся*/
|
||||
lDotDot = lPath.substr(lPath, lPath.lastIndexOf('/'));
|
||||
lDotDot = lDotDot.substr(lDotDot, lDotDot.lastIndexOf('/'));
|
||||
dotDot = path.substr(path, path.lastIndexOf('/'));
|
||||
dotDot = dotDot.substr(dotDot, dotDot.lastIndexOf('/'));
|
||||
/* Если предыдущий каталог корневой */
|
||||
if(lDotDot === '')
|
||||
lDotDot = '/';
|
||||
if (dotDot === '')
|
||||
dotDot = '/';
|
||||
|
||||
lLink = FS + lDotDot;
|
||||
link = FS + dotDot;
|
||||
|
||||
linkResult = Util.render(pLinkTemplate, {
|
||||
link : lLink,
|
||||
linkResult = Util.render(linkTemplate, {
|
||||
link : link,
|
||||
name : '..',
|
||||
target : ''
|
||||
});
|
||||
|
||||
/* Сохраняем путь к каталогу верхнего уровня*/
|
||||
lFileTable += Util.render(pTemplate,{
|
||||
fileTable += Util.render(template,{
|
||||
className : '',
|
||||
type : 'directory',
|
||||
name : linkResult,
|
||||
|
|
@ -392,23 +412,30 @@ var Util;
|
|||
|
||||
n = files.length;
|
||||
for (i = 0; i < n; i++) {
|
||||
lFile = files[i];
|
||||
type = lFile.size === 'dir' ? 'directory' : 'text-file';
|
||||
link = FS + lPath + lFile.name;
|
||||
target = lFile.size === 'dir' ? '' : "_blank";
|
||||
size = lFile.size === 'dir' ? '<dir>' : CloudFunc.getShortSize( lFile.size );
|
||||
owner = !lFile.uid ? 'root' : lFile.uid;
|
||||
mode = CloudFunc.getSymbolicPermissions(lFile.mode);
|
||||
file = files[i];
|
||||
link = FS + path + file.name;
|
||||
|
||||
if (file.size === 'dir') {
|
||||
type = 'directory';
|
||||
target = '';
|
||||
size = '<dir>';
|
||||
} else {
|
||||
type = 'text-file';
|
||||
target = '_blank';
|
||||
size = CloudFunc.getShortSize(file.size);
|
||||
}
|
||||
|
||||
linkResult = Util.render(pLinkTemplate, {
|
||||
owner = file.owner || 'root';
|
||||
mode = CloudFunc.getSymbolicPermissions(file.mode);
|
||||
|
||||
linkResult = Util.render(linkTemplate, {
|
||||
link : link,
|
||||
name : lFile.name,
|
||||
name : file.name,
|
||||
target : target
|
||||
});
|
||||
|
||||
|
||||
lFileTable += Util.render(pTemplate,{
|
||||
fileTable += Util.render(template,{
|
||||
className : '',
|
||||
/* Если папка - выводим пиктограмму папки *
|
||||
* В противоположном случае - файла */
|
||||
|
|
@ -420,9 +447,9 @@ var Util;
|
|||
});
|
||||
}
|
||||
|
||||
lFileTable += '</ul>';
|
||||
fileTable += '</ul>';
|
||||
|
||||
return lFileTable;
|
||||
return fileTable;
|
||||
};
|
||||
}
|
||||
})(this);
|
||||
})(this, Util);
|
||||
|
|
|
|||
|
|
@ -16,9 +16,8 @@
|
|||
var patches = dmp.patch_fromText(patch),
|
||||
result = dmp.patch_apply(patches, oldText),
|
||||
newText = result[0];
|
||||
|
||||
|
||||
return newText;
|
||||
};
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
|
|
|||
243
lib/server.js
|
|
@ -23,13 +23,15 @@
|
|||
CloudFunc = main.cloudfunc,
|
||||
AppCache = main.appcache,
|
||||
Socket = main.socket,
|
||||
Console = main.console,
|
||||
Terminal = main.terminal,
|
||||
|
||||
zlib = main.zlib,
|
||||
http = main.http,
|
||||
https = main.https,
|
||||
Util = main.util,
|
||||
express = main.express,
|
||||
expressApp = express.getApp(controller),
|
||||
expressApp,
|
||||
files = main.files,
|
||||
|
||||
Server, Rest, Route;
|
||||
|
|
@ -49,80 +51,97 @@
|
|||
* @param pConfig
|
||||
* @param pProcessing {index, appcache, rest}
|
||||
*/
|
||||
function start(pProcessing) {
|
||||
var lConfig = main.config;
|
||||
function start(options) {
|
||||
var redirectServer,
|
||||
config = main.config;
|
||||
|
||||
if (!pProcessing)
|
||||
pProcessing = {};
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
Rest = pProcessing.rest;
|
||||
Route = pProcessing.route;
|
||||
Rest = options.rest;
|
||||
Route = options.route;
|
||||
|
||||
init(pProcessing.appcache);
|
||||
init(options.appcache);
|
||||
|
||||
var lPort = process.env.PORT || /* c9 */
|
||||
process.env.app_port || /* nodester */
|
||||
process.env.VCAP_APP_PORT || /* cloudfoundry */
|
||||
lConfig.port,
|
||||
config.port,
|
||||
|
||||
lIP = process.env.IP || /* c9 */
|
||||
lConfig.ip ||
|
||||
config.ip ||
|
||||
(main.WIN32 ? '127.0.0.1' : '0.0.0.0'),
|
||||
|
||||
lSSL = pProcessing.ssl,
|
||||
lSSLPort = lConfig.sslPort,
|
||||
|
||||
lSSL = options.ssl,
|
||||
lSSLPort = config.sslPort,
|
||||
lHTTP = 'http://',
|
||||
lHTTPS = 'https://',
|
||||
|
||||
lSockets = function(pServer) {
|
||||
var lListen, msg, status;
|
||||
var listen, msg,
|
||||
status = 'off';
|
||||
|
||||
if (lConfig.socket && Socket)
|
||||
lListen = Socket.listen(pServer);
|
||||
if (config.socket && Socket) {
|
||||
listen = Socket.listen(pServer);
|
||||
|
||||
if (listen) {
|
||||
status = 'on';
|
||||
Console.init();
|
||||
Terminal.init();
|
||||
}
|
||||
}
|
||||
|
||||
status = lListen ? 'on' : 'off';
|
||||
msg = CloudFunc.formatMsg('sockets', '', status);
|
||||
|
||||
Util.log(msg);
|
||||
},
|
||||
|
||||
lHTTPServer = function() {
|
||||
Server = http.createServer(expressApp || controller);
|
||||
Server.on('error', Util.log);
|
||||
expressApp = express.getApp([
|
||||
Rest,
|
||||
Route,
|
||||
join,
|
||||
controller
|
||||
]);
|
||||
|
||||
Server = http.createServer(expressApp || respond);
|
||||
|
||||
Server.on('error', Util.log.bind(Util));
|
||||
Server.listen(lPort, lIP);
|
||||
lServerLog(lHTTP, lPort);
|
||||
lSockets(Server);
|
||||
},
|
||||
|
||||
lServerLog = function(pHTTP, pPort) {
|
||||
Util.log('* Server running at ' + pHTTP + lIP + ':' + pPort);
|
||||
lServerLog = function(http, port) {
|
||||
Util.log('* Server running at ' + http + lIP + ':' + port);
|
||||
};
|
||||
/* server mode or testing mode */
|
||||
if (lConfig.server) {
|
||||
if (config.server)
|
||||
if (lSSL) {
|
||||
Util.log('* Redirection http -> https is setted up');
|
||||
lServerLog(lHTTP, lPort);
|
||||
var lRedirectServer = http.createServer( function(pReq, pRes) {
|
||||
var lURL,
|
||||
lHost = pReq.headers.host,
|
||||
lParsed = URL.parse(lHost),
|
||||
lHostName = lParsed.protocol;
|
||||
|
||||
redirectServer = http.createServer(function(req, res) {
|
||||
var url,
|
||||
host = req.headers.host,
|
||||
parsed = url.parse(host),
|
||||
hostName = parsed.protocol;
|
||||
|
||||
lURL = lHTTPS + lHostName + lSSLPort + pReq.url;
|
||||
url = lHTTPS + hostName + lSSLPort + req.url;
|
||||
|
||||
main.redirect({
|
||||
response: pRes,
|
||||
url: lURL
|
||||
response: res,
|
||||
url: url
|
||||
});
|
||||
});
|
||||
|
||||
lRedirectServer.listen(lPort, lIP);
|
||||
redirectServer.listen(lPort, lIP);
|
||||
|
||||
Server = https.createServer(lSSL, controller);
|
||||
Server.on('error', function (pError) {
|
||||
Server = https.createServer(lSSL, respond);
|
||||
Server.on('error', function (error) {
|
||||
Util.log('Could not use https port: ' + lSSLPort);
|
||||
Util.log(pError);
|
||||
lRedirectServer.close();
|
||||
Util.log(error);
|
||||
redirectServer.close();
|
||||
Util.log('* Redirection http -> https removed');
|
||||
lHTTPServer();
|
||||
});
|
||||
|
|
@ -133,10 +152,28 @@
|
|||
lServerLog(lHTTPS, lSSLPort);
|
||||
} else
|
||||
lHTTPServer();
|
||||
} else
|
||||
else
|
||||
Util.log('Cloud Commander testing mode');
|
||||
}
|
||||
|
||||
function respond(req, res) {
|
||||
var i, n, func,
|
||||
funcs = ([
|
||||
Rest,
|
||||
Route,
|
||||
join,
|
||||
controller
|
||||
]);
|
||||
|
||||
n = funcs.length;
|
||||
for (i = 0; i < n; i++) {
|
||||
func = funcs[i];
|
||||
funcs[i] = func.bind(null, req, res);
|
||||
}
|
||||
|
||||
Util.loadOnLoad(funcs);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Главная функция, через которую проихсодит
|
||||
|
|
@ -144,96 +181,72 @@
|
|||
* @param req - запрос клиента (Request)
|
||||
* @param res - ответ сервера (Response)
|
||||
*/
|
||||
function controller(pReq, pRes) {
|
||||
/* Читаем содержимое папки, переданное в url */
|
||||
var lRet, lName, lMin, lCheck, lResult,
|
||||
lConfig = main.config,
|
||||
lParsedUrl = URL.parse(pReq.url),
|
||||
lQuery = lParsedUrl.search || '',
|
||||
lPath = lParsedUrl.pathname;
|
||||
|
||||
/* added supporting of Russian language in directory names */
|
||||
lPath = Querystring.unescape(lPath);
|
||||
function controller(req, res) {
|
||||
var check, result,
|
||||
config = main.config,
|
||||
isMin = config.minify,
|
||||
parsedUrl = URL.parse(req.url),
|
||||
query = parsedUrl.search || '',
|
||||
path = main.getPathName(req),
|
||||
name = path;
|
||||
|
||||
if (!expressApp)
|
||||
Util.log(pReq.method + ' ' + lPath + lQuery);
|
||||
Util.log(req.method + ' ' + path + query);
|
||||
|
||||
var lData = {
|
||||
name : lPath,
|
||||
request : pReq,
|
||||
response : pRes
|
||||
};
|
||||
/* watching is file changed */
|
||||
if (config.appcache)
|
||||
AppCache.watch(name);
|
||||
|
||||
if (lConfig.rest )
|
||||
lRet = Util.exec(Rest, lData);
|
||||
name = Path.join(DIR, name);
|
||||
check = checkExtension(name);
|
||||
result = isMin && check;
|
||||
|
||||
if (!lRet && Route)
|
||||
lRet = Util.exec(Route, lData);
|
||||
|
||||
if (!lRet)
|
||||
lRet = combine(lData);
|
||||
|
||||
if (!lRet) {
|
||||
lName = lData.name;
|
||||
|
||||
/* watching is file changed */
|
||||
if (lConfig.appcache)
|
||||
AppCache.watch(lName);
|
||||
|
||||
lName = Path.join(DIR, lName);
|
||||
lMin = lConfig.minify,
|
||||
lCheck = checkExtension(lName);
|
||||
lResult = lMin && lCheck;
|
||||
|
||||
Util.ifExec(!lResult,
|
||||
function(pParams) {
|
||||
var lSendName = pParams && pParams.name || lName;
|
||||
|
||||
main.sendFile({
|
||||
name : lSendName,
|
||||
cache : lConfig.cache,
|
||||
gzip : true,
|
||||
request : pReq,
|
||||
response : pRes
|
||||
});
|
||||
}, function(pCallBack) {
|
||||
Minify.optimize(lName, {
|
||||
callback : pCallBack,
|
||||
returnName : true
|
||||
Util.ifExec(!result,
|
||||
function(params) {
|
||||
var sendName = params && params.name || name;
|
||||
|
||||
main.sendFile({
|
||||
name : sendName,
|
||||
cache : config.cache,
|
||||
gzip : true,
|
||||
request : req,
|
||||
response : res
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function minify(name) {
|
||||
return function(callback) {
|
||||
}, function(callback) {
|
||||
Minify.optimize(name, {
|
||||
callback : callback
|
||||
callback : callback,
|
||||
returnName : true
|
||||
});
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function combine(params) {
|
||||
function minify(name, callback) {
|
||||
Minify.optimize(name, {
|
||||
callback : callback
|
||||
});
|
||||
}
|
||||
|
||||
function join(request, response, callback) {
|
||||
var names, i, n, name, minName, stream, check,
|
||||
funcs = [],
|
||||
config = main.config,
|
||||
dir = DIR,
|
||||
gzip = zlib.createGzip(),
|
||||
p = params,
|
||||
isGzip = main.isGZIP(p.request),
|
||||
path = params.name,
|
||||
isCombine = CloudFunc.isCombineURL(path),
|
||||
isGzip = main.isGZIP(request),
|
||||
path = main.getPathName(request),
|
||||
|
||||
isJoin = CloudFunc.isJoinURL(path),
|
||||
readPipe = function() {
|
||||
main.mainSetHeader({
|
||||
name : names[0],
|
||||
cache : config.cache,
|
||||
gzip : isGzip,
|
||||
request : p.request,
|
||||
response : p.response
|
||||
request : request,
|
||||
response : response
|
||||
});
|
||||
|
||||
if (!isGzip)
|
||||
stream = p.response;
|
||||
stream = response;
|
||||
else
|
||||
stream = gzip;
|
||||
|
||||
|
|
@ -244,15 +257,18 @@
|
|||
callback : function(error) {
|
||||
var errorStr;
|
||||
|
||||
if (error) {
|
||||
if (!p.response.headersSent)
|
||||
main.sendError(params, error);
|
||||
if (error)
|
||||
if (!response.headersSent)
|
||||
main.sendError({
|
||||
request : request,
|
||||
response : response,
|
||||
name : path
|
||||
}, error);
|
||||
else {
|
||||
Util.log(error);
|
||||
errorStr = error.toString();
|
||||
stream.end(errorStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -261,11 +277,11 @@
|
|||
* readPipe called with stream param
|
||||
*/
|
||||
if (isGzip)
|
||||
gzip.pipe(p.response);
|
||||
gzip.pipe(response);
|
||||
};
|
||||
|
||||
if (isCombine) {
|
||||
names = CloudFunc.getCombineArray(path);
|
||||
if (isJoin) {
|
||||
names = CloudFunc.getJoinArray(path);
|
||||
n = names.length;
|
||||
|
||||
if (!config.minify)
|
||||
|
|
@ -284,13 +300,14 @@
|
|||
}
|
||||
}
|
||||
|
||||
funcs.push(minify(name));
|
||||
funcs.push(minify.bind(null, name));
|
||||
}
|
||||
Util.asyncCall(funcs, readPipe);
|
||||
}
|
||||
}
|
||||
} else
|
||||
Util.exec(callback);
|
||||
|
||||
return isCombine;
|
||||
return isJoin;
|
||||
}
|
||||
|
||||
function checkExtension(name) {
|
||||
|
|
|
|||
|
|
@ -13,20 +13,24 @@
|
|||
var main = global.cloudcmd.main,
|
||||
fs = main.fs,
|
||||
Util = main.util,
|
||||
users = main.users,
|
||||
|
||||
WIN32 = main.WIN32,
|
||||
|
||||
checkParams = main.checkCallBackParams;
|
||||
|
||||
exports.getDirContent = function(pPath, pCallBack) {
|
||||
var lRet = Util.isString(pPath);
|
||||
exports.getDirContent = function(path, callback) {
|
||||
var ret = Util.isString(path);
|
||||
|
||||
if (lRet)
|
||||
fs.readdir(pPath, Util.call(readDir, {
|
||||
callback: pCallBack,
|
||||
path : pPath
|
||||
}));
|
||||
if (!ret)
|
||||
Util.exec(callback, "First parameter should be a string");
|
||||
else
|
||||
Util.exec(pCallBack, "First parameter should be a string");
|
||||
fs.readdir(path, readDir.bind(null, {
|
||||
callback : callback,
|
||||
path : path
|
||||
}));
|
||||
|
||||
return lRet;
|
||||
return ret;
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -35,84 +39,78 @@
|
|||
* @param pError
|
||||
* @param pFiles
|
||||
*/
|
||||
function readDir(pParams) {
|
||||
var lRet = checkParams(pParams);
|
||||
lRet = lRet && Util.checkObj(pParams.params, ['path']);
|
||||
function readDir(params, error, files) {
|
||||
var i, n, stats, filesData, fill, name, fileParams,
|
||||
p = params,
|
||||
dirPath = getDirPath(p.path);
|
||||
|
||||
if (lRet) {
|
||||
var p = pParams,
|
||||
c = pParams.params,
|
||||
lFiles = p.data,
|
||||
lDirPath = getDirPath(c.path);
|
||||
if (error)
|
||||
Util.exec(p.callback, error.toString());
|
||||
else {
|
||||
/* Получаем информацию о файлах */
|
||||
n = files.length,
|
||||
stats = {},
|
||||
|
||||
if (!p.error && lFiles) {
|
||||
lFiles.data = lFiles.sort();
|
||||
|
||||
/* Получаем информацию о файлах */
|
||||
var n = lFiles.length,
|
||||
lStats = {},
|
||||
lFilesData = {
|
||||
files : lFiles,
|
||||
stats : lStats,
|
||||
callback : c.callback,
|
||||
path : c.path
|
||||
},
|
||||
filesData = {
|
||||
files : files,
|
||||
stats : stats,
|
||||
callback : p.callback,
|
||||
path : p.path
|
||||
},
|
||||
|
||||
fill = fillJSON.bind(null, filesData);
|
||||
|
||||
if (n)
|
||||
for (i = 0; i < n; i++) {
|
||||
name = dirPath + files[i],
|
||||
|
||||
lFill = Util.retFunc(fillJSON, lFilesData);
|
||||
|
||||
if (n)
|
||||
for (var i = 0; i < n; i++) {
|
||||
var lName = lDirPath + lFiles[i],
|
||||
lParams = {
|
||||
callback : lFill,
|
||||
count : n,
|
||||
name : lFiles[i],
|
||||
stats : lStats,
|
||||
};
|
||||
|
||||
fs.stat(lName, Util.call(getFilesStat, lParams));
|
||||
}
|
||||
else
|
||||
fillJSON(lFilesData);
|
||||
} else
|
||||
Util.exec(c.callback, p.error.toString());
|
||||
fileParams = {
|
||||
callback : fill,
|
||||
count : n,
|
||||
name : files[i],
|
||||
stats : stats,
|
||||
};
|
||||
|
||||
fs.stat(name, onStat.bind(null, fileParams));
|
||||
}
|
||||
else
|
||||
fillJSON(filesData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* async getting file states
|
||||
* and putting it to lStats object
|
||||
* and putting it to stats object
|
||||
*/
|
||||
function getFilesStat(pParams) {
|
||||
var lRet = checkParams(pParams);
|
||||
lRet = lRet && Util.checkObjTrue(pParams.params,
|
||||
['callback', 'stats', 'name', 'count']);
|
||||
function onStat(params, error, stat) {
|
||||
var n, keys, p = params;
|
||||
|
||||
if (lRet) {
|
||||
var p = pParams,
|
||||
c = p.params;
|
||||
|
||||
c.stats[c.name] = !p.error ? p.data : {
|
||||
if (!error)
|
||||
p.stats[p.name] = stat;
|
||||
else
|
||||
p.stats[p.name] = {
|
||||
'mode' : 0,
|
||||
'size' : 0,
|
||||
'isDirectory' : Util.retFalse
|
||||
};
|
||||
|
||||
if (c.count === Object.keys(c.stats).length)
|
||||
Util.exec(c.callback);
|
||||
}
|
||||
|
||||
keys = Object.keys(p.stats);
|
||||
n = keys.length;
|
||||
|
||||
if (p.count === n)
|
||||
Util.exec(p.callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function fill JSON by file stats
|
||||
*
|
||||
* @param pStats - object, contain file stats.
|
||||
* @param stats - object, contain file stats.
|
||||
* example {'1.txt': stat}
|
||||
*
|
||||
* @param pFiles - array of files of current directory
|
||||
* @param files - array of files of current directory
|
||||
*/
|
||||
function fillJSON(pParams) {
|
||||
var name, stat, mode, isDir, size, uid, modeStr,
|
||||
var name, stat, mode, isDir, size, owner, modeStr,
|
||||
p, i, n, file, path, json, files,
|
||||
ret = Util.checkObjTrue(pParams, ['files', 'stats', 'path']);
|
||||
|
||||
|
|
@ -132,7 +130,7 @@
|
|||
for (i = 0; i < n; i++ ) {
|
||||
name = p.files[i];
|
||||
stat = p.stats[name];
|
||||
uid = stat.uid;
|
||||
owner = stat.uid;
|
||||
|
||||
if (stat) {
|
||||
/* Переводим права доступа в 8-ричную систему */
|
||||
|
|
@ -145,7 +143,7 @@
|
|||
file = {
|
||||
'name' : name,
|
||||
'size' : size,
|
||||
'uid' : uid,
|
||||
'owner' : owner,
|
||||
'mode' : mode
|
||||
};
|
||||
|
||||
|
|
@ -154,10 +152,37 @@
|
|||
|
||||
json.files = changeOrder(files);
|
||||
|
||||
Util.exec(p.callback, null, json);
|
||||
changeUIDToName(json, function(json) {
|
||||
Util.exec(p.callback, null, json);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function changeUIDToName(json, callback) {
|
||||
Util.ifExec(WIN32, callback, function() {
|
||||
users.getNames(function(error, names) {
|
||||
var i, n, current, owner,
|
||||
files = json.files;
|
||||
|
||||
Util.log(error);
|
||||
|
||||
n = files.length;
|
||||
for (i = 0; i < n; i++) {
|
||||
current = files[i];
|
||||
|
||||
owner = current.owner;
|
||||
owner = names[owner];
|
||||
|
||||
if (owner)
|
||||
current.owner = owner;
|
||||
}
|
||||
|
||||
Util.exec(callback, json);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function changeOrder(json) {
|
||||
var file, i, n,
|
||||
files = [],
|
||||
|
|
|
|||
321
lib/server/console.js
Normal file
|
|
@ -0,0 +1,321 @@
|
|||
(function() {
|
||||
'use strict';
|
||||
|
||||
var main = global.cloudcmd.main,
|
||||
DIR = main.DIR,
|
||||
SRVDIR = main.SRVDIR,
|
||||
|
||||
socket = main.socket,
|
||||
update = main.srvrequire('update'),
|
||||
|
||||
exec = main.child_process.exec,
|
||||
spawn = main.child_process.spawn,
|
||||
|
||||
Util = main.util,
|
||||
path = main.path,
|
||||
CloudFunc = main.cloudfunc,
|
||||
mainpackage = main.mainpackage,
|
||||
equalPart = Util.isContainStrAtBegin,
|
||||
CLOUDCMD = mainpackage.name,
|
||||
ClientFuncs = [],
|
||||
ClientDirs = [],
|
||||
Clients = [],
|
||||
WIN32 = main.WIN32,
|
||||
ConNum = 0,
|
||||
|
||||
CHANNEL = CloudFunc.CHANNEL_CONSOLE,
|
||||
|
||||
HELP = {
|
||||
stdout : CLOUDCMD + ' exit \n' +
|
||||
CLOUDCMD + ' update \n',
|
||||
},
|
||||
|
||||
/* windows commands thet require
|
||||
* unicode charset on locales
|
||||
* different then English
|
||||
*/
|
||||
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'];
|
||||
|
||||
/**
|
||||
* function listen on servers port
|
||||
* @pServer {Object} started server object
|
||||
*/
|
||||
exports.init = function() {
|
||||
var ret;
|
||||
|
||||
ret = socket.on('connection', function(clientSocket) {
|
||||
onConnection(clientSocket, function(json, all) {
|
||||
socket.emit(CHANNEL, json, clientSocket, all);
|
||||
});
|
||||
});
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
function onConnection(clientSocket, callback) {
|
||||
var msg, onDisconnect, onMessage;
|
||||
|
||||
++ConNum;
|
||||
|
||||
if (!Clients[ConNum]) {
|
||||
msg = log(ConNum, 'console connected\n');
|
||||
|
||||
Util.exec(callback, {
|
||||
stdout : msg
|
||||
});
|
||||
|
||||
Clients[ConNum] = true;
|
||||
|
||||
onMessage = getOnMessage(ConNum, callback);
|
||||
onDisconnect = function(conNum) {
|
||||
Clients[conNum] =
|
||||
ClientFuncs[conNum] = null;
|
||||
|
||||
log(conNum, 'console disconnected');
|
||||
|
||||
socket.removeListener(CHANNEL, onMessage, clientSocket);
|
||||
socket.removeListener('disconnect', onDisconnect, clientSocket);
|
||||
}.bind(null, ConNum);
|
||||
|
||||
socket.on(CHANNEL, onMessage, clientSocket);
|
||||
socket.on('disconnect', onDisconnect, clientSocket);
|
||||
} else {
|
||||
msg = log(ConNum, ' in use. Reconnecting...\n');
|
||||
|
||||
Util.exec(callback, {
|
||||
stdout: msg
|
||||
});
|
||||
|
||||
socket.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* function gets onMessage function
|
||||
* that execute needed command
|
||||
*
|
||||
* @param pConnNum
|
||||
* @param callback
|
||||
*/
|
||||
function getOnMessage(pConnNum, callback) {
|
||||
return function(pCommand) {
|
||||
var lWinCommand, lExec_func, firstChar,
|
||||
connName,
|
||||
lError, lRet, lExecSymbols, isContain,
|
||||
dir, options = {};
|
||||
|
||||
dir = ClientDirs[pConnNum];
|
||||
|
||||
if (!dir)
|
||||
dir = ClientDirs[pConnNum] = DIR;
|
||||
|
||||
connName = '#' + pConnNum + ': ';
|
||||
Util.log(connName + pCommand);
|
||||
|
||||
if (equalPart(pCommand, CLOUDCMD))
|
||||
lRet = onCloudCmd(pCommand, callback);
|
||||
else if (equalPart(pCommand, 'cd ')) {
|
||||
lRet = true;
|
||||
|
||||
onCD(pCommand, dir, function(json) {
|
||||
var error = json.stderr,
|
||||
stdout = json.stdout;
|
||||
|
||||
if (error)
|
||||
Util.exec(callback, json);
|
||||
else
|
||||
ClientDirs[pConnNum] = stdout;
|
||||
});
|
||||
}
|
||||
|
||||
if (!lRet) {
|
||||
/* if we on windows and command is build in
|
||||
* change code page to unicode becouse
|
||||
* windows use unicode on non English versions
|
||||
*/
|
||||
if (WIN32) {
|
||||
lWinCommand = pCommand.toUpperCase();
|
||||
|
||||
if (Win32Commands.indexOf(lWinCommand) >= 0)
|
||||
pCommand = 'chcp 65001 |' + pCommand;
|
||||
}
|
||||
|
||||
if (!ClientFuncs[pConnNum])
|
||||
ClientFuncs[pConnNum] = getExec(function(json, pError, pStderr) {
|
||||
log(pConnNum, pError, 'error');
|
||||
log(pConnNum, pStderr, 'stderror');
|
||||
|
||||
Util.exec(callback, json);
|
||||
});
|
||||
|
||||
lExec_func = ClientFuncs[pConnNum];
|
||||
lExecSymbols = ['*', '&', '{', '}', '|', '\'', '"'];
|
||||
isContain = Util.isContainStr(pCommand, lExecSymbols);
|
||||
firstChar = pCommand[0];
|
||||
options.cwd = dir;
|
||||
|
||||
if (firstChar === '#') {
|
||||
pCommand = pCommand.slice(1);
|
||||
pCommand = connName + pCommand;
|
||||
pCommand = Util.addNewLine(pCommand);
|
||||
|
||||
Util.exec(callback, {
|
||||
stdout: pCommand
|
||||
}, true);
|
||||
} else if (WIN32 || firstChar === ' ' || isContain)
|
||||
exec(pCommand, options, lExec_func);
|
||||
else
|
||||
getSpawn(pCommand, options, callback);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* function send result of command to client
|
||||
* @param callback
|
||||
*/
|
||||
function getExec(callback) {
|
||||
return function(pError, pStdout, pStderr) {
|
||||
var lErrorStr, lExecStr, lExec,
|
||||
lError = pStderr || pError;
|
||||
|
||||
if (lError) {
|
||||
if (Util.isString(lError))
|
||||
lErrorStr = lError;
|
||||
else
|
||||
lErrorStr = lError.toString();
|
||||
|
||||
lErrorStr = Util.addNewLine(lErrorStr);
|
||||
}
|
||||
|
||||
lExec = {
|
||||
stdout : pStdout,
|
||||
stderr : lErrorStr || lError
|
||||
};
|
||||
|
||||
Util.exec(callback, lExec, pError, pStderr);
|
||||
};
|
||||
}
|
||||
|
||||
function getSpawn(pCommand, options, callback) {
|
||||
var send, cmd, error,
|
||||
args = pCommand.split(' ');
|
||||
|
||||
pCommand = args.shift();
|
||||
|
||||
error = Util.tryCatchLog(function() {
|
||||
cmd = spawn(pCommand, args, options);
|
||||
});
|
||||
|
||||
if (!cmd)
|
||||
send(error + '', null);
|
||||
else {
|
||||
send = function(error, data) {
|
||||
var exec = {
|
||||
stderr: error,
|
||||
stdout: data
|
||||
};
|
||||
|
||||
Util.exec(callback, exec);
|
||||
};
|
||||
|
||||
cmd.stdout.on('data', function(data) {
|
||||
send(null, data + '');
|
||||
});
|
||||
|
||||
cmd.stderr.on('data', function(error) {
|
||||
send(error + '', null);
|
||||
});
|
||||
|
||||
cmd.on('error', Util.retFalse);
|
||||
cmd.on('close', function (code) {
|
||||
cmd = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function onCloudCmd(pCommand, callback) {
|
||||
var lRet;
|
||||
|
||||
pCommand = Util.removeStr(pCommand, CLOUDCMD);
|
||||
|
||||
if (!equalPart(pCommand, ' ')) {
|
||||
lRet = true;
|
||||
Util.exec(callback, HELP);
|
||||
}
|
||||
else {
|
||||
pCommand = Util.removeStr(pCommand, ' ');
|
||||
|
||||
if (equalPart(pCommand, 'update') && update) {
|
||||
lRet = true;
|
||||
update.get();
|
||||
|
||||
Util.exec(callback, {
|
||||
stdout: Util.addNewLine('update: ok')
|
||||
});
|
||||
}
|
||||
|
||||
if (Util.strCmp(pCommand, 'exit'))
|
||||
process.exit();
|
||||
}
|
||||
|
||||
return lRet;
|
||||
}
|
||||
|
||||
function onCD(pCommand, currDir, callback) {
|
||||
var dir,
|
||||
getDir = WIN32 ? 'chdir' : 'pwd',
|
||||
paramDir = Util.removeStr(pCommand, 'cd ');
|
||||
|
||||
if (equalPart(paramDir, ['/', '~']))
|
||||
dir = paramDir;
|
||||
else
|
||||
dir = path.join(currDir, paramDir);
|
||||
|
||||
exec('cd ' + dir + ' && ' + getDir, function (error, stdout, stderr) {
|
||||
var lRet,
|
||||
lMsg = '',
|
||||
lError = error || stderr;
|
||||
|
||||
if (lError) {
|
||||
lError = Util.stringifyJSON(lError);
|
||||
lMsg = lError;
|
||||
}
|
||||
|
||||
Util.exec(callback, {
|
||||
stderr : lMsg,
|
||||
stdout : Util.rmNewLine(stdout)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function log(pConnNum, pStr, pType) {
|
||||
var lRet,
|
||||
lType = ' ';
|
||||
|
||||
if (pStr) {
|
||||
|
||||
if (pType)
|
||||
lType += pType + ':';
|
||||
|
||||
lRet = 'client #' + pConnNum + lType + pStr;
|
||||
|
||||
Util.log(lRet);
|
||||
}
|
||||
|
||||
return lRet;
|
||||
}
|
||||
})();
|
||||
|
|
@ -19,85 +19,74 @@
|
|||
Util = main.util,
|
||||
path = main.path;
|
||||
|
||||
exports.getSize = function(pDir, pCallBack) {
|
||||
var lTotal = 0;
|
||||
exports.getSize = function(dir, callback) {
|
||||
var total = 0;
|
||||
|
||||
function calcSize(pParams){
|
||||
var lStat = pParams.stat,
|
||||
lSize = lStat && lStat.size || 0;
|
||||
function calcSize(stat) {
|
||||
var size = stat && stat.size || 0;
|
||||
|
||||
lTotal += lSize;
|
||||
total += size;
|
||||
}
|
||||
|
||||
processDir(pDir, calcSize, function() {
|
||||
Util.exec(pCallBack, null, lTotal);
|
||||
processDir(dir, calcSize, function() {
|
||||
Util.exec(callback, null, total);
|
||||
});
|
||||
};
|
||||
|
||||
function processDir(pDir, pFunc, pCallBack){
|
||||
var lAsyncRunning = 0,
|
||||
lFileCounter = 1;
|
||||
function processDir(dir, func, callback) {
|
||||
var asyncRunning = 0,
|
||||
fileCounter = 1;
|
||||
|
||||
function getDirInfo(pDir) {
|
||||
function getDirInfo(dir) {
|
||||
/* The lstat() function shall be equivalent to stat(),
|
||||
except when path refers to a symbolic link. In that case lstat()
|
||||
shall return information about the link, while stat() shall return
|
||||
information about the file the link references. */
|
||||
information about the file the link references.
|
||||
*/
|
||||
|
||||
fs.lstat(pDir, Util.call(getStat, {
|
||||
name: pDir
|
||||
}));
|
||||
fs.lstat(dir, getStat.bind(null, dir));
|
||||
}
|
||||
|
||||
function getStat(pParams) {
|
||||
var lRet = Util.checkObj(pParams, ['params']);
|
||||
if(lRet){
|
||||
var p = pParams,
|
||||
d = p.params,
|
||||
lStat = p.data,
|
||||
lPath = d.name;
|
||||
|
||||
--lFileCounter;
|
||||
|
||||
if (!p.error) {
|
||||
if ( lStat.isFile() )
|
||||
Util.exec(pFunc, {
|
||||
name: d.name,
|
||||
stat: lStat
|
||||
});
|
||||
else if ( lStat.isDirectory() ) {
|
||||
++lAsyncRunning;
|
||||
function getStat(dir, error, stat) {
|
||||
--fileCounter;
|
||||
|
||||
if (!error) {
|
||||
if (stat.isFile())
|
||||
Util.exec(func, stat);
|
||||
else if (stat.isDirectory()) {
|
||||
++asyncRunning;
|
||||
|
||||
fs.readdir(dir, function(error, files) {
|
||||
var dirPath, file, n, i;
|
||||
|
||||
fs.readdir(lPath, function(pError, pFiles) {
|
||||
lAsyncRunning--;
|
||||
asyncRunning--;
|
||||
|
||||
if (!error) {
|
||||
n = files.length;
|
||||
fileCounter += n;
|
||||
|
||||
var lDirPath, n;
|
||||
|
||||
if (!pError){
|
||||
n = pFiles.length;
|
||||
lFileCounter += n;
|
||||
for (i = 0; i < n; i++) {
|
||||
file = files[i];
|
||||
dirPath = path.join(dir, file);
|
||||
|
||||
for (var i = 0; i < n; i++) {
|
||||
lDirPath = path.join(lPath, pFiles[i]);
|
||||
process.nextTick(Util.retFunc(getDirInfo, lDirPath));
|
||||
}
|
||||
process.nextTick(getDirInfo.bind(null, dirPath));
|
||||
}
|
||||
|
||||
if(!n)
|
||||
execCallBack();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if(!n)
|
||||
execCallBack();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
execCallBack();
|
||||
}
|
||||
|
||||
function execCallBack(){
|
||||
if (!lFileCounter && !lAsyncRunning)
|
||||
Util.exec(pCallBack);
|
||||
function execCallBack() {
|
||||
if (!fileCounter && !asyncRunning)
|
||||
Util.exec(callback);
|
||||
}
|
||||
|
||||
getDirInfo(pDir);
|
||||
getDirInfo(dir);
|
||||
}
|
||||
})();
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -11,13 +11,72 @@
|
|||
|
||||
var main = global.cloudcmd.main,
|
||||
express = main.require('express'),
|
||||
httpAuth = main.require('http-auth'),
|
||||
crypto = main.crypto,
|
||||
basic,
|
||||
oldPass,
|
||||
oldName,
|
||||
app = express && express();
|
||||
|
||||
exports.getApp = function(controller) {
|
||||
if (app)
|
||||
app.use(express.logger('dev'))
|
||||
.all('*', controller);
|
||||
exports.getApp = function(middleware) {
|
||||
var i, n, middle,
|
||||
config = main.config,
|
||||
auth = config.auth;
|
||||
|
||||
if (app) {
|
||||
app.use(express.logger('dev'));
|
||||
|
||||
if (auth && httpAuth) {
|
||||
initAuth();
|
||||
app.use(httpAuth.connect(basic));
|
||||
}
|
||||
|
||||
if (middleware) {
|
||||
n = middleware.length;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
middle = middleware[i];
|
||||
app.use(middle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return app;
|
||||
};
|
||||
|
||||
|
||||
function initAuth() {
|
||||
basic = httpAuth.basic({
|
||||
realm: "Cloud Commander"
|
||||
}, function (username, password, callback) { // Custom authentication method.
|
||||
var hash,
|
||||
config = main.config,
|
||||
name = config.username,
|
||||
passwd = config.password,
|
||||
equal = username === name,
|
||||
sha = crypto.createHash('sha1');
|
||||
|
||||
if (!oldPass)
|
||||
oldPass = passwd;
|
||||
|
||||
if (!oldName)
|
||||
oldName = name;
|
||||
|
||||
if (!equal)
|
||||
username === oldName;
|
||||
|
||||
sha.update(password);
|
||||
hash = sha.digest('hex');
|
||||
equal = passwd === hash && equal;
|
||||
|
||||
if (!equal) {
|
||||
sha = crypto.createHash('sha1');
|
||||
sha.update(oldPass);
|
||||
hash = sha.digest('hex');
|
||||
equal = passwd === hash && equal;
|
||||
}
|
||||
|
||||
callback(equal);
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -16,60 +16,50 @@
|
|||
CloudFunc = main.cloudfunc,
|
||||
Util = main.util;
|
||||
|
||||
object.read = function(pFiles, pOptions, pCallBack) {
|
||||
var lDone = [],
|
||||
lFiles,
|
||||
lErrors,
|
||||
object.read = function(files, options, callback) {
|
||||
var done = [],
|
||||
errors,
|
||||
i, n,
|
||||
lName,
|
||||
lReadedFiles = {},
|
||||
lDoneFunc = function (pParams) {
|
||||
var msg, status, p, lName,
|
||||
lRet = Util.checkObj(pParams, ['error', 'data', 'params']);
|
||||
name,
|
||||
readFiles = {},
|
||||
doneFunc = function (name, error, data) {
|
||||
var msg, status;
|
||||
|
||||
if (lRet) {
|
||||
lDone.pop();
|
||||
p = pParams,
|
||||
lName = p.params;
|
||||
done.pop();
|
||||
|
||||
if (error) {
|
||||
status = 'error';
|
||||
|
||||
if (p.error) {
|
||||
status = 'error';
|
||||
|
||||
if (!lErrors)
|
||||
lErrors = {};
|
||||
|
||||
lErrors[lName] = p.error;
|
||||
}
|
||||
else {
|
||||
status = 'ok';
|
||||
lReadedFiles[lName] = p.data;
|
||||
}
|
||||
if (!errors)
|
||||
errors = {};
|
||||
|
||||
lName = path.basename(lName);
|
||||
msg = CloudFunc.formatMsg('read', lName, status);
|
||||
|
||||
Util.log(msg);
|
||||
|
||||
if (!lDone.length)
|
||||
Util.exec(pCallBack, lErrors, lReadedFiles);
|
||||
errors[name] = error;
|
||||
}
|
||||
else {
|
||||
status = 'ok';
|
||||
readFiles[name] = data;
|
||||
}
|
||||
|
||||
name = path.basename(name);
|
||||
msg = CloudFunc.formatMsg('read', name, status);
|
||||
|
||||
Util.log(msg);
|
||||
|
||||
if (!done.length)
|
||||
Util.exec(callback, errors, readFiles);
|
||||
};
|
||||
|
||||
if (Util.isFunction(pOptions)) {
|
||||
pCallBack = pOptions;
|
||||
pOptions = null;
|
||||
if (Util.isFunction(options)) {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
|
||||
if (Util.isArray(pFiles))
|
||||
lFiles = pFiles;
|
||||
else
|
||||
lFiles = [pFiles];
|
||||
|
||||
for(i = 0, n = lFiles.length; i < n; i++) {
|
||||
lName = lFiles.pop();
|
||||
lDone.push(lName);
|
||||
n = files && files.length;
|
||||
for (i = 0; i < n; i++) {
|
||||
name = files.pop();
|
||||
done.push(name);
|
||||
|
||||
fs.readFile(lName, pOptions, Util.call(lDoneFunc, lName));
|
||||
fs.readFile(name, options, doneFunc.bind(null, name));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -84,7 +74,7 @@
|
|||
p.write.end();
|
||||
Util.exec(p.callback);
|
||||
} else {
|
||||
name = p.dir + p.names.pop();
|
||||
name = p.dir + p.names.shift();
|
||||
|
||||
pipe.create({
|
||||
from : name,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
SLASH,
|
||||
ISWIN32,
|
||||
ext,
|
||||
path, fs, zlib, url, pipe, CloudFunc, diffPatch,
|
||||
path, fs, zlib, url, pipe, CloudFunc, diffPatch, querystring,
|
||||
|
||||
OK, FILE_NOT_FOUND, MOVED_PERMANENTLY,
|
||||
REQUEST, RESPONSE,
|
||||
|
|
@ -39,7 +39,7 @@
|
|||
exports.https = require('https'),
|
||||
exports.path = path = require('path'),
|
||||
exports.url = url = require('url'),
|
||||
exports.querystring = require('querystring'),
|
||||
exports.querystring = querystring = require('querystring'),
|
||||
|
||||
/* Constants */
|
||||
/* current dir + 2 levels up */
|
||||
|
|
@ -61,6 +61,7 @@
|
|||
|
||||
exports.generateHeaders = generateHeaders,
|
||||
exports.getQuery = getQuery,
|
||||
exports.getPathName = getPathName,
|
||||
exports.isGZIP = isGZIP,
|
||||
exports.mainSetHeader = mainSetHeader,
|
||||
|
||||
|
|
@ -104,6 +105,8 @@
|
|||
exports.cloudfunc = CloudFunc = librequire('cloudfunc'),
|
||||
exports.pipe = pipe = srvrequire('pipe'),
|
||||
exports.socket = srvrequire('socket'),
|
||||
exports.console = srvrequire('console'),
|
||||
exports.terminal = srvrequire('terminal'),
|
||||
exports.express = srvrequire('express'),
|
||||
exports.auth = srvrequire('auth').auth,
|
||||
exports.appcache = srvrequire('appcache'),
|
||||
|
|
@ -112,6 +115,7 @@
|
|||
diffPatch = librequire('diff/diff-match-patch').diff_match_patch,
|
||||
exports.diff = new (librequire('diff').DiffProto)(diffPatch),
|
||||
exports.time = srvrequire('time');
|
||||
exports.users = srvrequire('users');
|
||||
exports.rest = srvrequire('rest').api,
|
||||
exports.update = srvrequire('update'),
|
||||
exports.ischanged = srvrequire('ischanged');
|
||||
|
|
@ -126,39 +130,42 @@
|
|||
|
||||
/**
|
||||
* function do safe require of needed module
|
||||
* @param {Strin} pSrc
|
||||
* @param {Strin} src
|
||||
*/
|
||||
function mrequire(pSrc) {
|
||||
var lModule, msg,
|
||||
lError = Util.tryCatch(function() {
|
||||
lModule = require(pSrc);
|
||||
function mrequire(src) {
|
||||
var module, msg,
|
||||
error = Util.tryCatch(function() {
|
||||
module = require(src);
|
||||
});
|
||||
|
||||
if (lError)
|
||||
msg = CloudFunc.formatMsg('require', pSrc, 'no');
|
||||
if (error)
|
||||
if (error.code === 'MODULE_NOT_FOUND')
|
||||
msg = CloudFunc.formatMsg('require', src, 'no');
|
||||
else
|
||||
Util.log(error);
|
||||
|
||||
Util.log(msg);
|
||||
|
||||
return lModule;
|
||||
return module;
|
||||
}
|
||||
|
||||
function quietrequire(pSrc) {
|
||||
var lModule;
|
||||
function quietrequire(src) {
|
||||
var module;
|
||||
|
||||
Util.tryCatch(function() {
|
||||
lModule = require(pSrc);
|
||||
module = require(src);
|
||||
});
|
||||
|
||||
return lModule;
|
||||
return module;
|
||||
}
|
||||
|
||||
function rootrequire(pSrc) { return mrequire(DIR + pSrc); }
|
||||
function rootrequire(src) { return mrequire(DIR + src); }
|
||||
|
||||
function librequire(pSrc) { return mrequire(LIBDIR + pSrc); }
|
||||
function librequire(src) { return mrequire(LIBDIR + src); }
|
||||
|
||||
function srvrequire(pSrc) { return mrequire(SRVDIR + pSrc); }
|
||||
function srvrequire(src) { return mrequire(SRVDIR + src); }
|
||||
|
||||
function jsonrequire(pSrc) { return mrequire(JSONDIR + pSrc);}
|
||||
function jsonrequire(src) { return mrequire(JSONDIR + src);}
|
||||
|
||||
/**
|
||||
* function check is current platform is win32
|
||||
|
|
@ -280,45 +287,52 @@
|
|||
* @param Data - данные
|
||||
* @param pName - имя отсылаемого файла
|
||||
*/
|
||||
function sendResponse(pParams, pData, pNotLog) {
|
||||
var p, lQuery, lGzip, lHead, data,
|
||||
lRet = checkParams(pParams);
|
||||
function sendResponse(params, data, notLog) {
|
||||
var p, query, isGzip, head,
|
||||
ret = checkParams(params);
|
||||
|
||||
if (lRet) {
|
||||
p = pParams;
|
||||
data = p.data || pData;
|
||||
lGzip = isGZIP(p.request);
|
||||
if (ret) {
|
||||
p = params;
|
||||
data = p.data || data;
|
||||
isGzip = isGZIP(p.request);
|
||||
|
||||
lHead = generateHeaders({
|
||||
head = generateHeaders({
|
||||
name : p.name,
|
||||
cache : p.cache,
|
||||
gzip : lGzip,
|
||||
query : lQuery
|
||||
gzip : isGzip,
|
||||
query : query
|
||||
});
|
||||
|
||||
setHeader(lHead, p.response);
|
||||
setHeader(head, p.response);
|
||||
|
||||
if (!pNotLog)
|
||||
if (!notLog)
|
||||
Util.log(data);
|
||||
|
||||
/* если браузер поддерживает gzip-сжатие - сжимаем данные*/
|
||||
Util.ifExec(!lGzip,
|
||||
function(pParams) {
|
||||
var lRet = Util.checkObj(pParams, ['data']);
|
||||
Util.ifExec(!isGzip,
|
||||
function(params) {
|
||||
var ret = Util.checkObj(params, ['data']);
|
||||
|
||||
if (lRet) {
|
||||
p.status = pParams.status || p.status;
|
||||
p.data = pParams.data;
|
||||
if (ret) {
|
||||
p.status = params.status || p.status;
|
||||
p.data = params.data;
|
||||
}
|
||||
|
||||
p.response.statusCode = p.status || OK;
|
||||
p.response.end(p.data);
|
||||
},
|
||||
|
||||
function(pCallBack) {
|
||||
zlib.gzip (data, Util.call(gzipData, {
|
||||
callback : pCallBack
|
||||
}));
|
||||
function(callback) {
|
||||
zlib.gzip (data, function(error, data) {
|
||||
if (!error)
|
||||
p.data = data;
|
||||
else {
|
||||
p.status = FILE_NOT_FOUND;
|
||||
p.data = error.toString();
|
||||
}
|
||||
|
||||
Util.exec(callback, p);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -348,54 +362,26 @@
|
|||
/**
|
||||
* send error response
|
||||
*/
|
||||
function sendError(pParams, pError) {
|
||||
var p, lRet = checkParams(pParams);
|
||||
function sendError(params, error) {
|
||||
var p, ret = checkParams(params);
|
||||
|
||||
if (lRet) {
|
||||
p = pParams;
|
||||
if (ret) {
|
||||
p = params;
|
||||
p.status = FILE_NOT_FOUND;
|
||||
|
||||
if (!p.data && pError)
|
||||
p.data = pError.toString();
|
||||
if (!p.data && error)
|
||||
p.data = error.toString();
|
||||
|
||||
sendResponse(p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Функция получает сжатые данные
|
||||
* @param pHeader - заголовок файла
|
||||
* @pName
|
||||
*/
|
||||
function gzipData(pParams) {
|
||||
var lRet = checkCallBackParams(pParams),
|
||||
p = pParams;
|
||||
|
||||
if (lRet)
|
||||
lRet = Util.checkObj(pParams.params, ['callback']);
|
||||
|
||||
if (lRet) {
|
||||
var lCallBack = p.params.callback,
|
||||
lParams = {};
|
||||
|
||||
if (!p.error)
|
||||
lParams.data = p.data;
|
||||
else {
|
||||
lParams.status = FILE_NOT_FOUND;
|
||||
lParams.data = p.error.toString();
|
||||
}
|
||||
|
||||
Util.exec(lCallBack, lParams);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function checkCallBackParams(pParams) {
|
||||
return Util.checkObj(pParams, ['error', 'data', 'params']);
|
||||
}
|
||||
|
||||
function checkParams(pParams, pAdditional) {
|
||||
var lRet = Util.checkObjTrue( pParams, ['name', REQUEST, RESPONSE] );
|
||||
var lRet = Util.checkObjTrue(pParams, ['name', REQUEST, RESPONSE]);
|
||||
|
||||
if (lRet && pAdditional)
|
||||
lRet = Util.checkObjTrue( pParams, pAdditional);
|
||||
|
|
@ -403,15 +389,28 @@
|
|||
return lRet;
|
||||
}
|
||||
|
||||
function getQuery(pReq) {
|
||||
var lQuery, lParsedUrl;
|
||||
function getQuery(req) {
|
||||
var query, parsed;
|
||||
|
||||
if (pReq) {
|
||||
lParsedUrl = url.parse(pReq.url);
|
||||
lQuery = lParsedUrl.query;
|
||||
if (req) {
|
||||
parsed = url.parse(req.url);
|
||||
query = parsed.query;
|
||||
}
|
||||
|
||||
return lQuery;
|
||||
return query;
|
||||
}
|
||||
|
||||
function getPathName(req) {
|
||||
var pathname, parsed;
|
||||
|
||||
if (req) {
|
||||
parsed = url.parse(req.url);
|
||||
pathname = parsed.pathname;
|
||||
/* supporting of Russian language in directory names */
|
||||
pathname = querystring.unescape(pathname);
|
||||
}
|
||||
|
||||
return pathname;
|
||||
}
|
||||
|
||||
function isGZIP(pReq) {
|
||||
|
|
|
|||
|
|
@ -10,13 +10,14 @@
|
|||
'# 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' +
|
||||
'# and apiURL in config.json' + '\n' +
|
||||
'# http://cloudcmd.io' + '\n');
|
||||
|
||||
var main = global.cloudcmd.main,
|
||||
fs = main.fs,
|
||||
path = main.path,
|
||||
Hash = main.hash,
|
||||
crypto = main.crypto,
|
||||
Util = main.util,
|
||||
pipe = main.pipe,
|
||||
CloudFunc = main.cloudfunc,
|
||||
|
|
@ -29,26 +30,45 @@
|
|||
sendResponse= main.sendResponse,
|
||||
Header = main.generateHeaders({
|
||||
name:'api.json'
|
||||
});
|
||||
}),
|
||||
|
||||
fse = main.require('fs-extra') || {
|
||||
remove : fs.rmdir.bind(fs),
|
||||
mkdirs : fs.mkdir.bind(fs),
|
||||
copy : function(from, to, callback) {
|
||||
pipe.create({
|
||||
from : from,
|
||||
to : to,
|
||||
callback : callback
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* rest interface
|
||||
* @pParams {request, responce}
|
||||
*/
|
||||
exports.api = function(pParams) {
|
||||
var lRet = main.checkParams(pParams);
|
||||
exports.api = function(request, response, callback) {
|
||||
var apiURL, name, ret;
|
||||
|
||||
if (lRet) {
|
||||
var lAPIURL = main.config.apiURL,
|
||||
p = pParams;
|
||||
if (request && response) {
|
||||
apiURL = main.config.apiURL;
|
||||
name = main.getPathName(request);
|
||||
ret = Util.isContainStr(name, apiURL);
|
||||
|
||||
lRet = Util.isContainStr(p.name, lAPIURL);
|
||||
if (lRet) {
|
||||
p.name = Util.removeStrOneTime(p.name, lAPIURL) || '/';
|
||||
sendData(pParams);
|
||||
if (ret) {
|
||||
name = Util.removeStrOneTime(name, apiURL) || '/';
|
||||
sendData({
|
||||
request : request,
|
||||
response : response,
|
||||
name : name
|
||||
});
|
||||
}
|
||||
}
|
||||
return lRet;
|
||||
if (!ret)
|
||||
Util.exec(callback);
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -71,24 +91,24 @@
|
|||
* @param pParams {command, method, body, requrest, response}
|
||||
*/
|
||||
function sendData(pParams) {
|
||||
var p, lRet = main.checkParams(pParams);
|
||||
if(lRet){
|
||||
var p, ret = main.checkParams(pParams);
|
||||
if (ret) {
|
||||
p = pParams;
|
||||
lRet = Util.isContainStrAtBegin(p.name, CloudFunc.FS);
|
||||
ret = Util.isContainStrAtBegin(p.name, CloudFunc.FS);
|
||||
|
||||
if (lRet)
|
||||
if (ret)
|
||||
onFS(pParams);
|
||||
else {
|
||||
if(p.name[0] === '/')
|
||||
if (p.name[0] === '/')
|
||||
p.command = Util.removeStrOneTime(p.name, '/');
|
||||
|
||||
switch(p.request.method){
|
||||
switch(p.request.method) {
|
||||
case 'GET':
|
||||
lRet = onGET(pParams);
|
||||
ret = onGET(pParams);
|
||||
break;
|
||||
|
||||
case 'PUT':
|
||||
getBody(p.request, function(pBody){
|
||||
getBody(p.request, function(pBody) {
|
||||
p.body = pBody;
|
||||
onPUT(p);
|
||||
});
|
||||
|
|
@ -96,22 +116,22 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
return lRet;
|
||||
return ret;
|
||||
}
|
||||
|
||||
function onFS(pParams) {
|
||||
function onFS(params) {
|
||||
var p, lQuery, isGet,
|
||||
lRet = main.checkParams(pParams);
|
||||
ret = main.checkParams(params);
|
||||
|
||||
if (lRet){
|
||||
p = pParams;
|
||||
if (ret) {
|
||||
p = params;
|
||||
lQuery = main.getQuery(p.request);
|
||||
p.name = Util.removeStrOneTime(p.name, CloudFunc.FS) || '/';
|
||||
|
||||
switch (p.request.method) {
|
||||
case 'GET':
|
||||
isGet = onFSGet(lQuery, p.name, function(error, result) {
|
||||
checkSendError(error, pParams, function() {
|
||||
checkSendError(error, params, function() {
|
||||
sendResponse(p, result);
|
||||
});
|
||||
});
|
||||
|
|
@ -139,53 +159,37 @@
|
|||
|
||||
case 'PUT':
|
||||
if (lQuery === 'dir')
|
||||
fs.mkdir(p.name, function(pError) {
|
||||
checkSendError(pError, pParams, function() {
|
||||
sendMsg(pParams, 'make dir', p.name);
|
||||
fse.mkdirs(p.name, function(pError) {
|
||||
checkSendError(pError, params, function() {
|
||||
sendMsg(params, 'make dir', p.name);
|
||||
});
|
||||
});
|
||||
else if(lQuery === 'patch')
|
||||
getBody(p.request, function(pPatch) {
|
||||
fs.readFile(p.name, Util.call(read, pParams));
|
||||
else if (lQuery === 'patch')
|
||||
getBody(p.request, function(patch) {
|
||||
fs.readFile(p.name, 'utf8', read.bind(null, p.name));
|
||||
|
||||
function read(pParams) {
|
||||
var lDiff, lStr, p, lData, lName,
|
||||
lRet = main.checkCallBackParams(pParams) &&
|
||||
main.checkParams(pParams.params);
|
||||
|
||||
if (lRet) {
|
||||
p = pParams;
|
||||
lName = p.params.name;
|
||||
function read(name, error, data) {
|
||||
checkSendError(error, p.params, function() {
|
||||
var diffResult;
|
||||
|
||||
checkSendError(p.error, p.params, function() {
|
||||
lStr = p.data.toString();
|
||||
lRet = Util.tryCatchLog(function() {
|
||||
lDiff = diff.applyPatch(lStr, pPatch);
|
||||
});
|
||||
|
||||
if (lDiff && !lRet)
|
||||
fs.writeFile(lName, lDiff, Util.call(write, p.params));
|
||||
else {
|
||||
lName = path.basename(lName);
|
||||
sendMsg(p.params, 'patch', lName, 'fail');
|
||||
}
|
||||
ret = Util.tryCatchLog(function() {
|
||||
diffResult = diff.applyPatch(data, patch);
|
||||
});
|
||||
}
|
||||
|
||||
if (diffResult && !ret)
|
||||
fs.writeFile(name, diffResult, write.bind(null, name));
|
||||
else {
|
||||
name = path.basename(name);
|
||||
sendMsg(p.params, 'patch', name, 'fail');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function write(pParams) {
|
||||
var p, lName,
|
||||
lRet = main.checkCallBackParams(pParams) &&
|
||||
main.checkParams(pParams.params);
|
||||
|
||||
if (lRet) {
|
||||
p = pParams;
|
||||
|
||||
checkSendError(p.error, p.params, function() {
|
||||
lName = path.basename(p.params.name);
|
||||
sendMsg(p.params, 'patch', lName);
|
||||
});
|
||||
}
|
||||
function write(name, error) {
|
||||
checkSendError(error, params, function() {
|
||||
name = path.basename(name);
|
||||
sendMsg(params, 'patch', name);
|
||||
});
|
||||
}
|
||||
});
|
||||
else
|
||||
|
|
@ -193,73 +197,77 @@
|
|||
read : p.request,
|
||||
to : p.name,
|
||||
callback : function(pError) {
|
||||
checkSendError(pError, pParams, function() {
|
||||
checkSendError(pError, params, function() {
|
||||
var lName = path.basename(p.name);
|
||||
sendMsg(pParams, 'save', lName);
|
||||
sendMsg(params, 'save', lName);
|
||||
});
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'DELETE':
|
||||
if (lQuery === 'dir')
|
||||
fs.rmdir(p.name, function(pError){
|
||||
checkSendError(pError, pParams, function() {
|
||||
sendMsg(pParams, 'delete', p.name);
|
||||
});
|
||||
onDelete(params, lQuery, function(error, msg, callback) {
|
||||
checkSendError(error, params, function() {
|
||||
if (callback)
|
||||
Util.exec(callback);
|
||||
else
|
||||
sendMsg(params, 'delete', msg);
|
||||
});
|
||||
else if (lQuery === 'files') {
|
||||
getBody(p.request, function(pBody) {
|
||||
var lFiles = Util.parseJSON(pBody),
|
||||
n = lFiles.length,
|
||||
lDir = p.name,
|
||||
log = Util.log,
|
||||
lAssync = 0;
|
||||
|
||||
function stat(pStat) {
|
||||
var lRet = Util.checkObjTrue(pStat, 'params') &&
|
||||
Util.checkObjTrue(pStat.params, 'name');
|
||||
|
||||
if (lRet) {
|
||||
var p = pStat,
|
||||
d = p.params;
|
||||
|
||||
++lAssync;
|
||||
|
||||
checkSendError(p.error, pParams, function() {
|
||||
if (p.data.isDirectory())
|
||||
fs.rmdir(d.name, log);
|
||||
|
||||
else if (p.data.isFile())
|
||||
fs.unlink(d.name, log);
|
||||
});
|
||||
|
||||
if (lAssync === n && !p.error)
|
||||
sendMsg(pParams, 'delete', pBody);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for(var i = 0; i < n; i ++) {
|
||||
var lName = lDir + lFiles[i];
|
||||
Util.log(lName);
|
||||
fs.stat(lName, Util.call(stat, {
|
||||
name: lName
|
||||
}));
|
||||
}
|
||||
});
|
||||
}else
|
||||
fs.unlink(p.name, function(pError) {
|
||||
checkSendError(pError, pParams, function() {
|
||||
sendMsg(pParams, 'delete', p.name);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return lRet;
|
||||
return ret;
|
||||
}
|
||||
|
||||
function onDelete(params, query, callback) {
|
||||
var rmFile = fs.unlink.bind(fs),
|
||||
rmDir = fse.remove.bind(fse),
|
||||
p = params;
|
||||
|
||||
if (query === 'dir')
|
||||
rmDir(p.name, function(error) {
|
||||
Util.exec(callback, error, p.name);
|
||||
});
|
||||
else if (query === 'files')
|
||||
getBody(p.request, function(body) {
|
||||
var i, name,
|
||||
files = Util.parseJSON(body),
|
||||
n = files.length,
|
||||
dir = p.name,
|
||||
log = Util.log.bind(Util),
|
||||
assync = 0;
|
||||
|
||||
function onStat(name, error, stat) {
|
||||
++assync;
|
||||
|
||||
if (error)
|
||||
Util.exec(callback, error);
|
||||
else {
|
||||
if (stat.isDirectory())
|
||||
rmDir(name, log);
|
||||
|
||||
else if (stat.isFile())
|
||||
rmFile(name, log);
|
||||
|
||||
if (assync === n)
|
||||
Util.exec(callback, null, body);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i ++) {
|
||||
name = dir + files[i];
|
||||
|
||||
Util.log(name);
|
||||
|
||||
fs.stat(name, onStat.bind(null, name));
|
||||
}
|
||||
});
|
||||
else
|
||||
rmFile(p.name, function(error) {
|
||||
Util.exec(callback, error, p.name);
|
||||
});
|
||||
}
|
||||
|
||||
function onFSGet(query, name, callback) {
|
||||
|
|
@ -314,8 +322,8 @@
|
|||
* @param pParams {command, method, body, requrest, response}
|
||||
*/
|
||||
function onGET(pParams) {
|
||||
var lRet = main.checkParams(pParams);
|
||||
if (lRet) {
|
||||
var ret = main.checkParams(pParams);
|
||||
if (ret) {
|
||||
var p = pParams,
|
||||
lCmd = p.command;
|
||||
|
||||
|
|
@ -336,7 +344,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
return lRet;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -345,17 +353,19 @@
|
|||
* @param pParams {command, method, body, requrest, response}
|
||||
*/
|
||||
function onPUT(pParams) {
|
||||
var name, data, json, config,
|
||||
lRet = main.checkParams(pParams, ['body']);
|
||||
var name, data, json, config, callback,
|
||||
ret = main.checkParams(pParams, ['body']);
|
||||
|
||||
if (lRet) {
|
||||
if (ret) {
|
||||
var p = pParams,
|
||||
lCmd = p.command,
|
||||
lFiles = Util.parseJSON(p.body);
|
||||
|
||||
console.log(lFiles);
|
||||
|
||||
switch(lCmd) {
|
||||
case 'auth':
|
||||
main.auth(p.body, function(pTocken){
|
||||
main.auth(p.body, function(pTocken) {
|
||||
send({
|
||||
response: p.response,
|
||||
data: pTocken
|
||||
|
|
@ -364,7 +374,7 @@
|
|||
break;
|
||||
|
||||
case 'mv':
|
||||
if(!Util.checkObjTrue(lFiles, ['from', 'to']) )
|
||||
if (!Util.checkObjTrue(lFiles, ['from', 'to']) )
|
||||
sendError(pParams, p.data);
|
||||
else
|
||||
fs.rename(lFiles.from, lFiles.to, function(pError) {
|
||||
|
|
@ -376,19 +386,16 @@
|
|||
break;
|
||||
|
||||
case 'cp':
|
||||
if (!Util.checkObjTrue(lFiles, ['from', 'to']))
|
||||
callback = function(error) {
|
||||
checkSendError(error, pParams, function() {
|
||||
sendMsg(pParams, 'copy', lFiles.to);
|
||||
});
|
||||
};
|
||||
|
||||
if (!Util.checkObjTrue(lFiles, ['from', 'to']))
|
||||
sendError(pParams, p.data);
|
||||
else
|
||||
pipe.create({
|
||||
from : lFiles.from,
|
||||
to : lFiles.to,
|
||||
callback : function(pError) {
|
||||
if (pError)
|
||||
sendError(pParams, pError);
|
||||
else
|
||||
sendMsg(pParams, 'copy', lFiles.to);
|
||||
}
|
||||
});
|
||||
fse.copy(lFiles.from, lFiles.to, callback);
|
||||
|
||||
break;
|
||||
|
||||
|
|
@ -411,7 +418,16 @@
|
|||
break;
|
||||
|
||||
case 'config':
|
||||
config = main.config;
|
||||
var hash,
|
||||
passwd = lFiles && lFiles.password,
|
||||
sha = crypto.createHash('sha1');
|
||||
config = main.config;
|
||||
|
||||
if (passwd) {
|
||||
sha.update(passwd);
|
||||
passwd = sha.digest('hex');
|
||||
lFiles.password = passwd;
|
||||
}
|
||||
|
||||
for (name in lFiles)
|
||||
config[name] = lFiles[name];
|
||||
|
|
@ -432,7 +448,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
return lRet;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -441,14 +457,14 @@
|
|||
* @param pReq
|
||||
* @param pCallBack
|
||||
*/
|
||||
function getBody(pReq, pCallBack) {
|
||||
function getBody(req, pCallBack) {
|
||||
var lBody = '';
|
||||
|
||||
pReq.on('data', function(chunk) {
|
||||
req.on('data', function(chunk) {
|
||||
lBody += chunk.toString();
|
||||
});
|
||||
|
||||
pReq.on('end', function() {
|
||||
req.on('end', function() {
|
||||
Util.exec(pCallBack, lBody);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,58 +1,57 @@
|
|||
/* module make possible connectoin thru socket.io on a server */
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
var main = global.cloudcmd.main,
|
||||
DIR = main.DIR,
|
||||
SRVDIR = main.SRVDIR,
|
||||
|
||||
io = main.require('socket.io'),
|
||||
update = main.srvrequire('update'),
|
||||
exec = main.child_process.exec,
|
||||
Util = main.util,
|
||||
path = main.path,
|
||||
mainpackage = main.mainpackage,
|
||||
equalPart = Util.isContainStrAtBegin,
|
||||
CLOUDCMD = mainpackage.name,
|
||||
ClientFuncs = [],
|
||||
ClientDirs = [],
|
||||
OnMessageFuncs = [],
|
||||
INFO_LOG_LEVEL = 2,
|
||||
ENV = process.env,
|
||||
WIN32 = main.WIN32,
|
||||
HELP = {
|
||||
stdout : CLOUDCMD + ' exit \n' +
|
||||
CLOUDCMD + ' update \n',
|
||||
},
|
||||
CloudFunc = main.cloudfunc,
|
||||
|
||||
/* windows commands thet require
|
||||
* unicode charset on locales
|
||||
* different then English
|
||||
*/
|
||||
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'];
|
||||
WIN32 = main.WIN32,
|
||||
INFO_LOG_LEVEL = 2,
|
||||
AllListeners = [];
|
||||
|
||||
exports.on = addListener;
|
||||
exports.addListener = addListener;
|
||||
exports.removeListener = removeListener;
|
||||
exports.send = send;
|
||||
exports.emit = emit;
|
||||
exports.listen = listen;
|
||||
|
||||
function addListener(name, func, socket) {
|
||||
if (!socket)
|
||||
socket = io.sockets;
|
||||
|
||||
CloudFunc.addListener(name, func, AllListeners, socket);
|
||||
}
|
||||
|
||||
function removeListener(name, func, socket) {
|
||||
CloudFunc.removeListener(name, func, AllListeners, socket);
|
||||
}
|
||||
|
||||
function send(msg, socket) {
|
||||
if (socket)
|
||||
socket.send(msg);
|
||||
}
|
||||
|
||||
function emit(channel, message, socket, all) {
|
||||
var obj;
|
||||
|
||||
if (socket) {
|
||||
if (all)
|
||||
obj = socket.broadcast;
|
||||
else
|
||||
obj = socket;
|
||||
|
||||
obj.emit(channel, message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* function listen on servers port
|
||||
* @pServer {Object} started server object
|
||||
*/
|
||||
exports.listen = function(pServer) {
|
||||
var lRet, lConnNum, lMsg, lConn_func;
|
||||
|
||||
function listen(pServer) {
|
||||
if (io) {
|
||||
io = io.listen(pServer);
|
||||
lConnNum = 0;
|
||||
|
||||
io.set('log level', INFO_LOG_LEVEL);
|
||||
|
||||
|
|
@ -75,268 +74,8 @@
|
|||
'xhr-polling',
|
||||
'jsonp-polling'
|
||||
]);
|
||||
|
||||
lRet = io.sockets.on('connection', function (socket) {
|
||||
++lConnNum;
|
||||
|
||||
if(!OnMessageFuncs[lConnNum]) {
|
||||
lMsg = log(lConnNum, 'connected\n');
|
||||
|
||||
jsonSend(socket, {
|
||||
stdout : lMsg
|
||||
});
|
||||
|
||||
OnMessageFuncs[lConnNum] = onMessage(lConnNum, socket);
|
||||
lConn_func = OnMessageFuncs[lConnNum];
|
||||
|
||||
socket.on('message', lConn_func);
|
||||
socket.on('disconnect', Util.call(disconnect, lConnNum));
|
||||
} else {
|
||||
lMsg = log(lConnNum, ' in use. Reconnecting...');
|
||||
|
||||
jsonSend(socket, {
|
||||
stdout: lMsg
|
||||
});
|
||||
|
||||
socket.disconnect();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
return lRet;
|
||||
};
|
||||
|
||||
function disconnect(pParams) {
|
||||
var lConnNum, lRet = Util.checkObj(pParams, ['params']);
|
||||
|
||||
if(lRet) {
|
||||
lConnNum = pParams.params;
|
||||
OnMessageFuncs [lConnNum] =
|
||||
ClientFuncs [lConnNum] = null;
|
||||
|
||||
log(lConnNum, 'disconnected');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* function gets onMessage function
|
||||
* that execute needed command
|
||||
*
|
||||
* @param pConnNum, pSocket
|
||||
*/
|
||||
function onMessage(pConnNum, pSocket) {
|
||||
return function(pCommand) {
|
||||
var lMsg, lWinCommand, lExec_func, firstChar,
|
||||
connName,
|
||||
lError, lRet, lExecSymbols, isContain,
|
||||
dir, options = {};
|
||||
|
||||
dir = ClientDirs[pConnNum];
|
||||
|
||||
if (!dir)
|
||||
dir = ClientDirs[pConnNum] = DIR;
|
||||
|
||||
connName = '#' + pConnNum + ': ';
|
||||
Util.log(connName + pCommand);
|
||||
|
||||
if (equalPart(pCommand, CLOUDCMD))
|
||||
lRet = onCloudCmd(pCommand, function(json) {
|
||||
jsonSend(pSocket, json);
|
||||
});
|
||||
else if (equalPart(pCommand, 'cd ')) {
|
||||
lRet = true;
|
||||
|
||||
onCD(pCommand, dir, function(json) {
|
||||
var error = json.stderr,
|
||||
stdout = json.stdout;
|
||||
|
||||
if (error)
|
||||
jsonSend(pSocket, json);
|
||||
else
|
||||
ClientDirs[pConnNum] = stdout;
|
||||
});
|
||||
}
|
||||
|
||||
if (!lRet) {
|
||||
/* if we on windows and command is build in
|
||||
* change code page to unicode becouse
|
||||
* windows use unicode on non English versions
|
||||
*/
|
||||
if(WIN32) {
|
||||
lWinCommand = pCommand.toUpperCase();
|
||||
|
||||
if (Win32Commands.indexOf(lWinCommand) >= 0)
|
||||
pCommand = 'chcp 65001 |' + pCommand;
|
||||
}
|
||||
|
||||
if(!ClientFuncs[pConnNum])
|
||||
ClientFuncs[pConnNum] = getExec(function(json, pError, pStderr) {
|
||||
log(pConnNum, pError, 'error');
|
||||
log(pConnNum, pStderr, 'stderror');
|
||||
|
||||
jsonSend(pSocket, json);
|
||||
});
|
||||
|
||||
lExec_func = ClientFuncs[pConnNum];
|
||||
lExecSymbols = ['*', '&', '{', '}', '|', '\'', '"'];
|
||||
isContain = Util.isContainStr(pCommand, lExecSymbols);
|
||||
firstChar = pCommand[0];
|
||||
options.cwd = dir;
|
||||
|
||||
if (firstChar === '#') {
|
||||
pCommand = pCommand.slice(1);
|
||||
pCommand = connName + pCommand;
|
||||
pCommand = Util.addNewLine(pCommand);
|
||||
|
||||
lMsg = Util.stringifyJSON({
|
||||
stdout: pCommand
|
||||
});
|
||||
|
||||
io.sockets.emit('message', lMsg);
|
||||
} else if (WIN32 || firstChar === ' ' || isContain)
|
||||
exec(pCommand, options, lExec_func);
|
||||
else
|
||||
getSpawn(pCommand, options, function(json) {
|
||||
jsonSend(pSocket, json);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* function send result of command to client
|
||||
* @param pSocket
|
||||
*/
|
||||
function getExec(callback) {
|
||||
return function(pError, pStdout, pStderr) {
|
||||
var lErrorStr, lExecStr, lExec,
|
||||
lError = pStderr || pError;
|
||||
|
||||
if (lError) {
|
||||
if (Util.isString(lError))
|
||||
lErrorStr = lError;
|
||||
else
|
||||
lErrorStr = lError.toString();
|
||||
|
||||
lErrorStr = Util.addNewLine(lErrorStr);
|
||||
}
|
||||
|
||||
lExec = {
|
||||
stdout : pStdout,
|
||||
stderr : lErrorStr || lError
|
||||
};
|
||||
|
||||
Util.exec(callback, lExec, pError, pStderr);
|
||||
};
|
||||
}
|
||||
|
||||
function getSpawn(pCommand, options, callback) {
|
||||
var send, cmd, spawn,
|
||||
args = pCommand.split(' ');
|
||||
|
||||
pCommand = args.shift();
|
||||
|
||||
spawn = main.child_process.spawn;
|
||||
cmd = spawn(pCommand, args, options);
|
||||
send = function(data, isError) {
|
||||
var lExec = {},
|
||||
msg = data.toString();
|
||||
|
||||
if (isError)
|
||||
lExec.stderr = msg;
|
||||
else
|
||||
lExec.stdout = msg;
|
||||
|
||||
Util.exec(callback, lExec);
|
||||
};
|
||||
|
||||
cmd.stdout.on('data', send);
|
||||
|
||||
cmd.stderr.on('data', function(data) {
|
||||
send(data, true);
|
||||
});
|
||||
|
||||
cmd.on('error', Util.retFalse);
|
||||
}
|
||||
|
||||
function onCloudCmd(pCommand, callback) {
|
||||
var lRet;
|
||||
|
||||
pCommand = Util.removeStr(pCommand, CLOUDCMD);
|
||||
|
||||
if (!equalPart(pCommand, ' ')) {
|
||||
lRet = true;
|
||||
Util.exec(callback, HELP);
|
||||
}
|
||||
else {
|
||||
pCommand = Util.removeStr(pCommand, ' ');
|
||||
|
||||
if (equalPart(pCommand, 'update') && update) {
|
||||
lRet = true;
|
||||
update.get();
|
||||
|
||||
Util.exec(callback, {
|
||||
stdout: Util.addNewLine('update: ok')
|
||||
});
|
||||
}
|
||||
|
||||
if (Util.strCmp(pCommand, 'exit'))
|
||||
process.exit();
|
||||
}
|
||||
|
||||
return lRet;
|
||||
}
|
||||
|
||||
function onCD(pCommand, currDir, callback) {
|
||||
var dir,
|
||||
getDir = WIN32 ? 'chdir' : 'pwd',
|
||||
paramDir = Util.removeStr(pCommand, 'cd ');
|
||||
|
||||
if (equalPart(paramDir, ['/', '~']))
|
||||
dir = paramDir;
|
||||
else
|
||||
dir = path.join(currDir, paramDir);
|
||||
|
||||
exec('cd ' + dir + ' && ' + getDir, function (error, stdout, stderr) {
|
||||
var lRet,
|
||||
lMsg = '',
|
||||
lError = error || stderr;
|
||||
|
||||
if (lError) {
|
||||
lError = Util.stringifyJSON(lError);
|
||||
lMsg = lError;
|
||||
}
|
||||
|
||||
Util.exec(callback, {
|
||||
stderr : lMsg,
|
||||
stdout : Util.rmNewLine(stdout)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function log(pConnNum, pStr, pType) {
|
||||
var lRet,
|
||||
lType = ' ';
|
||||
|
||||
if (pStr) {
|
||||
|
||||
if (pType)
|
||||
lType += pType + ':';
|
||||
|
||||
lRet = 'client #' + pConnNum + lType + pStr;
|
||||
|
||||
Util.log(lRet);
|
||||
}
|
||||
|
||||
return lRet;
|
||||
}
|
||||
|
||||
function jsonSend(socket, json) {
|
||||
var msg = Util.stringifyJSON(json);
|
||||
socket.send(msg);
|
||||
return io;
|
||||
}
|
||||
})();
|
||||
|
|
|
|||
128
lib/server/terminal.js
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
(function() {
|
||||
'use strict';
|
||||
|
||||
var main = global.cloudcmd.main,
|
||||
DIR = main.DIR,
|
||||
socket = main.socket,
|
||||
spawn = main.child_process.spawn,
|
||||
pty = main.require('pty.js'),
|
||||
|
||||
Util = main.util,
|
||||
path = main.path,
|
||||
CloudFunc = main.cloudfunc,
|
||||
|
||||
mainpackage = main.mainpackage,
|
||||
CLOUDCMD = mainpackage.name,
|
||||
ClientDirs = [],
|
||||
Clients = [],
|
||||
WIN32 = main.WIN32,
|
||||
|
||||
CHANNEL = CloudFunc.CHANNEL_TERMINAL,
|
||||
CHANNEL_RESIZE = CloudFunc.CHANNEL_TERMINAL_RESIZE,
|
||||
|
||||
ConNum = 0,
|
||||
|
||||
INFO = 'to use terminal install pty.js: npm i pty.js';
|
||||
|
||||
/**
|
||||
* function listen on servers port
|
||||
* @pServer {Object} started server object
|
||||
*/
|
||||
exports.init = function() {
|
||||
var ret, func,
|
||||
makePty = function(clientSocket) {
|
||||
onConnection(clientSocket, function(channel, data) {
|
||||
socket.emit(channel, data, clientSocket);
|
||||
});
|
||||
},
|
||||
|
||||
sendInfo = function(clientSocket) {
|
||||
Util.log(INFO);
|
||||
socket.emit(CHANNEL, INFO, clientSocket);
|
||||
};
|
||||
|
||||
if (pty)
|
||||
func = makePty;
|
||||
else
|
||||
func = sendInfo;
|
||||
|
||||
ret = socket.on('connection', func);
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
function onConnection(clientSocket, callback) {
|
||||
var msg, onDisconnect, resizeFunc, dataFunc, term;
|
||||
|
||||
++ConNum;
|
||||
|
||||
if (!Clients[ConNum]) {
|
||||
log(ConNum, 'terminal connected');
|
||||
|
||||
term = getTerm(callback);
|
||||
dataFunc = onData.bind(null, term);
|
||||
resizeFunc = onResize.bind(null, term, callback);
|
||||
|
||||
onDisconnect = function(conNum, term) {
|
||||
Clients[conNum] = null;
|
||||
|
||||
log(conNum, 'terminal disconnected');
|
||||
|
||||
socket.removeListener(CHANNEL, dataFunc, clientSocket);
|
||||
socket.removeListener(CHANNEL_RESIZE, resizeFunc, clientSocket);
|
||||
socket.removeListener('disconnect', onDisconnect, clientSocket);
|
||||
|
||||
term.destroy();
|
||||
}.bind(null, ConNum, term);
|
||||
|
||||
socket.on(CHANNEL, dataFunc, clientSocket);
|
||||
socket.on(CHANNEL_RESIZE, resizeFunc, clientSocket);
|
||||
socket.on('disconnect', onDisconnect, clientSocket);
|
||||
} else {
|
||||
log(ConNum, ' in use. Reconnecting...\n');
|
||||
socket.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
function onResize(term, callback, size) {
|
||||
term.resize(size.cols, size.rows);
|
||||
Util.exec(callback, CHANNEL_RESIZE, size);
|
||||
}
|
||||
|
||||
function onData(term, data) {
|
||||
term.write(data);
|
||||
}
|
||||
|
||||
function getTerm(callback) {
|
||||
var onData = Util.exec.bind(Util, callback, CHANNEL),
|
||||
|
||||
term = pty.spawn('bash', [], {
|
||||
name: 'xterm-color',
|
||||
cols: 80,
|
||||
rows: 25,
|
||||
cwd : DIR,
|
||||
env : process.env
|
||||
});
|
||||
|
||||
term.on('data', onData);
|
||||
|
||||
return term;
|
||||
}
|
||||
|
||||
function log(pConnNum, pStr, pType) {
|
||||
var lRet,
|
||||
lType = ' ';
|
||||
|
||||
if (pStr) {
|
||||
|
||||
if (pType)
|
||||
lType += pType + ':';
|
||||
|
||||
lRet = 'client #' + pConnNum + lType + pStr;
|
||||
|
||||
Util.log(lRet);
|
||||
}
|
||||
|
||||
return lRet;
|
||||
}
|
||||
})();
|
||||
82
lib/server/users.js
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
(function(object) {
|
||||
'use strict';
|
||||
|
||||
if(!global.cloudcmd)
|
||||
return console.log(
|
||||
'# time.js' + '\n' +
|
||||
'# -----------' + '\n' +
|
||||
'# Module is part of Cloud Commander,' + '\n' +
|
||||
'# used for getting file change time.' + '\n' +
|
||||
'# http://cloudcmd.io' + '\n');
|
||||
|
||||
var main = global.cloudcmd.main,
|
||||
fs = main.fs,
|
||||
time = main.time,
|
||||
Util = main.util,
|
||||
FILE = '/etc/passwd',
|
||||
FileTime,
|
||||
Names;
|
||||
|
||||
object.getNames = function(callback) {
|
||||
getTime(function(error, names) {
|
||||
Util.exec(callback, error, names);
|
||||
});
|
||||
};
|
||||
|
||||
function getTime(callback) {
|
||||
time.get(FILE, function(error, time) {
|
||||
if (error)
|
||||
callback(error);
|
||||
else if (FileTime === time) {
|
||||
if (!Names)
|
||||
error = 'user: parse error of ' + FILE;
|
||||
|
||||
callback(error, Names);
|
||||
} else {
|
||||
FileTime = time;
|
||||
read(callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function read(callback) {
|
||||
fs.readFile(FILE, 'utf8', function(error, passwd) {
|
||||
if (!error)
|
||||
Names = get(passwd);
|
||||
|
||||
callback(error, Names);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/** Функция парсит uid и имена пользователей
|
||||
* из переданного в строке вычитаного файла /etc/passwd
|
||||
* и возвращает массив обьектов имён и uid пользователей
|
||||
* @pPasswd_s - строка, в которой находиться файл /etc/passwd
|
||||
*/
|
||||
function get(passwd) {
|
||||
var uid, name, line,
|
||||
users = {};
|
||||
|
||||
if (passwd)
|
||||
do {
|
||||
line = passwd.substr(passwd, passwd.indexOf('\n') + 1);
|
||||
|
||||
if (line) {
|
||||
passwd = Util.removeStr(passwd, line);
|
||||
|
||||
/* получаем первое слово строки */
|
||||
name = line.substr(line, line.indexOf(':'));
|
||||
line = Util.removeStr(line, name + ':x:');
|
||||
|
||||
/* получаем uid */
|
||||
uid = line.substr(line, line.indexOf(':'));
|
||||
|
||||
if (uid)
|
||||
users[uid] = name;
|
||||
}
|
||||
} while (passwd);
|
||||
|
||||
return users;
|
||||
}
|
||||
})(this);
|
||||
314
lib/util.js
|
|
@ -19,25 +19,25 @@
|
|||
function UtilProto() {
|
||||
var Util = this;
|
||||
|
||||
this.asyncCall = function(pFuncs, pOnLoad, pContext) {
|
||||
this.asyncCall = function(funcs, callback) {
|
||||
var i, element, name, func,
|
||||
funcsCount = pFuncs.length,
|
||||
funcsCount = funcs.length,
|
||||
count = 0,
|
||||
data = [];
|
||||
|
||||
for (i = 0; i < funcsCount; i++) {
|
||||
func = pFuncs[i];
|
||||
func = funcs[i];
|
||||
callCheckFunc(i, func);
|
||||
}
|
||||
|
||||
function checkFunc(pNum, pData) {
|
||||
var i, m = pData.length,
|
||||
var i, n = pData.length,
|
||||
params = [];
|
||||
|
||||
++count;
|
||||
|
||||
if (m >= 2) {
|
||||
for (i = 0; i < m; i++)
|
||||
if (n >= 2) {
|
||||
for (i = 0; i < n; i++)
|
||||
params[i] = pData[i];
|
||||
|
||||
data[pNum] = params;
|
||||
|
|
@ -45,21 +45,36 @@
|
|||
data[pNum] = pData[0];
|
||||
|
||||
if (count === funcsCount)
|
||||
pOnLoad.apply(pContext, data);
|
||||
Util.retExec(callback).apply(null, data);
|
||||
}
|
||||
|
||||
function callCheckFunc(pNum, pFunc) {
|
||||
Util.exec(pFunc, function() {
|
||||
checkFunc(pNum, arguments);
|
||||
function callCheckFunc(num, func) {
|
||||
Util.exec(func, function() {
|
||||
checkFunc(num, arguments);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* bind function to arguments without context
|
||||
*/
|
||||
|
||||
this.bind = function(callback) {
|
||||
var result,
|
||||
args = Util.slice(arguments, 1),
|
||||
bind = Function.prototype.bind;
|
||||
|
||||
args.unshift(null);
|
||||
result = bind.apply(callback, args);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* ad new line (if it's not)
|
||||
* @param {string} pText
|
||||
*/
|
||||
this.addNewLine = function(pText){
|
||||
this.addNewLine = function(pText) {
|
||||
var lNewLine = '',
|
||||
n = pText && pText.length;
|
||||
|
||||
|
|
@ -73,45 +88,13 @@
|
|||
* rm new line (if it's)
|
||||
* @param {string} pText
|
||||
*/
|
||||
this.rmNewLine = function(pText){
|
||||
this.rmNewLine = function(pText) {
|
||||
var strs = ['\n', '\r'],
|
||||
text = Util.removeStr(pText, strs);
|
||||
|
||||
return text;
|
||||
};
|
||||
|
||||
/** setting function context
|
||||
* @param {function} pFunction
|
||||
* @param {object} pContext
|
||||
*/
|
||||
this.bind = function(pFunction, pContext) {
|
||||
var lRet;
|
||||
|
||||
if (Util.isFunction(pFunction))
|
||||
lRet = pFunction.bind(pContext);
|
||||
|
||||
return lRet;
|
||||
};
|
||||
|
||||
/**
|
||||
* callback for functions(pError, pData)
|
||||
* thet moves on our parameters.
|
||||
*
|
||||
* @param pFunc
|
||||
* @param pParams
|
||||
*/
|
||||
this.call = function(pFunc, pParams) {
|
||||
function lFunc(pError, pData) {
|
||||
Util.exec(pFunc, {
|
||||
error : pError,
|
||||
data : pData,
|
||||
params : pParams
|
||||
});
|
||||
}
|
||||
|
||||
return lFunc;
|
||||
};
|
||||
|
||||
/**
|
||||
* Функция ищет в имени файла расширение
|
||||
* и если находит возвращает true
|
||||
|
|
@ -210,28 +193,22 @@
|
|||
};
|
||||
|
||||
/**
|
||||
* Copy properties array pProps from pFromObj to pToObj
|
||||
* Copy properties from from to to
|
||||
*
|
||||
* @param pFromObj
|
||||
* @param pToObj
|
||||
* @param pProps
|
||||
* @param from
|
||||
* @param to
|
||||
*/
|
||||
this.copyObj = function(pFromObj, pToObj, pProps) {
|
||||
var toObj = pToObj || pProps || {},
|
||||
forIn = function(obj) {
|
||||
Util.forIn(obj, function(name) {
|
||||
toObj[name] = obj[name];
|
||||
});
|
||||
};
|
||||
|
||||
if (Util.isObject(pFromObj)) {
|
||||
if (pProps)
|
||||
forIn(pProps);
|
||||
|
||||
forIn(pFromObj);
|
||||
this.copyObj = function(to, from) {
|
||||
if (!from) {
|
||||
from = to;
|
||||
to = {};
|
||||
}
|
||||
|
||||
return toObj;
|
||||
Util.forIn(from, function(name) {
|
||||
to[name] = from[name];
|
||||
});
|
||||
|
||||
return to;
|
||||
};
|
||||
|
||||
this.convertArrToObj = function(pArrKeys, pArrVal) {
|
||||
|
|
@ -335,13 +312,13 @@
|
|||
* @param pObj
|
||||
*/
|
||||
this.stringifyJSON = function(pObj) {
|
||||
var lRet;
|
||||
var ret;
|
||||
|
||||
Util.tryCatchLog(function() {
|
||||
lRet = JSON.stringify(pObj, null, 4);
|
||||
ret = JSON.stringify(pObj, null, 4);
|
||||
});
|
||||
|
||||
return lRet;
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -350,20 +327,20 @@
|
|||
* @param pStr2
|
||||
*/
|
||||
this.strCmp = function(pStr1, pStr2) {
|
||||
var lRet = Util.isString(pStr1);
|
||||
var i, n,
|
||||
lRet = Util.isString(pStr1);
|
||||
|
||||
if (lRet) {
|
||||
if ( Util.isArray(pStr2) )
|
||||
for(var i = 0, n = pStr2.length; i < n; i++) {
|
||||
lRet = Util.strCmp( pStr1, pStr2[i] );
|
||||
if (lRet)
|
||||
if (Util.isArray(pStr2))
|
||||
for (i = 0, n = pStr2.length; i < n; i++) {
|
||||
lRet = Util.strCmp(pStr1, pStr2[i]);
|
||||
|
||||
if (lRet)
|
||||
break;
|
||||
}
|
||||
else if ( Util.isString(pStr2) )
|
||||
else if (Util.isString(pStr2))
|
||||
lRet = Util.isContainStr(pStr1, pStr2) &&
|
||||
pStr1.length === pStr2.length;
|
||||
}
|
||||
|
||||
return lRet;
|
||||
|
||||
|
|
@ -388,21 +365,24 @@
|
|||
*/
|
||||
|
||||
this.isContainStr = function(pStr1, pStr2) {
|
||||
var lRet = Util.isString(pStr1);
|
||||
var i, n, regExp, str,
|
||||
ret = Util.isString(pStr1);
|
||||
|
||||
if ( lRet ) {
|
||||
if ( Util.isArray(pStr2) )
|
||||
for(var i = 0, n = pStr2.length; i < n; i++) {
|
||||
lRet = Util.isContainStr( pStr1, pStr2[i] );
|
||||
if (ret)
|
||||
if (Util.isArray(pStr2)) {
|
||||
n = pStr2.length;
|
||||
|
||||
for(i = 0; i < n; i++) {
|
||||
str = pStr2[i];
|
||||
ret = Util.isContainStr(pStr1, str);
|
||||
|
||||
if (lRet)
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
else if ( Util.isString(pStr2) )
|
||||
lRet = pStr1.indexOf(pStr2) >= 0;
|
||||
}
|
||||
} else if (Util.isString(pStr2))
|
||||
ret = pStr1.indexOf(pStr2) >= 0;
|
||||
|
||||
return lRet;
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -438,21 +418,19 @@
|
|||
* @param pArg
|
||||
*/
|
||||
this.log = function() {
|
||||
var lArg = arguments,
|
||||
lConsole = Scope.console,
|
||||
lDate = '[' + Util.getDate() + '] ',
|
||||
var args = this.slice(arguments),
|
||||
console = Scope.console,
|
||||
lDate = '[' + Util.getDate() + '] ';
|
||||
|
||||
lUnShift = Util.bind([].unshift, lArg),
|
||||
lShift = Util.bind([].shift, lArg),
|
||||
lJoin = Util.bind([].join, lArg);
|
||||
|
||||
if (lConsole && lArg.length && lArg[0]) {
|
||||
lUnShift(lDate);
|
||||
lConsole.log.apply(lConsole, lArg);
|
||||
lShift();
|
||||
if (console && args.length && args[0]) {
|
||||
args.unshift(lDate);
|
||||
|
||||
console.log.apply(console, args);
|
||||
|
||||
args.shift();
|
||||
}
|
||||
|
||||
return lJoin(' ');
|
||||
return args.join(' ');
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -490,24 +468,19 @@
|
|||
|
||||
/**
|
||||
* load functions thrue callbacks one-by-one
|
||||
* @param pFunc_a {Array} - array of functions
|
||||
* @param pData - not necessarily
|
||||
* @param funcs {Array} - array of functions
|
||||
*/
|
||||
this.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);
|
||||
};
|
||||
this.loadOnLoad = function(funcs) {
|
||||
var func, callback;
|
||||
|
||||
if (Util.isArray(funcs)) {
|
||||
func = funcs.shift();
|
||||
|
||||
if ( !Util.isUndefined(pData) )
|
||||
pData = {
|
||||
data : pData,
|
||||
callback : lCallBack
|
||||
};
|
||||
callback = function(pData) {
|
||||
return Util.loadOnLoad(funcs);
|
||||
};
|
||||
|
||||
Util.exec(lFunc , pData || lCallBack);
|
||||
Util.exec(func, callback);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -581,9 +554,10 @@
|
|||
|
||||
|
||||
this.escapeRegExp = function(pStr) {
|
||||
var lRet = pStr;
|
||||
var lRet = pStr,
|
||||
isStr = Util.isString(pStr);
|
||||
|
||||
if ( Util.isString(pStr) )
|
||||
if (isStr)
|
||||
lRet = pStr.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
||||
|
||||
return lRet;
|
||||
|
|
@ -749,38 +723,17 @@
|
|||
|
||||
/**
|
||||
* return save exec function
|
||||
* @param pCallBack
|
||||
* @param pArg
|
||||
* @param callback
|
||||
*/
|
||||
this.retExec = function() {
|
||||
var args = arguments;
|
||||
var result,
|
||||
exec = Util.exec.bind(Util),
|
||||
args = Util.slice(arguments);
|
||||
|
||||
return function() {
|
||||
var argsLocal, callback,
|
||||
n = args.length;
|
||||
|
||||
if (n > 1)
|
||||
argsLocal = args;
|
||||
else {
|
||||
callback = args[0];
|
||||
argsLocal = arguments;
|
||||
[].unshift.call(argsLocal, callback);
|
||||
}
|
||||
|
||||
Util.exec.apply(null, argsLocal);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* return function wich exec function in params
|
||||
* @param arguments: callback, args
|
||||
*/
|
||||
this.retFunc = function() {
|
||||
var args = arguments;
|
||||
args.unshift(exec);
|
||||
result = Util.bind.apply(null, args);
|
||||
|
||||
return function() {
|
||||
return Util.exec.apply(null, args);
|
||||
};
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -811,52 +764,19 @@
|
|||
};
|
||||
|
||||
/**
|
||||
* set value to property of object, if object exist
|
||||
* @param pArgs {object, property, value}
|
||||
* function makes new array based on first
|
||||
*
|
||||
* @param array
|
||||
*/
|
||||
this.setValue = function(pArgs) {
|
||||
var lRet = false;
|
||||
this.slice = function(array, count) {
|
||||
var ret;
|
||||
|
||||
if ( Util.isObject(pArgs) ) {
|
||||
var lObj = pArgs.object,
|
||||
lProp = pArgs.property,
|
||||
lVal = pArgs.lVal;
|
||||
|
||||
if (lObj) {
|
||||
lObj[lProp] = lVal;
|
||||
lRet = true;
|
||||
}
|
||||
}
|
||||
if (array)
|
||||
ret = [].slice.call(array, count);
|
||||
|
||||
return lRet;
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* set timout before callback would be called
|
||||
* @param pArgs {func, callback, time}
|
||||
*/
|
||||
this.setTimeout = function(pArgs) {
|
||||
var lDone,
|
||||
lFunc = pArgs.func,
|
||||
lTime = pArgs.time || 1000,
|
||||
lCallBack = function(pArgument) {
|
||||
if (!lDone) {
|
||||
lDone = Util.exec(pArgs.callback, pArgument);
|
||||
}
|
||||
};
|
||||
|
||||
var lTimeoutFunc = function() {
|
||||
setTimeout(function() {
|
||||
Util.exec(lFunc, lCallBack);
|
||||
if (!lDone)
|
||||
lTimeoutFunc();
|
||||
}, lTime);
|
||||
};
|
||||
|
||||
lTimeoutFunc();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* function execute param function in
|
||||
* try...catch block
|
||||
|
|
@ -928,23 +848,14 @@
|
|||
* ...
|
||||
* @param pArgN
|
||||
*/
|
||||
this.exec = function(pCallBack) {
|
||||
var lRet, lCallBack;
|
||||
this.exec = function(callback) {
|
||||
var ret,
|
||||
args = Util.slice(arguments, 1);
|
||||
|
||||
if (Util.isFunction(callback))
|
||||
ret = callback.apply(null, args);
|
||||
|
||||
/* drop first element */
|
||||
[].shift.call(arguments);
|
||||
if (pCallBack) {
|
||||
if (Util.isFunction(pCallBack))
|
||||
lRet = pCallBack.apply(null, arguments);
|
||||
else {
|
||||
lCallBack = pCallBack.callback || pCallBack.success;
|
||||
/* add first element */
|
||||
[].unshift.call(arguments, lCallBack);
|
||||
lRet = Util.exec.apply(null, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
return lRet;
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -952,14 +863,15 @@
|
|||
* @pArg
|
||||
*/
|
||||
this.execIfExist = function(pObj, pName, pArg) {
|
||||
var lRet;
|
||||
var ret, bind,
|
||||
func = pObj && pObj[pName];
|
||||
|
||||
if (pObj) {
|
||||
var lFunc = Util.bind(pObj[pName], pObj);
|
||||
lRet = Util.exec(lFunc, pArg);
|
||||
if (func) {
|
||||
func = func.bind(pObj);
|
||||
ret = Util.exec(func, pArg);
|
||||
}
|
||||
|
||||
return lRet;
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "cloudcmd",
|
||||
"version": "0.7.0",
|
||||
"version": "0.8.0",
|
||||
"author": "coderaiser <mnemonic.enemy@gmail.com> (https://github.com/coderaiser)",
|
||||
"description": "Cloud Commander - file manager with console and editor",
|
||||
"homepage": "http://cloudcmd.io",
|
||||
|
|
@ -18,9 +18,11 @@
|
|||
"subdomain": "cloudcmd",
|
||||
"dependencies": {
|
||||
"dropbox": "0.10.2",
|
||||
"minify": "0.2.3",
|
||||
"minify": "0.2.5",
|
||||
"socket.io": "0.9.16",
|
||||
"express": "3.4.x"
|
||||
"express": "3.4.x",
|
||||
"http-auth": "2.1.x",
|
||||
"fs-extra": "0.8.x"
|
||||
},
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
"browser" : true,
|
||||
"devel" : true,
|
||||
"eqeqeq" : true,
|
||||
"globalstrict" : true,
|
||||
"jquery" : false,
|
||||
"newcap" : true,
|
||||
"noarg" : true,
|
||||
|
|
|
|||
|
|
@ -1,22 +1,25 @@
|
|||
<li draggable class>
|
||||
<div class="fm-header">
|
||||
<span class="mini-icon "></span>
|
||||
<span class="name reduce-text">name</span>
|
||||
<span class="size reduce-text">size</span>
|
||||
<span class="owner reduce-text">owner</span>
|
||||
<span class="mode reduce-text">mode</span>
|
||||
</div><ul class="files"><li draggable class="">
|
||||
<span class="mini-icon directory"></span>
|
||||
<span class=name>
|
||||
<a href="/fs/etc" title=".." target="" draggable=true>..</a>
|
||||
</span>
|
||||
<span class=size><dir></span><span class=owner>.</span>
|
||||
<span class=mode></span>
|
||||
</li><li draggable class>
|
||||
<span class="name reduce-text"><a href="/fs/etc" title=".." target="" draggable=true>..</a></span>
|
||||
<span class="size reduce-text"><dir></span>
|
||||
<span class="owner reduce-text">.</span>
|
||||
<span class="mode reduce-text">--- --- ---</span>
|
||||
</li><li draggable class="">
|
||||
<span class="mini-icon directory"></span>
|
||||
<span class=name>
|
||||
<a href="/fs/etc/X11/applnk" title="applnk" target="" draggable=true>applnk</a>
|
||||
</span>
|
||||
<span class=size><dir></span><span class=owner>root</span>
|
||||
<span class=mode>rwx r-x r-x</span>
|
||||
</li><li draggable class>
|
||||
<span class="name reduce-text"><a href="/fs/etc/X11/applnk" title="applnk" target="" draggable=true>applnk</a></span>
|
||||
<span class="size reduce-text"><dir></span>
|
||||
<span class="owner reduce-text">root</span>
|
||||
<span class="mode reduce-text">rwx r-x r-x</span>
|
||||
</li><li draggable class="">
|
||||
<span class="mini-icon text-file"></span>
|
||||
<span class=name>
|
||||
<a href="/fs/etc/X11/prefdm" title="prefdm" target="_blank" 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>
|
||||
<span class="name reduce-text"><a href="/fs/etc/X11/prefdm" title="prefdm" target="_blank" draggable=true>prefdm</a></span>
|
||||
<span class="size reduce-text">1.30kb</span>
|
||||
<span class="owner reduce-text">root</span>
|
||||
<span class="mode reduce-text">rwx r-x r-x</span>
|
||||
</li></ul>
|
||||