From cd1aaa3b375c26f21fc70a171a9f71a5e61b11a1 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 2 Oct 2012 04:27:55 -0400 Subject: [PATCH 001/281] minor changes --- config.json | 2 +- css/style.css | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/config.json b/config.json index 996ae14f..898abb06 100644 --- a/config.json +++ b/config.json @@ -1,6 +1,6 @@ { "cache" : {"allowed" : false}, - "appcache" : true, + "appcache" : false, "minification" : { "js" : true, "css" : true, diff --git a/css/style.css b/css/style.css index c8f06f7a..4afca18b 100644 --- a/css/style.css +++ b/css/style.css @@ -36,7 +36,8 @@ } body{ - font:16px "Droid Sans Mono"; + font:16px "Droid Sans Mono"; + background-color:white; } .menu{ From 1168477155eee2f4abaaaa4c9e9589be66bf1d2b Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 2 Oct 2012 05:58:44 -0400 Subject: [PATCH 002/281] fixed bug with appcache config --- ChangeLog | 6 ++++++ server.js | 7 +++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 57c811fe..064bf96a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2012.10.*, Version 0.1.8 + +* Fixed bug with appcache config. Windows reads file +not just like Linux. + + 2012.10.01, Version 0.1.7 * Changed name of menu files, fixed npm and jitsu diff --git a/server.js b/server.js index c61806e8..5cd6989c 100644 --- a/server.js +++ b/server.js @@ -677,8 +677,11 @@ CloudServer.indexReaded = function(pList){ pIndex = pIndex.replace('Cloud Commander', '' + CloudFunc.setTitle() + ''); - if(!CloudServer.Config.appcache) - pIndex = pIndex.replace(' manifest="/cloudcmd.appcache"', ''); + if(!CloudServer.Config.appcache){ + if(process.platform === 'win32') + pIndex = pIndex.replace(' manifest=/cloudcmd.appcache', ''); + else pIndex = pIndex.replace(' manifest="/cloudcmd.appcache"', ''); + } var lHeader; /* если браузер поддерживает gzip-сжатие*/ From 2641e707cba315063201577986d5c1d786fb6a3a Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 2 Oct 2012 09:14:05 -0400 Subject: [PATCH 003/281] minor changes --- server.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/server.js b/server.js index 5cd6989c..9e9e48be 100644 --- a/server.js +++ b/server.js @@ -657,6 +657,7 @@ CloudServer.indexReaded = function(pList){ return console.log(pError); } + var lWin32 = process.platform === 'win32'; /* и сохраняем в кэш */ CloudServer.Cache.set(CloudServer.INDEX, pIndex); @@ -666,9 +667,16 @@ CloudServer.indexReaded = function(pList){ * меняем в index.html обычные css на * минифицированый */ - if(CloudServer.Minify._allowed.css) - pIndex = pIndex.replace('', '') - .replace('/css/style.css', CloudServer.Minify.MinFolder + 'all.min.css'); + if(CloudServer.Minify._allowed.css){ + var lReplace_s = ''; + else + lReplace_s = lReplace_s + '"/css/reset.css>"'; + + pIndex = pIndex.replace(lReplace_s, ''); + pIndex = pIndex.replace('/css/style.css', CloudServer.Minify.MinFolder + 'all.min.css'); + } pIndex = pIndex.toString().replace('
', '
'+pList); @@ -678,9 +686,10 @@ CloudServer.indexReaded = function(pList){ '' + CloudFunc.setTitle() + ''); if(!CloudServer.Config.appcache){ - if(process.platform === 'win32') + if(!lWin32) pIndex = pIndex.replace(' manifest=/cloudcmd.appcache', ''); - else pIndex = pIndex.replace(' manifest="/cloudcmd.appcache"', ''); + else + pIndex = pIndex.replace(' manifest="/cloudcmd.appcache"', ''); } var lHeader; From 3b26faa3d26779f3e74488ff49707ef5446738a5 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 2 Oct 2012 09:47:19 -0400 Subject: [PATCH 004/281] minor changes --- lib/server/socket.js | 19 +++++++++++++++++++ server.js | 9 +++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/lib/server/socket.js b/lib/server/socket.js index 28ef5d4f..c29329eb 100644 --- a/lib/server/socket.js +++ b/lib/server/socket.js @@ -41,6 +41,25 @@ function onMessage(pConnNum, pSocket){ return function(pCommand) { console.log(pCommand); + if( pCommand.indexOf('cloudcmd') === 0 ){ + pCommand = pCommand.replace('cloudcmd', ''); + + if(pCommand.indexOf(' exit') === 0){ + pCommand = 'kill -9 ' + process.pid; + } + else { + var lMsg = { + stdout : 'cloudcmd exit - for shutdown cloudcmd', + stderr : null + }; + + lMsg = JSON.stringify(lMsg); + pSocket.send(lMsg); + + return; + } + } + /* change code page to unicode */ if(Win32_b) pCommand = 'chcp 65001 |' + pCommand; diff --git a/server.js b/server.js index 9e9e48be..c725eb66 100644 --- a/server.js +++ b/server.js @@ -656,13 +656,14 @@ CloudServer.indexReaded = function(pList){ if(pError){ return console.log(pError); } - - var lWin32 = process.platform === 'win32'; + /* и сохраняем в кэш */ CloudServer.Cache.set(CloudServer.INDEX, pIndex); pIndex = pIndex.toString(); + var lWin32 = process.platform === 'win32'; + /* если выбрана опция минифизировать скрпиты * меняем в index.html обычные css на * минифицированый @@ -672,7 +673,7 @@ CloudServer.indexReaded = function(pList){ if(lWin32) lReplace_s = lReplace_s + '/css/reset.css>'; else - lReplace_s = lReplace_s + '"/css/reset.css>"'; + lReplace_s = lReplace_s + '"/css/reset.css">'; pIndex = pIndex.replace(lReplace_s, ''); pIndex = pIndex.replace('/css/style.css', CloudServer.Minify.MinFolder + 'all.min.css'); @@ -686,7 +687,7 @@ CloudServer.indexReaded = function(pList){ '' + CloudFunc.setTitle() + ''); if(!CloudServer.Config.appcache){ - if(!lWin32) + if(lWin32) pIndex = pIndex.replace(' manifest=/cloudcmd.appcache', ''); else pIndex = pIndex.replace(' manifest="/cloudcmd.appcache"', ''); From 2b28a44facbf430677582f7f08abde33498f74de Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 2 Oct 2012 10:05:06 -0400 Subject: [PATCH 005/281] minor changes --- client.js | 26 ++++++++++++++++++++------ config.json | 2 +- lib/client/editor/_codemirror.js | 12 +++++++----- lib/client/terminal.js | 18 ++++++++++-------- 4 files changed, 38 insertions(+), 20 deletions(-) diff --git a/client.js b/client.js index bef3f31b..794e5c94 100644 --- a/client.js +++ b/client.js @@ -142,15 +142,26 @@ CloudClient.Util = (function(){ var lXMLHTTP; this.addClass = function(pElement, pClass){ + var lRet_b = true; + var lClassList = pElement.classList; - if(lClassList) - lClassList.add(pClass); + if(lClassList){ + if( !lClassList.contains(pClass) ) + lClassList.add(pClass); + else + lRet_b = false; + } else{ var lSpaceChar = ''; if(pElement.className) lSpaceChar = ' '; - pElement.className += lSpaceChar + 'hidden'; + if( !pElement.contains(pClass) ) + pElement.className += lSpaceChar + pClass; + else + lRet_b = false; } + + return lRet_b; }; this.ajax = function(pParams){ @@ -584,7 +595,7 @@ CloudClient.Util = (function(){ }, hideLoad : function(){ - lLoadingImage = LImages_o.loading(); + lLoadingImage = LImages_o.loading(); Util.hide(lLoadingImage); }, @@ -791,14 +802,17 @@ CloudClient.Util = (function(){ }; this.hidePanel = function(pActive){ + var lRet_b = false; var lPanel = lThis.getPanel(pActive); if(lPanel) - this.hide(lPanel); + lRet_b = this.hide(lPanel); + + return lRet_b; }; this.hide = function(pElement){ - return this.addClass(pElement, 'hidden'); + return this.addClass(pElement, 'hidden'); }; this.removeClass = function(pElement, pClass){ diff --git a/config.json b/config.json index 898abb06..75bcf6c6 100644 --- a/config.json +++ b/config.json @@ -2,7 +2,7 @@ "cache" : {"allowed" : false}, "appcache" : false, "minification" : { - "js" : true, + "js" : false, "css" : true, "html" : true, "img" : true diff --git a/lib/client/editor/_codemirror.js b/lib/client/editor/_codemirror.js index dc61b604..6e05c9eb 100644 --- a/lib/client/editor/_codemirror.js +++ b/lib/client/editor/_codemirror.js @@ -151,12 +151,14 @@ var CloudCommander, CloudFunc, CodeMirror; if(typeof data === 'object') data = JSON.stringify(data, null, 4); - initCodeMirror(data); + var lHided = Util.hidePanel(); + if(lHided){ + initCodeMirror(data); + + /* removing keyBinding if set */ + KeyBinding.unSet(); + } - /* removing keyBinding if set */ - KeyBinding.unSet(); - - Util.hidePanel(); Util.Images.hideLoad(); Loading = false; diff --git a/lib/client/terminal.js b/lib/client/terminal.js index 73e2d756..5303ebb1 100644 --- a/lib/client/terminal.js +++ b/lib/client/terminal.js @@ -66,14 +66,16 @@ var CloudCommander, $; JqueryTerminal.show = (function(){ Util.Images.hideLoad(); - Hidden = false; - - Util.hidePanel(); - Util.show(TerminalId); - - KeyBinding.unSet(); - - Term.resume(); + Hidden = false; + /* only if panel was hided */ + var lHided = Util.hidePanel(); + if(lHided){ + Util.show(TerminalId); + + KeyBinding.unSet(); + + Term.resume(); + } }); JqueryTerminal.hide = (function(){ From 188651cb7a40dd6c994e73a4f822aecc8d03055a Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 2 Oct 2012 10:06:37 -0400 Subject: [PATCH 006/281] fixed bug with showing terminal and viewer at the same time --- ChangeLog | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 064bf96a..b503afa5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,13 @@ 2012.10.*, Version 0.1.8 -* Fixed bug with appcache config. Windows reads file -not just like Linux. +* Added ability to shutdown Cloud Commander +thrue terminal command: "cloudcmd exit" + +* Fixed bug with showing terminal and viewer +at the same time. + +* Fixed bug with appcache config. Windows reads +file not just like Linux. 2012.10.01, Version 0.1.7 From b45af0c0ddce35071d19468711c7f795e23fbba6 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 2 Oct 2012 11:24:38 -0400 Subject: [PATCH 007/281] refactored CodeMirror editor plagin --- lib/client/editor/_codemirror.js | 30 +++++++++++++++++++++--------- lib/client/terminal.js | 26 ++++++++++++++++++++------ lib/server/socket.js | 2 +- 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/lib/client/editor/_codemirror.js b/lib/client/editor/_codemirror.js index 6e05c9eb..840e06a8 100644 --- a/lib/client/editor/_codemirror.js +++ b/lib/client/editor/_codemirror.js @@ -21,8 +21,14 @@ var CloudCommander, CloudFunc, CodeMirror; }) }; + cloudcmd.Editor.dir = 'lib/client/editor/'; + CodeMirrorEditor.dir = cloudcmd.Editor.dir + 'codemirror/'; /* private functions */ + + /** + * function initialize CodeMirror + */ function initCodeMirror(pValue){ if(!FM) FM = Util.getById('fm'); @@ -50,11 +56,10 @@ var CloudCommander, CloudFunc, CodeMirror; }); } - cloudcmd.Editor.dir = 'lib/client/editor/'; - CodeMirrorEditor.dir = cloudcmd.Editor.dir + 'codemirror/'; - - /* function loads CodeMirror js and css files */ - CodeMirrorEditor.load = (function(){ + /** + * function loads CodeMirror js and css files + */ + function load(){ /* function loads css files of CodeMirror */ var loadAll = function() { Util.cssLoad([ @@ -88,13 +93,15 @@ var CloudCommander, CloudFunc, CodeMirror; /* load CodeMirror main module */ Util.jsload(CodeMirrorEditor.dir + 'codemirror.js', loadAll); - }); + } - /* function shows CodeMirror editor */ + /** + * function shows CodeMirror editor + */ CodeMirrorEditor.show = (function(){ /* if CodeMirrorEditor is not loaded - loading him */ if(!CodeMirrorLoaded) - return this.load(); + return load(); /* if CodeMirror function show already * called do not call it again @@ -166,7 +173,9 @@ var CloudCommander, CloudFunc, CodeMirror; }); }); - /* function hides CodeMirror editor */ + /** + * function hides CodeMirror editor + */ CodeMirrorEditor.hide = (function() { var lElem = CodeMirrorElement; KeyBinding.set(); @@ -177,6 +186,9 @@ var CloudCommander, CloudFunc, CodeMirror; Util.showPanel(); }); + /** + * function bind keys + */ cloudcmd.Editor.Keys = (function(pIsReadOnly){ ReadOnly = pIsReadOnly; diff --git a/lib/client/terminal.js b/lib/client/terminal.js index 5303ebb1..09d5a3f7 100644 --- a/lib/client/terminal.js +++ b/lib/client/terminal.js @@ -11,12 +11,14 @@ var CloudCommander, $; TerminalId, Term, Hidden = false; - + cloudcmd.Terminal = {}; var JqueryTerminal = {}; - + /** + * function loads jquery-terminal + */ JqueryTerminal.load = (function(){ Util.cssLoad({ src : 'lib/client/terminal/jquery-terminal/jquery.terminal.css' @@ -48,6 +50,9 @@ var CloudCommander, $; }); + /** + * function do basic initialization + */ JqueryTerminal.init = (function(){ if(!TerminalId){ var lFM = Util.getById('fm'); @@ -63,13 +68,16 @@ var CloudCommander, $; console.log('Error. Something went wrong FM not found'); }); + /** + * functin show jquery-terminal + */ JqueryTerminal.show = (function(){ Util.Images.hideLoad(); - Hidden = false; /* only if panel was hided */ var lHided = Util.hidePanel(); if(lHided){ + Hidden = false; Util.show(TerminalId); KeyBinding.unSet(); @@ -78,6 +86,9 @@ var CloudCommander, $; } }); + /** + * function hide jquery-terminal + */ JqueryTerminal.hide = (function(){ Hidden = true; @@ -90,6 +101,9 @@ var CloudCommander, $; }); + /** + * function bind keys + */ cloudcmd.Terminal.Keys = (function(){ /* loading js and css*/ Util.jqueryLoad( JqueryTerminal.load ); @@ -105,15 +119,15 @@ var CloudCommander, $; else if(!Hidden && event.keyCode === cloudcmd.KEY.ESC) JqueryTerminal.hide(); - + }; - + /* добавляем обработчик клавишь */ if (document.addEventListener) document.addEventListener('keydown', key_event, false); - else + else document.onkeypress = key_event; }); diff --git a/lib/server/socket.js b/lib/server/socket.js index c29329eb..6d4bc8f8 100644 --- a/lib/server/socket.js +++ b/lib/server/socket.js @@ -15,7 +15,7 @@ exports.listen = function(pServer){ /* number of connections */ var lConnNum = 0; - io.sockets.on('connection', function (socket) { + io.sockets.on('connection', function (socket){ ++lConnNum; socket.send('{"stdout":"client connected"}'); From 4cad10f750a938202dcd55bc69f9c88559b57db5 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 2 Oct 2012 11:25:30 -0400 Subject: [PATCH 008/281] minor changes --- lib/client/editor/_codemirror.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/client/editor/_codemirror.js b/lib/client/editor/_codemirror.js index 840e06a8..b36b26ef 100644 --- a/lib/client/editor/_codemirror.js +++ b/lib/client/editor/_codemirror.js @@ -98,7 +98,7 @@ var CloudCommander, CloudFunc, CodeMirror; /** * function shows CodeMirror editor */ - CodeMirrorEditor.show = (function(){ + CodeMirrorEditor.show = function(){ /* if CodeMirrorEditor is not loaded - loading him */ if(!CodeMirrorLoaded) return load(); @@ -171,12 +171,12 @@ var CloudCommander, CloudFunc, CodeMirror; Loading = false; } }); - }); + }; /** * function hides CodeMirror editor */ - CodeMirrorEditor.hide = (function() { + CodeMirrorEditor.hide = function() { var lElem = CodeMirrorElement; KeyBinding.set(); @@ -184,12 +184,12 @@ var CloudCommander, CloudFunc, CodeMirror; FM.removeChild(lElem); Util.showPanel(); - }); + }; /** * function bind keys */ - cloudcmd.Editor.Keys = (function(pIsReadOnly){ + cloudcmd.Editor.Keys = function(pIsReadOnly){ ReadOnly = pIsReadOnly; var lThis = this.CodeMirror; @@ -238,7 +238,7 @@ var CloudCommander, CloudFunc, CodeMirror; key_event(); }; } - }); + }; cloudcmd.Editor.CodeMirror = CodeMirrorEditor; })(); \ No newline at end of file From f1bd5aff502ef8919ff205bf5e3ec4e92115eb99 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 3 Oct 2012 04:28:13 -0400 Subject: [PATCH 009/281] added update module --- cloudcmd.js | 9 +++++++-- lib/server/update.js | 26 ++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 lib/server/update.js diff --git a/cloudcmd.js b/cloudcmd.js index 613c07d2..453b1cac 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -1,12 +1,17 @@ "use strict"; -var Server = cloudRequire('./server'), + +var LIBDIRSERVER = './lib/server/', + Server = cloudRequire('./server'), path = cloudRequire('path'), - fs = cloudRequire('fs'); + fs = cloudRequire('fs'), + update = cloudRequire(LIBDIRSERVER + 'update'); var Config = readConfig(); Config ? Server.start(Config) : Server.start(); +update.get(); + function readConfig(){ diff --git a/lib/server/update.js b/lib/server/update.js new file mode 100644 index 00000000..8b30c799 --- /dev/null +++ b/lib/server/update.js @@ -0,0 +1,26 @@ +/* module update cloud commander */ + +var exec = require('child_process').exec; + +exports.get = function(){ + exec('git pull', pull); +}; + +/** + * function pulls cloud cmd content from repo + * @param pError + * @param pStdout + * @param pStderr + */ +function pull(pError, pStdout, pStderr){ + if (pError !== null) { + console.log('exec error: ' + pError); + } + + var lExec = { + stdout : pStdout, + stderr : pStderr || pError + }; + + console.log(lExec); +} \ No newline at end of file From 2162ecd2e2a9c9d4dd672bbae2ac4f9b0200d4af Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 3 Oct 2012 04:33:06 -0400 Subject: [PATCH 010/281] added ability to update automaticaly --- ChangeLog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog b/ChangeLog index b503afa5..b21640fe 100644 --- a/ChangeLog +++ b/ChangeLog @@ -9,6 +9,8 @@ at the same time. * Fixed bug with appcache config. Windows reads file not just like Linux. +* Added ability to update thrue git. + 2012.10.01, Version 0.1.7 From 86a1504cd913efb8079ddcd3d89c9c89b76c36c7 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 3 Oct 2012 05:22:23 -0400 Subject: [PATCH 011/281] Added ability to change charset on terminal only if it's build in command on win32 --- ChangeLog | 3 +++ client.js | 27 +++++++++++++++++++++------ lib/client/socket.js | 7 ++++++- lib/server/socket.js | 32 ++++++++++++++++++++++++++++---- 4 files changed, 58 insertions(+), 11 deletions(-) diff --git a/ChangeLog b/ChangeLog index b21640fe..611e20d8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -11,6 +11,9 @@ file not just like Linux. * Added ability to update thrue git. +* Added ability to change charset on terminal only if +it's build in command on win32. + 2012.10.01, Version 0.1.7 diff --git a/client.js b/client.js index 794e5c94..a4e2a1ee 100644 --- a/client.js +++ b/client.js @@ -350,9 +350,9 @@ CloudClient.Util = (function(){ };*/ /* ie */ /* if object - then onload or onerror */ - }else if (typeof lFunc === 'object') { + }else if ( this.isObject(lFunc) ) { if(lFunc.onload && - typeof lFunc.onload === 'function'){ + this.isFunction(lFunc.onload)){ element.onload = lFunc.onload; /* element.onreadystatechange = function(){ @@ -403,9 +403,8 @@ CloudClient.Util = (function(){ if(typeof lFunc === 'function') lFunc(); - else if(typeof lFunc === 'object' && - typeof lFunc.onload === 'function') - lFunc.onload(); + else if( this.isObject(lFunc) && this.isFunction(lFunc.onload) ) + lFunc.onload(); } return element; @@ -728,6 +727,22 @@ CloudClient.Util = (function(){ return ( lCurrentFileClass.indexOf(lCurrentClass) >= 0 ); }; + /** + * functions check is pObject is object + * @param pObject + */ + this.isObject = function(pObject){ + return typeof pObject === 'object'; + }; + + /** + * functions check is pFunction is function + * @param pFunction + */ + this.isFunction = function(pFunction){ + return typeof pFunction === 'function'; + }; + this.getCurrentLink = function(pCurrentFile){ var lLink; if(pCurrentFile) @@ -1510,7 +1525,7 @@ CloudClient._getJSONfromFileTable=function() */ i=2; /* пропускам Path и Header*/ - for(;i 0 ) + pCommand = 'chcp 65001 |' + pCommand; + } if(!ClientFuncs[pConnNum]) ClientFuncs[pConnNum] = getExec(pSocket); @@ -95,3 +101,21 @@ function getExec(pSocket){ console.log(lExec); }; } + + +/* windows commands thet require + * unicode charset on locales + * different then English + */ +var Win32Commands = ['ASSOC', 'AT', 'ATTRIB', 'BREAK', 'CACLS', 'CALL', + 'CD', 'CHCP', 'CHDIR', 'CHKDSK', 'CHKNTFS', 'CLS', + 'CMD', 'COLOR', 'COMP', 'COMPACT', 'CONVERT', 'COPY', + 'DATE', 'DEL', 'DIR', 'DISKCOMP', 'DISKCOPY', 'DOSKEY', + 'ECHO', 'ENDLOCAL', 'ERASE', 'EXIT', 'FC', 'FIND', + 'FINDSTR', 'FOR', 'FORMAT', 'FTYPE', 'GOTO', 'GRAFTABL', + 'HELP', 'IF', 'LABEL', 'MD', 'MKDIR', 'MODE', 'MORE', + 'MOVE', 'PATH', 'PAUSE', 'POPD', 'PRINT', 'PROMPT', + 'PUSHD', 'RD', 'RECOVER', 'REM', 'REN', 'RENAME', + 'REPLACE', 'RMDIR', 'SET', 'SETLOCAL', 'SHIFT', 'SORT', + 'START', 'SUBST', 'TIME', 'TITLE', 'TREE', 'TYPE', + 'VER', 'VERIFY', 'VOL', 'XCOPY']; \ No newline at end of file From 53e0858c3b0e2c56f09fa025158fa7adcafbdb2b Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 3 Oct 2012 05:33:01 -0400 Subject: [PATCH 012/281] minor changes --- cloudcmd.js | 5 ++++- lib/server/update.js | 33 +++++++++++++++++++++++++++------ 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 453b1cac..9fdb6b50 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -69,7 +69,10 @@ function writeLogsToFile(){ })(process.stdout.write); } -/* function do safe require of needed module */ +/** + * function do safe require of needed module + * @param pModule + */ function cloudRequire(pModule){ try{ return require(pModule); diff --git a/lib/server/update.js b/lib/server/update.js index 8b30c799..5e69bb77 100644 --- a/lib/server/update.js +++ b/lib/server/update.js @@ -1,6 +1,7 @@ /* module update cloud commander */ -var exec = require('child_process').exec; +var exec = require('child_process').exec, + packagejson = cloudRequire('package.json'); exports.get = function(){ exec('git pull', pull); @@ -13,14 +14,34 @@ exports.get = function(){ * @param pStderr */ function pull(pError, pStdout, pStderr){ - if (pError !== null) { - console.log('exec error: ' + pError); + + if(!pError){ + if(pStdout !== 'Already up-to-date.\n'){ + pStdout = 'Cloud Commander updated. Restart to use new version.'; + } + else pStdout = 'Cloud Commander is up to date'; + + if(packagejson) + pStdout = 'Version ' + packagejson.version + '\n' + pStdout; } - + var lExec = { stdout : pStdout, stderr : pStderr || pError - }; - + }; + console.log(lExec); +} + +/** + * function do safe require of needed module + * @param pModule + */ +function cloudRequire(pModule){ + try{ + return require(pModule); + } + catch(pError){ + return false; + } } \ No newline at end of file From 97ad4a8d07c1f308d82194d860e300cc09ffdf85 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 3 Oct 2012 05:33:35 -0400 Subject: [PATCH 013/281] minor changes --- lib/server/update.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/server/update.js b/lib/server/update.js index 5e69bb77..5ff76288 100644 --- a/lib/server/update.js +++ b/lib/server/update.js @@ -19,7 +19,7 @@ function pull(pError, pStdout, pStderr){ if(pStdout !== 'Already up-to-date.\n'){ pStdout = 'Cloud Commander updated. Restart to use new version.'; } - else pStdout = 'Cloud Commander is up to date'; + else pStdout = 'Cloud Commander is up to date.'; if(packagejson) pStdout = 'Version ' + packagejson.version + '\n' + pStdout; From 3594542099a92d34982d92512c50a77cf8356aab Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 3 Oct 2012 05:41:35 -0400 Subject: [PATCH 014/281] minor changes --- lib/server/socket.js | 33 ++++++++++++++++++++++++++++----- lib/server/update.js | 7 ++++++- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/lib/server/socket.js b/lib/server/socket.js index 07e0763e..a085c7f9 100644 --- a/lib/server/socket.js +++ b/lib/server/socket.js @@ -1,7 +1,9 @@ /* module make possible connectoin thrue socket.io on a server */ -var io = require('socket.io'), - exec = require('child_process').exec, +var LIBDIRSERVER = './lib/server/', + io = require('socket.io'), + exec = require('child_process').exec, + update = cloudRequire(LIBDIRSERVER + 'update'), ClientFuncs = [], OnMessageFuncs = [], Win32_b = process.platform === 'win32'; @@ -44,8 +46,16 @@ function onMessage(pConnNum, pSocket){ if( pCommand.indexOf('cloudcmd') === 0 ){ pCommand = pCommand.replace('cloudcmd', ''); - if(pCommand.indexOf(' exit') === 0){ - pCommand = 'kill -9 ' + process.pid; + if(pCommand.indexOf(' ') === 0){ + pCommand = pCommand.replace(' ',''); + + if(pCommand.indexOf('update') === 0){ + if(update) + update.get(); + } + + if(pCommand.indexOf('exit') === 0) + pCommand = 'kill -9 ' + process.pid; } else { var lMsg = { @@ -118,4 +128,17 @@ var Win32Commands = ['ASSOC', 'AT', 'ATTRIB', 'BREAK', 'CACLS', 'CALL', 'PUSHD', 'RD', 'RECOVER', 'REM', 'REN', 'RENAME', 'REPLACE', 'RMDIR', 'SET', 'SETLOCAL', 'SHIFT', 'SORT', 'START', 'SUBST', 'TIME', 'TITLE', 'TREE', 'TYPE', - 'VER', 'VERIFY', 'VOL', 'XCOPY']; \ No newline at end of file + 'VER', 'VERIFY', 'VOL', 'XCOPY']; + + /** + * function do safe require of needed module + * @param pModule + */ +function cloudRequire(pModule){ + try{ + return require(pModule); + } + catch(pError){ + return false; + } +} \ No newline at end of file diff --git a/lib/server/update.js b/lib/server/update.js index 5ff76288..5704b0f5 100644 --- a/lib/server/update.js +++ b/lib/server/update.js @@ -3,7 +3,11 @@ var exec = require('child_process').exec, packagejson = cloudRequire('package.json'); -exports.get = function(){ +/** + * function gets update + * @param pCallBack + */ +exports.get = function(pCallBack){ exec('git pull', pull); }; @@ -21,6 +25,7 @@ function pull(pError, pStdout, pStderr){ } else pStdout = 'Cloud Commander is up to date.'; + console.log(packagejson); if(packagejson) pStdout = 'Version ' + packagejson.version + '\n' + pStdout; } From a55d1033959507cbf80856c2e0de5219a2573caa Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 3 Oct 2012 05:47:26 -0400 Subject: [PATCH 015/281] minor changes --- cloudcmd.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cloudcmd.js b/cloudcmd.js index 9fdb6b50..54334b75 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -10,7 +10,8 @@ var Config = readConfig(); Config ? Server.start(Config) : Server.start(); -update.get(); +if(update) + update.get(); function readConfig(){ From 9ea012410ac3eaf157105cd617b84234d369acb8 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 3 Oct 2012 07:32:29 -0400 Subject: [PATCH 016/281] minor changes --- lib/server/update.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/server/update.js b/lib/server/update.js index 5704b0f5..94cb3def 100644 --- a/lib/server/update.js +++ b/lib/server/update.js @@ -1,7 +1,7 @@ /* module update cloud commander */ var exec = require('child_process').exec, - packagejson = cloudRequire('package.json'); + packagejson = cloudRequire(process.cwd() + '/package'); /** * function gets update @@ -26,6 +26,7 @@ function pull(pError, pStdout, pStderr){ else pStdout = 'Cloud Commander is up to date.'; console.log(packagejson); + console.log( process.cwd() ); if(packagejson) pStdout = 'Version ' + packagejson.version + '\n' + pStdout; } @@ -35,7 +36,7 @@ function pull(pError, pStdout, pStderr){ stderr : pStderr || pError }; - console.log(lExec); + console.log(lExec); } /** From 009215ef426af6c8f5971a6ec78593d6b108f142 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 3 Oct 2012 07:33:30 -0400 Subject: [PATCH 017/281] minor changes --- lib/server/update.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/server/update.js b/lib/server/update.js index 94cb3def..8c4a1d85 100644 --- a/lib/server/update.js +++ b/lib/server/update.js @@ -19,7 +19,8 @@ exports.get = function(pCallBack){ */ function pull(pError, pStdout, pStderr){ - if(!pError){ + if(!pError){ + pStderr = ''; if(pStdout !== 'Already up-to-date.\n'){ pStdout = 'Cloud Commander updated. Restart to use new version.'; } From b7dbfaf5bfc0d89341a911fce4f64e16ea980486 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 3 Oct 2012 07:34:22 -0400 Subject: [PATCH 018/281] minor changes --- lib/server/update.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/server/update.js b/lib/server/update.js index 8c4a1d85..1b7828df 100644 --- a/lib/server/update.js +++ b/lib/server/update.js @@ -26,7 +26,6 @@ function pull(pError, pStdout, pStderr){ } else pStdout = 'Cloud Commander is up to date.'; - console.log(packagejson); console.log( process.cwd() ); if(packagejson) pStdout = 'Version ' + packagejson.version + '\n' + pStdout; From 1801159aa155afde921942e0aea694ddb73a021a Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 3 Oct 2012 16:03:38 +0300 Subject: [PATCH 019/281] added link to documentation --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 9e35390a..582bebb0 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,10 @@ Editor's hot keys - F4 - open CodeMirror editor - Esc - close CodeMirror editor +Documentation +--------------- +JS Doc documentation could be found in [http://jsdoc.info/coderaiser/cloudcmd/](http://jsdoc.info/coderaiser/cloudcmd/) + Installing --------------- **Cloud Commander** installing is very easy. All you need it's just clone From 6a7ce5219c5085c0917e4f64873dad7068cc172e Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 3 Oct 2012 16:50:59 +0300 Subject: [PATCH 020/281] added comment --- cloudcmd.bat | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cloudcmd.bat b/cloudcmd.bat index 96b7b02c..cff0a18f 100644 --- a/cloudcmd.bat +++ b/cloudcmd.bat @@ -1 +1,9 @@ +:: ------------------------------------- +:: Changing charset back to 866, becouse +:: all win32 default commands, thet work +:: thrue cmd.exe showing out in unicode +:: charset. So Cloud Commander changes +:: 866 charset to Unicode 65001 sometime +:: when it's neaded. +:: ------------------------------------- node cloudcmd || chcp 866 \ No newline at end of file From 72f5b89f72351b56cce9c70613acb26ff9f576d0 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 3 Oct 2012 10:11:42 -0400 Subject: [PATCH 021/281] refactored --- lib/client/terminal.js | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/lib/client/terminal.js b/lib/client/terminal.js index 09d5a3f7..7fbf605b 100644 --- a/lib/client/terminal.js +++ b/lib/client/terminal.js @@ -16,10 +16,12 @@ var CloudCommander, $; var JqueryTerminal = {}; + /* PRIVATE FUNCTIONS */ + /** * function loads jquery-terminal */ - JqueryTerminal.load = (function(){ + function load(){ Util.cssLoad({ src : 'lib/client/terminal/jquery-terminal/jquery.terminal.css' }); @@ -29,7 +31,7 @@ var CloudCommander, $; var lLoadTerm_func = function(){ Util.jsload('lib/client/terminal/jquery-terminal/jquery.terminal.js', function(){ - JqueryTerminal.init(); + init(); $(function($, undefined) { Term = JqueryTerminal.Term = $('#terminal').terminal(function(command, term){ @@ -47,13 +49,12 @@ var CloudCommander, $; Util.jsload('lib/client/terminal/jquery-terminal/jquery.mousewheel.js', lLoadTerm_func); - }); - + } /** * function do basic initialization */ - JqueryTerminal.init = (function(){ + function init(){ if(!TerminalId){ var lFM = Util.getById('fm'); if(lFM) @@ -66,12 +67,14 @@ var CloudCommander, $; } else console.log('Error. Something went wrong FM not found'); - }); + } + + /* PUBLICK FUNCTIONS */ /** * functin show jquery-terminal */ - JqueryTerminal.show = (function(){ + JqueryTerminal.show = function(){ Util.Images.hideLoad(); /* only if panel was hided */ @@ -84,12 +87,12 @@ var CloudCommander, $; Term.resume(); } - }); + }; /** * function hide jquery-terminal */ - JqueryTerminal.hide = (function(){ + JqueryTerminal.hide = function(){ Hidden = true; Util.hide(TerminalId); @@ -98,7 +101,7 @@ var CloudCommander, $; KeyBinding.set(); Term.pause(); - }); + }; /** @@ -106,7 +109,7 @@ var CloudCommander, $; */ cloudcmd.Terminal.Keys = (function(){ /* loading js and css*/ - Util.jqueryLoad( JqueryTerminal.load ); + Util.jqueryLoad(load); var key_event = function(event){ /* если клавиши можно обрабатывать */ From 2b8cdf698c66d15cdeb3899f0002ba740fd8d0ba Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 3 Oct 2012 10:14:19 -0400 Subject: [PATCH 022/281] minor changes --- lib/server/socket.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/server/socket.js b/lib/server/socket.js index a085c7f9..f1b21c8a 100644 --- a/lib/server/socket.js +++ b/lib/server/socket.js @@ -105,10 +105,8 @@ function getExec(pSocket){ stderr : pStderr || pError }; - var lExec_str = JSON.stringify(lExec); - + var lExec_str = JSON.stringify(lExec); pSocket.send(lExec_str); - console.log(lExec); }; } @@ -130,7 +128,7 @@ var Win32Commands = ['ASSOC', 'AT', 'ATTRIB', 'BREAK', 'CACLS', 'CALL', 'START', 'SUBST', 'TIME', 'TITLE', 'TREE', 'TYPE', 'VER', 'VERIFY', 'VOL', 'XCOPY']; - /** +/** * function do safe require of needed module * @param pModule */ From 479276f4ca5c58896cf4a0595db74c8e69d2e81b Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 3 Oct 2012 10:20:43 -0400 Subject: [PATCH 023/281] refactored --- lib/client/viewer.js | 74 +++++++++++++++++----------------- lib/server/is-file-changed.js | 75 ----------------------------------- 2 files changed, 38 insertions(+), 111 deletions(-) delete mode 100644 lib/server/is-file-changed.js diff --git a/lib/client/viewer.js b/lib/client/viewer.js index 3b12ec1f..03e360ea 100644 --- a/lib/client/viewer.js +++ b/lib/client/viewer.js @@ -3,9 +3,12 @@ var CloudCommander, CloudFunc, $; * https://github.com/fancyapps/fancyBox */ (function(){ - var cloudcmd = CloudCommander; - var Util = CloudCommander.Util; - var KeyBinding = CloudCommander.KeyBinding; + "use strict"; + + var cloudcmd = CloudCommander, + Util = CloudCommander.Util, + KeyBinding = CloudCommander.KeyBinding, + FancyBox; cloudcmd.Viewer = { get: (function(){ @@ -13,15 +16,12 @@ var CloudCommander, CloudFunc, $; }) }; - var FancyBox = {}; - cloudcmd.Viewer.dir = './lib/client/viewer/'; FancyBox.dir = cloudcmd.Viewer.dir + 'fancybox/'; - /* function return configureation - * for FancyBox open and - * onclick (it shoud be - * different objects) + /** + * function return configureation for FancyBox open and + * onclick (it shoud be different objects) */ FancyBox.getConfig = (function(){ return{ @@ -57,7 +57,8 @@ var CloudCommander, CloudFunc, $; }; }); - /* function loads css and js of FancyBox + /** + * function loads css and js of FancyBox * @pParent - this * @pCallBack - executes, when everything loaded */ @@ -93,13 +94,14 @@ var CloudCommander, CloudFunc, $; }); }); - /* function loads data an put it to pSuccess_f - * @pA - link to data - * @pSucces_f - function, thet process data (@data) + /** + * function loads data an put it to pSuccess_f + * @param pA - link to data + * @param pSucces_f - function, thet process data (@data) * * Example: loadData('index.html', function(pData){console.log(pData)}); */ - FancyBox.loadData = (function(pA, pSuccess_f){ + FancyBox.loadData = function(pA, pSuccess_f){ Util.Images.showLoad(); var lThis = this; @@ -125,9 +127,9 @@ var CloudCommander, CloudFunc, $; Util.Images.hideLoad(); } }); - }); + }; - FancyBox.onDataLoaded = (function(pData){ + FancyBox.onDataLoaded = function(pData){ var lConfig = FancyBox.getConfig(); /* if we got json - show it */ @@ -135,10 +137,10 @@ var CloudCommander, CloudFunc, $; pData = JSON.stringify(pData, null, 4); $.fancybox('
' + pData + '
', lConfig); - }); + }; - FancyBox.set = (function(){ + FancyBox.set = function(){ if(Util.getByClass('fancybox').length) return; try{ @@ -147,17 +149,15 @@ var CloudCommander, CloudFunc, $; /* get all file links */ var lA = Util.getByTag('a', lPanel); - - var lThis = this; - + var lDblClick_f = function(pA){ return function(){ - var lConfig = lThis.getConfig(); + var lConfig = FancyBox.getConfig(); lConfig.href = pA.href; if(pA.rel) $.fancybox(lConfig); else - lThis.loadData(pA, lThis.onDataLoaded); + FancyBox.loadData(pA, FancyBox.onDataLoaded); }; }; @@ -176,10 +176,13 @@ var CloudCommander, CloudFunc, $; }catch(pError){ console.log(pError); } - }); + }; - FancyBox.show = (function(pCurrentFile){ + /** + * function shows FancyBox + */ + FancyBox.show = function(pCurrentFile){ FancyBox.set(); var lConfig = this.getConfig(); @@ -199,28 +202,27 @@ var CloudCommander, CloudFunc, $; } Util.Images.hideLoad(); - }); + }; - cloudcmd.Viewer.Keys = (function(pCurrentFile){ - "use strict"; - + cloudcmd.Viewer.Keys = function(pCurrentFile){ var lCallBack_f = (function(){ - var key_event = (function(pEvent){ + + var lF3 = cloudcmd.KEY.F3; + var key_event = function(pEvent){ /* если клавиши можно обрабатывать */ if( KeyBinding.get() ) - if(pEvent.keyCode === cloudcmd.KEY.F3 && - pEvent.shiftKey){ + if(pEvent.keyCode === lF3 && pEvent.shiftKey){ var lCurrentFile = Util.getCurrentFile(); FancyBox.show(lCurrentFile); - + pEvent.preventDefault(); } - }); + }; /* добавляем обработчик клавишь */ if (document.addEventListener) document.addEventListener('keydown', key_event, false); - + else{ var lFunc; if(typeof document.onkeydown === 'function') @@ -242,7 +244,7 @@ var CloudCommander, CloudFunc, $; Util.jqueryLoad(function(){ FancyBox.load(FancyBox, lCallBack_f); }); - }); + }; cloudcmd.Viewer.FancyBox = FancyBox; })(); \ No newline at end of file diff --git a/lib/server/is-file-changed.js b/lib/server/is-file-changed.js deleted file mode 100644 index e79fa515..00000000 --- a/lib/server/is-file-changed.js +++ /dev/null @@ -1,75 +0,0 @@ -/* Модуль проверяет поменялось ли содержимое файла по хэшу. */ - -var fs = require('fs'), - crypto = require('crypto'); - -/* object contains hashes of files*/ -var Hashes; - -/* - * Function reads hash table of files - * checks is file changed or not - * and return result. - * @pFileName - name of file - * @pFileData - data of file - * result: boolean - */ -exports.check = function(pFileName, pFileData, pOverWrite_b){ - var lReadedHash; - - /* boolean hashes.json changed or not */ - var lHashesChanged_b = false; - - if(!Hashes) - try { - console.log('trying to read hashes.json'); - Hashes = require(process.cwd() + '/hashes'); - - }catch(pError) { - console.log('hashes.json not found... \n'); - Hashes = {}; - } - - for(var lFileName in Hashes) - /* if founded row with - * file name - save hash - */ - if (lFileName === pFileName) { - lReadedHash = Hashes[pFileName]; - break; - } - - /* create hash of file data */ - var lFileHash = crypto.createHash('sha1'); - lFileHash = crypto.createHash('sha1'); - lFileHash.update(pFileData); - lFileHash = lFileHash.digest('hex'); - - if(lReadedHash !== lFileHash){ - Hashes[pFileName] = lFileHash; - lHashesChanged_b = true; - } - - if(pOverWrite_b){ - /* if hashes file was changes - write it */ - if(lHashesChanged_b) - fs.writeFile('./hashes.json', - JSON.stringify(Hashes), - fileWrited('./hashes.json')); - else console.log('no one file has been changed'); - } - /* has file changed? */ - return lHashesChanged_b; -}; - -/* - * Функция вызываеться после записи файла - * и выводит ошибку или сообщает, - * что файл успешно записан - */ -function fileWrited(pFileName){ - "use strict"; - return function(error){ - console.log(error?error:('file '+pFileName+' writed...')); - }; -} \ No newline at end of file From eb78921c3748996fbe2f01a5e970d9cf18413318 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 3 Oct 2012 10:21:33 -0400 Subject: [PATCH 024/281] minor changes --- lib/client/viewer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/client/viewer.js b/lib/client/viewer.js index 03e360ea..5e387121 100644 --- a/lib/client/viewer.js +++ b/lib/client/viewer.js @@ -8,9 +8,9 @@ var CloudCommander, CloudFunc, $; var cloudcmd = CloudCommander, Util = CloudCommander.Util, KeyBinding = CloudCommander.KeyBinding, - FancyBox; + FancyBox = {}; - cloudcmd.Viewer = { + cloudcmd.Viewer = { get: (function(){ return this.FancyBox; }) From 1df30e21a82ebca983c37d3404536db3d4b8e693 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 4 Oct 2012 03:51:36 -0400 Subject: [PATCH 025/281] fixed bug with testing mode --- ChangeLog | 3 +++ server.js | 9 ++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 611e20d8..ac7f5fee 100644 --- a/ChangeLog +++ b/ChangeLog @@ -14,6 +14,9 @@ file not just like Linux. * Added ability to change charset on terminal only if it's build in command on win32. +* Fixed bug with testing mode. If was same arguments, +Cloud Commander works in testing mode and server do not +started. 2012.10.01, Version 0.1.7 diff --git a/server.js b/server.js index c725eb66..11e993bd 100644 --- a/server.js +++ b/server.js @@ -176,8 +176,12 @@ CloudServer.start = function (pConfig) { !process.env.HOME.indexOf('/opt/haibu')) { this.IP = '0.0.0.0'; } + + + console.log(process.argv); + console.log(this.Config); /* server mode or testing mode */ - if (!process.argv[2] && this.Config.server) { + if (this.Config.server) { var http = require('http'); try { @@ -193,9 +197,8 @@ CloudServer.start = function (pConfig) { console.log('Cloud Commander server could not started'); console.log(pError); } - }else{ + }else console.log('Cloud Commander testing mode'); - } }; From b68018ea11f1ebfb8d1bbcbc9aec995066030303 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 4 Oct 2012 03:57:34 -0400 Subject: [PATCH 026/281] added mirror on cloudfoundry --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 582bebb0..0550847c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ Cloud Commander [![Build Status](https://secure.travis-ci.org/coderaiser/cloudcmd.png?branch=master)](http://travis-ci.org/coderaiser/cloudcmd) =============== **Cloud Commander** - two-panels file manager, totally writed on js. -View [demo](http://cloudcmd.jit.su/ "demo"), [mirror on nodester](http://cloudcmd.nodester.com/ "mirror on nodester") +View [demo](http://cloudcmd.jit.su/ "demo"), [mirror on nodester](http://cloudcmd.nodester.com/ "mirror on nodester"), +[mirror on cloudfoundry] (http://cloudcmd.cloudfoundry.com "mirror on cloudfoundry") Google PageSpeed Score : [100](https://developers.google.com/speed/pagespeed/insights#url=http_3A_2F_2Fdemo-cloudcmd.cloudfoundry.com_2F&mobile=false "score") (out of 100). @@ -71,6 +72,7 @@ All main configuration could be done thrue config.json. ```js { "cache" : {"allowed" : true}, /* cashing of js and css files in memory */ + "appcache" : false, /* html5 feature appcache */ "minification" : { /* minification of js,css,html and img */ "js" : false, /* minify module neaded */ "css" : false, /* npm i minify */ From eb7113b88722e014aec64821905ebac5d151df5b Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 4 Oct 2012 04:49:06 -0400 Subject: [PATCH 027/281] added more demo mirrors to readme --- ChangeLog | 3 +++ README.md | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index ac7f5fee..83a922b1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -18,6 +18,9 @@ it's build in command on win32. Cloud Commander works in testing mode and server do not started. +* Added more demo mirrors to readme (appfog, cloudfoundry). + + 2012.10.01, Version 0.1.7 * Changed name of menu files, fixed npm and jitsu diff --git a/README.md b/README.md index 0550847c..dd5edc54 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ Cloud Commander [![Build Status](https://secure.travis-ci.org/coderaiser/cloudcmd.png?branch=master)](http://travis-ci.org/coderaiser/cloudcmd) =============== **Cloud Commander** - two-panels file manager, totally writed on js. -View [demo](http://cloudcmd.jit.su/ "demo"), [mirror on nodester](http://cloudcmd.nodester.com/ "mirror on nodester"), -[mirror on cloudfoundry] (http://cloudcmd.cloudfoundry.com "mirror on cloudfoundry") +View [demo](http://cloudcmd.jit.su/ "demo"), +[mirror on nodester](http://cloudcmd.nodester.com/ "mirror on nodester"), +[mirror on cloudfoundry] (http://cloudcmd.cloudfoundry.com "mirror on cloudfoundry"), +[mirror on appfog] (http://cloudcmd.aws.af.cm "mirror on appfog) Google PageSpeed Score : [100](https://developers.google.com/speed/pagespeed/insights#url=http_3A_2F_2Fdemo-cloudcmd.cloudfoundry.com_2F&mobile=false "score") (out of 100). From 77504ffe9b933cb698b3c69f603b29719dae4db8 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 4 Oct 2012 11:51:56 +0300 Subject: [PATCH 028/281] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index dd5edc54..5a9a335e 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ Cloud Commander [![Build Status](https://secure.travis-ci.org/coderaiser/cloudcmd.png?branch=master)](http://travis-ci.org/coderaiser/cloudcmd) =============== **Cloud Commander** - two-panels file manager, totally writed on js. -View [demo](http://cloudcmd.jit.su/ "demo"), -[mirror on nodester](http://cloudcmd.nodester.com/ "mirror on nodester"), -[mirror on cloudfoundry] (http://cloudcmd.cloudfoundry.com "mirror on cloudfoundry"), -[mirror on appfog] (http://cloudcmd.aws.af.cm "mirror on appfog) +View demo on [nodester](http://cloudcmd.nodester.com/ "nodester"), +[jitsu](http://cloudcmd.jit.su/ "jitsu"), +[cloudfoundry] (http://cloudcmd.cloudfoundry.com "cloudfoundry"), +[appfog] (http://cloudcmd.aws.af.cm "appfog"). Google PageSpeed Score : [100](https://developers.google.com/speed/pagespeed/insights#url=http_3A_2F_2Fdemo-cloudcmd.cloudfoundry.com_2F&mobile=false "score") (out of 100). From da02446d3f877951345b0e2a8e0337c05ecd7407 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 4 Oct 2012 05:02:37 -0400 Subject: [PATCH 029/281] minor changes --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5a9a335e..827f0315 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ Cloud Commander [![Build Status](https://secure.travis-ci.org/coderaiser/cloudcmd.png?branch=master)](http://travis-ci.org/coderaiser/cloudcmd) =============== **Cloud Commander** - two-panels file manager, totally writed on js. -View demo on [nodester](http://cloudcmd.nodester.com/ "nodester"), -[jitsu](http://cloudcmd.jit.su/ "jitsu"), +View demo on [jitsu](http://cloudcmd.jit.su/ "jitsu"), [cloudfoundry] (http://cloudcmd.cloudfoundry.com "cloudfoundry"), -[appfog] (http://cloudcmd.aws.af.cm "appfog"). +[appfog] (http://cloudcmd.aws.af.cm "appfog"), +[nodester](http://cloudcmd.nodester.com/ "nodester"). Google PageSpeed Score : [100](https://developers.google.com/speed/pagespeed/insights#url=http_3A_2F_2Fdemo-cloudcmd.cloudfoundry.com_2F&mobile=false "score") (out of 100). From 32c0312c7ec951eefd41d5b8a7a18cb5e608f65d Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 4 Oct 2012 09:20:07 -0400 Subject: [PATCH 030/281] removed resing event from jquery-terminal --- ChangeLog | 2 ++ README.md | 1 - lib/client/terminal.js | 16 ++++++++++------ .../terminal/jquery-terminal/jquery.terminal.css | 5 ++++- .../terminal/jquery-terminal/jquery.terminal.js | 7 +++---- 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/ChangeLog b/ChangeLog index 83a922b1..72ac659b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -20,6 +20,8 @@ started. * Added more demo mirrors to readme (appfog, cloudfoundry). +* Removed resing event from jquery-terminal. + 2012.10.01, Version 0.1.7 diff --git a/README.md b/README.md index 827f0315..4bcc82b9 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,6 @@ we moves to *limited mode*. Limited-mode features --------------- -- only 1 panel available - no keybinding - no local caching - full loading of all web page(with styles, js-scripts, html-page etc). diff --git a/lib/client/terminal.js b/lib/client/terminal.js index 7fbf605b..2a86fd6c 100644 --- a/lib/client/terminal.js +++ b/lib/client/terminal.js @@ -28,21 +28,25 @@ var CloudCommander, $; Util.jsload('lib/client/socket.js'); - var lLoadTerm_func = function(){ + var lLoadTerm_func = function(){ Util.jsload('lib/client/terminal/jquery-terminal/jquery.terminal.js', function(){ init(); - + $(function($, undefined) { - Term = JqueryTerminal.Term = $('#terminal').terminal(function(command, term){ + Term = JqueryTerminal.Term = $('#terminal').terminal(function(command, term){ term.echo(''); cloudcmd.Socket.send(command); }, { - greetings : 'Javascript Interpreter', - prompt : 'cloudcmd> ' + greetings : '[[;#729FCF;]Cloud Commander Terminal]', + prompt : '[[;#729FCF;]cloudcmd> ]', + color : '#729FCF;' }); }); - + /* removing resize function, no need for us */ + Term.resize = function(){}; + $(window).unbind('resize'); + JqueryTerminal.show(); }); }; diff --git a/lib/client/terminal/jquery-terminal/jquery.terminal.css b/lib/client/terminal/jquery-terminal/jquery.terminal.css index a5450638..f4391f9c 100644 --- a/lib/client/terminal/jquery-terminal/jquery.terminal.css +++ b/lib/client/terminal/jquery-terminal/jquery.terminal.css @@ -26,7 +26,10 @@ font-family: FreeMono, monospace; color: #aaa; background-color: #000; - line-height: 14px; + line-height: 16px; + /* removing breaking the lines */ + white-space: nowrap; + overflow:hidden; } .terminal .cmd span { float: left; diff --git a/lib/client/terminal/jquery-terminal/jquery.terminal.js b/lib/client/terminal/jquery-terminal/jquery.terminal.js index dadc8880..749c83fd 100644 --- a/lib/client/terminal/jquery-terminal/jquery.terminal.js +++ b/lib/client/terminal/jquery-terminal/jquery.terminal.js @@ -1656,9 +1656,7 @@ function get_stack(caller) { div = $('
').html(encodeHTML(string)); } output.append(div); - /* - div.width('100%'); - */ + div.width('100%'); scroll_to_bottom(); return div; } @@ -1853,7 +1851,7 @@ function get_stack(caller) { }).get().join('\n'); } }, - resize: function(width, height) { + resize: function(width, height) { if (width && height) { self.width(width); self.height(height); @@ -2363,6 +2361,7 @@ function get_stack(caller) { } else { self.disable(); } + $(window).resize(self.resize); self.click(function() { self.focus(); From 6f2c42aa730dda90bc828ce480ce4a9d03d1ada0 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 4 Oct 2012 09:56:03 -0400 Subject: [PATCH 031/281] minor changes --- lib/client/terminal.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/client/terminal.js b/lib/client/terminal.js index 2a86fd6c..17b0700a 100644 --- a/lib/client/terminal.js +++ b/lib/client/terminal.js @@ -43,8 +43,9 @@ var CloudCommander, $; color : '#729FCF;' }); }); - /* removing resize function, no need for us */ - Term.resize = function(){}; + /* removing resize function, no need for us */ + Term.resize = function(pEvent){}; + $(window).unbind('resize'); JqueryTerminal.show(); From 6330b02408a7522621c04a74f1878f79e787a64a Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 4 Oct 2012 10:11:21 -0400 Subject: [PATCH 032/281] fixed bug with error code of program execution in terminal --- ChangeLog | 2 ++ lib/client/socket.js | 6 +++--- lib/server/socket.js | 3 +++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 72ac659b..ee2e6ad7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -22,6 +22,8 @@ started. * Removed resing event from jquery-terminal. +* Fixed bug with error code of program execution in terminal. + 2012.10.01, Version 0.1.7 diff --git a/lib/client/socket.js b/lib/client/socket.js index d1aab1b7..4cca6819 100644 --- a/lib/client/socket.js +++ b/lib/client/socket.js @@ -73,9 +73,9 @@ var CloudCommander, io; if(lStdout) lResult = lTerm.echo(lStdout); - - if(lStderr) - lResult = lTerm.error(lStderr); + + if(lStderr && lStderr.code != 1) + lResult = lTerm.error(lStderr.toString()); } else{ /* if term not accesable diff --git a/lib/server/socket.js b/lib/server/socket.js index f1b21c8a..586190ed 100644 --- a/lib/server/socket.js +++ b/lib/server/socket.js @@ -66,6 +66,9 @@ function onMessage(pConnNum, pSocket){ lMsg = JSON.stringify(lMsg); pSocket.send(lMsg); + console.log('received from client: ' + pCommand); + console.log('sended to client: ' + lMsg); + return; } } From 394615285027e980c266e52b94d7618c7c4883d0 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 5 Oct 2012 04:52:54 -0400 Subject: [PATCH 033/281] fixed bug with menu items sshow and view --- ChangeLog | 2 + client.js | 50 ++++++++----- lib/client/editor/_codemirror.js | 17 ++--- lib/client/keyBinding.js | 25 ++++--- lib/client/menu.js | 118 ++++++++++++++++--------------- lib/client/viewer.js | 14 ++-- server.js | 2 +- 7 files changed, 126 insertions(+), 102 deletions(-) diff --git a/ChangeLog b/ChangeLog index ee2e6ad7..23d1c4e3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -24,6 +24,8 @@ started. * Fixed bug with error code of program execution in terminal. +* Fixed bug with menu itmes edit and view. + 2012.10.01, Version 0.1.7 diff --git a/client.js b/client.js index a4e2a1ee..e2a7c42e 100644 --- a/client.js +++ b/client.js @@ -728,19 +728,35 @@ CloudClient.Util = (function(){ }; /** - * functions check is pObject is object - * @param pObject + * functions check is pVarible is boolean + * @param pVarible */ - this.isObject = function(pObject){ - return typeof pObject === 'object'; + this.isBoolean = function(pVarible){ + return this.isType(pVarible, 'boolean'); }; /** - * functions check is pFunction is function - * @param pFunction + * functions check is pVarible is object + * @param pVarible */ - this.isFunction = function(pFunction){ - return typeof pFunction === 'function'; + this.isObject = function(pVarible){ + return this.isType(pVarible, 'object'); + }; + + /** + * functions check is pVarible is function + * @param pVarible + */ + this.isFunction = function(pVarible){ + return this.isType(pVarible, 'function'); + }; + /** + * functions check is pVarible is pType + * @param pVarible + * @param pType + */ + this.isType = function(pVarible, pType){ + return typeof pVarible === pType; }; this.getCurrentLink = function(pCurrentFile){ @@ -901,13 +917,13 @@ CloudClient.KeyBinding = (function(){ }); /* function loads and shows editor */ -CloudClient.Editor = (function(pCurrentFile, pIsReadOnly) { +CloudClient.Editor = (function(pIsReadOnly) { /* loading CloudMirror plagin */ Util.jsload(CloudClient.LIBDIRCLIENT + 'editor/_codemirror.js',{ //'editor/_ace.js',{ onload:(function(){ - cloudcmd.Editor.Keys(pCurrentFile, pIsReadOnly); + cloudcmd.Editor.Keys(pIsReadOnly); }) }); }); @@ -931,7 +947,7 @@ CloudClient.GoogleAnalytics = (function(){ Util.jsload('lib/client/google_analytics.js'); },5000); - if(typeof lFunc === 'function') + if( Util.isFunction(lFunc) ) lFunc(); document.onmousemove = lFunc; @@ -1056,7 +1072,7 @@ CloudClient._editFileName = (function(pParent){ * setted up earlier */ document.onclick = lDocumentOnclick; - if(typeof lDocumentOnclick === 'function') + if( Util.isFunction(lDocumentOnclick) ) lDocumentOnclick(); }); @@ -1098,12 +1114,12 @@ CloudClient._setCurrent=(function(){ } /* если мы попали сюда с энтера*/ if(pFromEnter===true){ - if(typeof this.ondblclick === 'function') + if( Util.isFunction(this.ondblclick) ) this.ondblclick(this); /* enter pressed on file */ else{ var lA = this.getElementsByTagName('a')[0]; - if(typeof lA.ondblclick === 'function') + if( Util.isFunction(lA.ondblclick) ) lA.ondblclick(this); } }/* если мы попали сюда от клика мышки */ @@ -1169,7 +1185,7 @@ CloudClient.baseInit = (function(){ applicationCache.onupdateready = function(){ console.log('app cacheed'); location.reload(); - if(typeof lFunc === 'function') + if( Util.isFunction(lFunc) ) lFunc(); }; } @@ -1266,8 +1282,8 @@ CloudClient._changeLinks = function(pPanelID){ var lTarget = pEvent.currentTarget || pEvent.target; Util.setCurrentFile(lTarget); - if(typeof CloudCommander.Menu === 'function'){ - CloudCommander.Menu({ + if(Util.isFunction(cloudcmd.Menu) ){ + cloudcmd.Menu({ x: pEvent.x, y: pEvent.y }); diff --git a/lib/client/editor/_codemirror.js b/lib/client/editor/_codemirror.js index b36b26ef..005b7c74 100644 --- a/lib/client/editor/_codemirror.js +++ b/lib/client/editor/_codemirror.js @@ -98,7 +98,10 @@ var CloudCommander, CloudFunc, CodeMirror; /** * function shows CodeMirror editor */ - CodeMirrorEditor.show = function(){ + CodeMirrorEditor.show = function(pReadOnly){ + if( Util.isBoolean(pReadOnly) ) + ReadOnly = pReadOnly; + /* if CodeMirrorEditor is not loaded - loading him */ if(!CodeMirrorLoaded) return load(); @@ -189,12 +192,11 @@ var CloudCommander, CloudFunc, CodeMirror; /** * function bind keys */ - cloudcmd.Editor.Keys = function(pIsReadOnly){ - ReadOnly = pIsReadOnly; + cloudcmd.Editor.Keys = function(pReadOnly){ + ReadOnly = pReadOnly; - var lThis = this.CodeMirror; /* loading js and css of CodeMirror */ - lThis.show(); + CodeMirrorEditor.show(); var key_event = function(pEvent){ @@ -203,8 +205,7 @@ var CloudCommander, CloudFunc, CodeMirror; /* if f4 or f3 pressed */ var lF3 = cloudcmd.KEY.F3; var lF4 = cloudcmd.KEY.F4; - //var lShow = lThis.show.bind(lThis); - var lShow = Util.bind(lThis.show, lThis); + var lShow = Util.bind( CodeMirrorEditor.show, CodeMirrorEditor ); if(!pEvent.shiftKey){ switch(pEvent.keyCode) @@ -228,7 +229,7 @@ var CloudCommander, CloudFunc, CodeMirror; else{ var lFunc; - if(typeof document.onkeydown === 'function') + if( Util.isFunction(document.onkeydown) ) lFunc = document.onkeydown; document.onkeydown = function(){ diff --git a/lib/client/keyBinding.js b/lib/client/keyBinding.js index 02328cf3..eed04caf 100644 --- a/lib/client/keyBinding.js +++ b/lib/client/keyBinding.js @@ -71,7 +71,7 @@ var CloudCommander; console.log('openning config window...'); Util.Images.showLoad(); - if (typeof cloudcmd.Config === 'function') + if ( Util.isFunction(cloudcmd.Config) ) cloudcmd.Config(); } @@ -121,11 +121,11 @@ var CloudCommander; var lViewer = cloudcmd.Viewer; var lEditor = cloudcmd.Editor; - - if(event.shiftKey && typeof lViewer === 'function') - lViewer(lCurrentFile); - else if (typeof lEditor === 'function') + if(event.shiftKey && Util.isFunction(lViewer) ) + lViewer(lCurrentFile); + + else if ( Util.isFunction(lEditor) ) lEditor(true); event.preventDefault();//запрет на дальнейшее действие @@ -135,14 +135,14 @@ var CloudCommander; else if(event.keyCode === KEY.F4) { Util.Images.showLoad(); - if (typeof cloudcmd.Editor === 'function') + if ( Util.isFunction(cloudcmd.Editor) ) cloudcmd.Editor(); event.preventDefault();//запрет на дальнейшее действие } else if(event.keyCode === KEY.F10 && event.shiftKey){ - if (typeof cloudcmd.Menu === 'function') + if ( Util.isFunction(cloudcmd.Menu) ) cloudcmd.Menu(); event.preventDefault();//запрет на дальнейшее действие @@ -150,7 +150,7 @@ var CloudCommander; else if (event.keyCode === KEY.TRA){ Util.Images.showLoad({top: true}); - if(typeof cloudcmd.Terminal === 'function') + if( Util.isFunction(cloudcmd.Terminal) ) cloudcmd.Terminal(); } /* навигация по таблице файлов*/ @@ -270,10 +270,13 @@ var CloudCommander; /* из него достаём спан с именем файла*/ lName = Util.getByClass('name', lCurrentFile); + /* если нету (что вряд ли) - выходим*/ if(!lName)return false; + /* достаём все ссылки*/ var lATag = Util.getByTag('a', lName[0]); + /* если нету - выходим */ if(!lATag)return false; @@ -282,7 +285,8 @@ var CloudCommander; * (opera, ie), вызываем событие onclick, */ - if(lCurrentFile.onclick)lCurrentFile.onclick(true); + if(lCurrentFile.onclick) + lCurrentFile.onclick(true); else try{ lATag[0].click(); } @@ -311,8 +315,7 @@ var CloudCommander; var lRefreshIcon = Util.getRefreshButton(); if(lRefreshIcon){ /* получаем название файла*/ - var lSelectedName = Util - .getByTag('a', lCurrentFile)[0].textContent; + var lSelectedName = Util.getCurrentName(); /* если нашли элемент нажимаем него * а если не можем - нажимаем на diff --git a/lib/client/menu.js b/lib/client/menu.js index e534026f..320790e7 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -5,36 +5,38 @@ var CloudCommander, $; (function(){ "use strict"; - var cloudcmd = CloudCommander; - var KeyBinding = cloudcmd.KeyBinding; - - cloudcmd.Menu = {}; - - var Util = CloudCommander.Util; - - CloudCommander.Menu.dir = './lib/client/menu/'; + var cloudcmd = CloudCommander, + KeyBinding = cloudcmd.KeyBinding, + Util = cloudcmd.Util, + MenuSeted = false, + Menu = {}, + Position; + + Menu.dir = './lib/client/menu/'; /* enable and disable menu constant */ - CloudCommander.Menu.ENABLED = false; - + Menu.ENABLED = false; - CloudCommander.Menu.showEditor = (function(pReadOnly){ + /* PRIVATE FUNCTIONS */ + + /** function shows editor + * @param pReadOnly + */ + function showEditor(pReadOnly){ Util.Images.showLoad(); var lEditor = pReadOnly ? cloudcmd.Viewer : cloudcmd.Editor; - var lCurrent = Util.getCurrentFile(); - if(lCurrent){ - if(typeof lEditor === 'function') - lEditor(lCurrent); - else{ - lEditor = lEditor.get(); - if(lEditor && lEditor.show) - lEditor.show(lCurrent); - } - } - }); + + if( Util.isFunction(lEditor) ) + lEditor(pReadOnly); + else{ + lEditor = lEditor.get(); + if(lEditor && lEditor.show) + lEditor.show(); + } + } - /* function return configureation for menu */ - CloudCommander.Menu.getConfig = (function(){ + /** function return configureation for menu */ + function getConfig (){ return{ // define which elements trigger this menu selector: 'li', @@ -48,11 +50,11 @@ var CloudCommander, $; // define the elements of the menu items: { view: {name: 'View', callback: function(key, opt){ - CloudCommander.showEditor(true); + showEditor(true); }}, edit: {name: 'Edit', callback: function(key, opt){ - CloudCommander.showEditor(false); + showEditor(); }}, 'delete': {name: 'Delete', @@ -94,24 +96,23 @@ var CloudCommander, $; }} } }; - }); + } - /* function loads css and js of Menu - * @pParent - this - * @pPosition - position of menu + /** function loads css and js of Menu + * @param pParent - this + * @param pPosition - position of menu */ - CloudCommander.Menu.load = (function(pPosition){ - var lThis = this; + function load(){ var ljsLoad_f = function(){ - var lUISrc = lThis.dir + 'ui.position.js'; - var lMenuSrc = lThis.dir + 'contextMenu.js'; + var lUISrc = Menu.dir + 'ui.position.js'; + var lMenuSrc = Menu.dir + 'contextMenu.js'; Util.jsload(lUISrc, function(){ - Util.jsload(lMenuSrc, lThis.show(lThis, pPosition)); + Util.jsload(lMenuSrc, Menu.show()); }); }; - var lSrc = this.dir + 'contextMenu.css'; + var lSrc = Menu.dir + 'contextMenu.css'; Util.cssLoad({ src : lSrc, @@ -121,11 +122,11 @@ var CloudCommander, $; } } }); - }); + } - CloudCommander.Menu.set = (function(){ - if(!this.seted){ - $.contextMenu(this.getConfig()); + function set(){ + if(!MenuSeted){ + $.contextMenu(getConfig()); var lFunc_f = document.onclick; /* @@ -183,44 +184,43 @@ var CloudCommander, $; } }; - this.seted = true; + MenuSeted = true; } - }); + } - CloudCommander.Menu.seted = false; - /* function shows menu for the first time + /** function shows menu for the first time * right away after loading */ - CloudCommander.Menu.show = (function(pThis, pPosition){ + Menu.show = function(){ return function(){ - pThis.set(); + set(); Util.Images.hideLoad(); - if(pPosition && pPosition.x && pPosition.y) - $('li').contextMenu(pPosition); + if(Position && Position.x && Position.y) + $('li').contextMenu(Position); else $('li').contextMenu(); }; - }); + }; /* key binding function */ - CloudCommander.Menu.Keys = (function(pPosition){ - "use strict"; - - var lFunc = document.oncontextmenu; + Menu.Keys = function(pPosition){ + Position = pPosition; + + var lFunc = document.oncontextmenu; document.oncontextmenu = function(){ if(typeof lFunc === 'function') lFunc(); - return CloudCommander.Menu.ENABLED; + return Menu.ENABLED; }; var key_event = (function(pEvent){ /* если клавиши можно обрабатывать */ if( KeyBinding.get() ) /* if shift + F10 pressed */ - if(pEvent.keyCode === CloudCommander.KEY.F10 && + if(pEvent.keyCode === cloudcmd.KEY.F10 && pEvent.shiftKey){ var lCurrent = Util.getCurrentFile(); if(lCurrent) @@ -247,9 +247,11 @@ var CloudCommander, $; }; /* showing context menu preview*/ - CloudCommander.Menu.show(); + Menu.show(); } - CloudCommander.Menu.load(pPosition); - }); + load(); + }; + + cloudcmd.Menu = Menu; })(); \ No newline at end of file diff --git a/lib/client/viewer.js b/lib/client/viewer.js index 5e387121..7e96dee2 100644 --- a/lib/client/viewer.js +++ b/lib/client/viewer.js @@ -182,13 +182,17 @@ var CloudCommander, CloudFunc, $; /** * function shows FancyBox */ - FancyBox.show = function(pCurrentFile){ + FancyBox.show = function(pCallBack){ FancyBox.set(); var lConfig = this.getConfig(); - if(typeof pCurrentFile !== 'function'){ - var lA = Util.getByClass('fancybox', pCurrentFile)[0]; + if( Util.isFunction(pCallBack) ) + pCallBack(); + else{ + var lCurrentFile = Util.getCurrentFile(); + + var lA = Util.getByClass('fancybox', lCurrentFile)[0]; if(lA){ if(lA.rel) $.fancybox.open({ href : lA.href }, @@ -196,10 +200,6 @@ var CloudCommander, CloudFunc, $; else this.loadData(lA, this.onDataLoaded); } } - else { - var lFunc_f = pCurrentFile; - lFunc_f(); - } Util.Images.hideLoad(); }; diff --git a/server.js b/server.js index 11e993bd..e7ddb1a2 100644 --- a/server.js +++ b/server.js @@ -179,7 +179,7 @@ CloudServer.start = function (pConfig) { console.log(process.argv); - console.log(this.Config); + /* server mode or testing mode */ if (this.Config.server) { var http = require('http'); From d66f0cc391ca6276bd8e9b21b8e9f162e03db43e Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 5 Oct 2012 05:10:11 -0400 Subject: [PATCH 034/281] refactored --- lib/client/keyBinding.js | 42 ++++++++-------- lib/client/viewer.js | 102 ++++++++++++++++++++------------------- 2 files changed, 74 insertions(+), 70 deletions(-) diff --git a/lib/client/keyBinding.js b/lib/client/keyBinding.js index eed04caf..08965991 100644 --- a/lib/client/keyBinding.js +++ b/lib/client/keyBinding.js @@ -66,8 +66,10 @@ var CloudCommander; lName, i; /* если клавиши можно обрабатывать*/ if(keyBinded && event){ + var lKeyCode = event.keyCode; + /* open configuration window */ - if(event.keyCode === KEY.O && event.altKey){ + if(lKeyCode === KEY.O && event.altKey){ console.log('openning config window...'); Util.Images.showLoad(); @@ -82,7 +84,7 @@ var CloudCommander; * мы были на левой и * наоборот */ - else if(event.keyCode === KEY.TAB){ + else if(lKeyCode === KEY.TAB){ console.log('Tab pressed'); try{ @@ -109,21 +111,21 @@ var CloudCommander; event.preventDefault();//запрет на дальнейшее действие } /* if f2 pressed */ - else if(event.keyCode === KEY.F2){ + else if(lKeyCode === KEY.F2){ } - else if(event.keyCode === KEY.Delete) + else if(lKeyCode === KEY.Delete) Util.removeCurrent(lCurrentFile); /* if f3 or shift+f3 or alt+f3 pressed */ - else if(event.keyCode === KEY.F3){ + else if(lKeyCode === KEY.F3){ Util.Images.showLoad(); var lViewer = cloudcmd.Viewer; var lEditor = cloudcmd.Editor; if(event.shiftKey && Util.isFunction(lViewer) ) - lViewer(lCurrentFile); + lViewer(); else if ( Util.isFunction(lEditor) ) lEditor(true); @@ -132,7 +134,7 @@ var CloudCommander; } /* if f4 pressed */ - else if(event.keyCode === KEY.F4) { + else if(lKeyCode === KEY.F4) { Util.Images.showLoad(); if ( Util.isFunction(cloudcmd.Editor) ) @@ -140,7 +142,7 @@ var CloudCommander; event.preventDefault();//запрет на дальнейшее действие } - else if(event.keyCode === KEY.F10 && + else if(lKeyCode === KEY.F10 && event.shiftKey){ if ( Util.isFunction(cloudcmd.Menu) ) cloudcmd.Menu(); @@ -148,14 +150,14 @@ var CloudCommander; event.preventDefault();//запрет на дальнейшее действие } - else if (event.keyCode === KEY.TRA){ + else if (lKeyCode === KEY.TRA){ Util.Images.showLoad({top: true}); if( Util.isFunction(cloudcmd.Terminal) ) cloudcmd.Terminal(); } /* навигация по таблице файлов*/ /* если нажали клавишу вверх*/ - else if(event.keyCode === KEY.UP){ + else if(lKeyCode === KEY.UP){ /* если ненайдены выделенные файлы - выходим*/ if(!lCurrentFile) return; @@ -173,7 +175,7 @@ var CloudCommander; } /* если нажали клавишу в низ*/ - else if(event.keyCode === KEY.DOWN){ + else if(lKeyCode === KEY.DOWN){ /* если ненайдены выделенные файлы - выходим*/ if(!lCurrentFile)return; @@ -191,7 +193,7 @@ var CloudCommander; * переходим к самому верхнему * элементу */ - else if(event.keyCode === KEY.HOME){ + else if(lKeyCode === KEY.HOME){ /* получаем первый элемент * пропускаем путь и заголовки столбиков * выделяем верхий файл @@ -210,7 +212,7 @@ var CloudCommander; /* если нажали клавишу End * выделяем последний элемент */ - else if( event.keyCode === KEY.END){ + else if(lKeyCode === KEY.END){ /* выделяем самый нижний файл */ lCurrentFile = lCurrentFile. parentElement.lastElementChild; @@ -225,7 +227,7 @@ var CloudCommander; /* если нажали клавишу page down * проматываем экран */ - else if(event.keyCode === KEY.PAGE_DOWN){ + else if(lKeyCode === KEY.PAGE_DOWN){ Util.getPanel().scrollByPages(1); for(i=0; i<30; i++){ @@ -241,7 +243,7 @@ var CloudCommander; /* если нажали клавишу page up * проматываем экран */ - else if(event.keyCode === KEY.PAGE_UP){ + else if(lKeyCode === KEY.PAGE_UP){ Util.getPanel().scrollByPages(-1); for(i=0; i<30; i++){ @@ -264,7 +266,7 @@ var CloudCommander; } /* если нажали Enter - открываем папку*/ - else if(event.keyCode === KEY.ENTER){ + else if(lKeyCode === KEY.ENTER){ /* если ненайдены выделенные файлы - выходим*/ if(!lCurrentFile)return; @@ -304,7 +306,7 @@ var CloudCommander; * сервера, а не из кэша * (обновляем кэш) */ - else if(event.keyCode === KEY.R && + else if(lKeyCode === KEY.R && event.ctrlKey){ console.log('+r pressed'); console.log('reloading page...'); @@ -331,7 +333,7 @@ var CloudCommander; } /* если нажали +d чистим кэш */ - else if(event.keyCode === KEY.D && + else if(lKeyCode === KEY.D && event.ctrlKey){ console.log('+d pressed'); console.log('clearing cache...'); @@ -348,7 +350,7 @@ var CloudCommander; * убираем все обработчики * нажатий клавиш */ - else if(event.keyCode === KEY.Q && + else if(lKeyCode === KEY.Q && event.altKey){ //document.removeEventListener('keydown', key_event,false); console.log('+q pressed'); @@ -367,7 +369,7 @@ var CloudCommander; * устанавливаем все обработчики * нажатий клавиш */ - else if(event.keyCode === KEY.S && + else if(lKeyCode === KEY.S && event.altKey){ /* обрабатываем нажатия на клавиши*/ keyBinded = true; diff --git a/lib/client/viewer.js b/lib/client/viewer.js index 7e96dee2..84968744 100644 --- a/lib/client/viewer.js +++ b/lib/client/viewer.js @@ -19,11 +19,53 @@ var CloudCommander, CloudFunc, $; cloudcmd.Viewer.dir = './lib/client/viewer/'; FancyBox.dir = cloudcmd.Viewer.dir + 'fancybox/'; + /* PRIVATE FUNCTIONS */ + + function set(){ + if(Util.getByClass('fancybox').length) + return; + try{ + /* get current panel (left or right) */ + var lPanel = Util.getPanel(); + + /* get all file links */ + var lA = Util.getByTag('a', lPanel); + + var lDblClick_f = function(pA){ + return function(){ + var lConfig = FancyBox.getConfig(); + lConfig.href = pA.href; + if(pA.rel) + $.fancybox(lConfig); + else + FancyBox.loadData(pA, FancyBox.onDataLoaded); + }; + }; + + /* first two is not files nor folders*/ + for (var i=2; i < lA.length; i++) { + var lName = lA[i].title || lA[i].textContent; + + lA[i].className = 'fancybox'; + if(CloudFunc.checkExtension(lName, ['png','jpg', 'gif','ico'])){ + lA[i].rel = 'gallery'; + } + + lA[i].ondblclick = lDblClick_f(lA[i]); + } + + }catch(pError){ + console.log(pError); + } + } + + + /** * function return configureation for FancyBox open and * onclick (it shoud be different objects) */ - FancyBox.getConfig = (function(){ + FancyBox.getConfig = function(){ return{ /* function do her work * before FauncyBox shows @@ -55,16 +97,16 @@ var CloudCommander, CloudFunc, $; }, padding : 0 }; - }); + }; /** * function loads css and js of FancyBox * @pParent - this * @pCallBack - executes, when everything loaded */ - FancyBox.load = (function(pThis, pCallBack){ + FancyBox.load = function(pCallBack){ var ljsLoad_f = function(){ - var lSrc = pThis.dir + 'jquery.fancybox.js'; + var lSrc = FancyBox.dir + 'jquery.fancybox.js'; Util.jsload(lSrc,{ onload: pCallBack }); @@ -85,14 +127,14 @@ var CloudCommander, CloudFunc, $; '}' }); - var lSrc = pThis.dir +'jquery.fancybox.css'; + var lSrc = FancyBox.dir +'jquery.fancybox.css'; Util.cssLoad({ src : lSrc, func : { onload: ljsLoad_f } }); - }); + }; /** * function loads data an put it to pSuccess_f @@ -139,51 +181,11 @@ var CloudCommander, CloudFunc, $; $.fancybox('
' + pData + '
', lConfig); }; - - FancyBox.set = function(){ - if(Util.getByClass('fancybox').length) - return; - try{ - /* get current panel (left or right) */ - var lPanel = Util.getPanel(); - - /* get all file links */ - var lA = Util.getByTag('a', lPanel); - - var lDblClick_f = function(pA){ - return function(){ - var lConfig = FancyBox.getConfig(); - lConfig.href = pA.href; - if(pA.rel) - $.fancybox(lConfig); - else - FancyBox.loadData(pA, FancyBox.onDataLoaded); - }; - }; - - /* first two is not files nor folders*/ - for (var i=2; i < lA.length; i++) { - var lName = lA[i].title || lA[i].textContent; - - lA[i].className = 'fancybox'; - if(CloudFunc.checkExtension(lName, ['png','jpg', 'gif','ico'])){ - lA[i].rel = 'gallery'; - } - - lA[i].ondblclick = lDblClick_f(lA[i]); - } - - }catch(pError){ - console.log(pError); - } - }; - - /** * function shows FancyBox */ FancyBox.show = function(pCallBack){ - FancyBox.set(); + set(); var lConfig = this.getConfig(); @@ -204,7 +206,7 @@ var CloudCommander, CloudFunc, $; Util.Images.hideLoad(); }; - cloudcmd.Viewer.Keys = function(pCurrentFile){ + cloudcmd.Viewer.Keys = function(){ var lCallBack_f = (function(){ var lF3 = cloudcmd.KEY.F3; @@ -237,12 +239,12 @@ var CloudCommander, CloudFunc, $; } /* showing images preview*/ - FancyBox.show(pCurrentFile); + FancyBox.show(); Util.Images.hideLoad(); }); Util.jqueryLoad(function(){ - FancyBox.load(FancyBox, lCallBack_f); + FancyBox.load(lCallBack_f); }); }; From 074910286863acd8fcc8b7d2dbd891baaddd33a0 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 5 Oct 2012 06:53:23 -0400 Subject: [PATCH 035/281] minor changes --- ChangeLog | 1 + lib/client/terminal.js | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 23d1c4e3..9c544232 100644 --- a/ChangeLog +++ b/ChangeLog @@ -34,6 +34,7 @@ problem with menu showing. * Fixed bug with empty directorys. If directory was empty, we could not go in. + * Removed packed versions of files, uglifing would be doing on-a-fly diff --git a/lib/client/terminal.js b/lib/client/terminal.js index 17b0700a..4358d07e 100644 --- a/lib/client/terminal.js +++ b/lib/client/terminal.js @@ -107,8 +107,7 @@ var CloudCommander, $; Term.pause(); }; - - + /** * function bind keys */ From 06d87ebabc6834c0f82d0e98016537d39de12e10 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 5 Oct 2012 09:09:44 -0400 Subject: [PATCH 036/281] added CONTRIBUTING --- config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.json b/config.json index 75bcf6c6..8ce7879f 100644 --- a/config.json +++ b/config.json @@ -1,8 +1,8 @@ { - "cache" : {"allowed" : false}, + "cache" : {"allowed" : true}, "appcache" : false, "minification" : { - "js" : false, + "js" : true, "css" : true, "html" : true, "img" : true From b896ee9da9982fecaf06e66c8424670814ecd6a4 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 12 Oct 2012 07:07:12 -0400 Subject: [PATCH 037/281] Added function jsLoadOnLoad, thet loads js files one-by-one, and then calls callback if needed --- ChangeLog | 3 +++ client.js | 73 +++++++++++++++++++++++++++++++++++++---------------- config.json | 4 +-- 3 files changed, 56 insertions(+), 24 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9c544232..59ff7f0a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -26,6 +26,9 @@ started. * Fixed bug with menu itmes edit and view. +* Added function jsLoadOnLoad, thet loads js files +one-by-one, and then calls callback if needed. + 2012.10.01, Version 0.1.7 diff --git a/client.js b/client.js index e2a7c42e..2f1c5199 100644 --- a/client.js +++ b/client.js @@ -1,8 +1,6 @@ /* Функция которая возвратит обьект CloudCommander - * @window - обьект window - * @document - обьект document * @CloudFunc - обьект содержащий общий функционал - * клиентский и серверный + * клиентский и серверный */ var CloudCommander = (function(){ @@ -165,8 +163,7 @@ CloudClient.Util = (function(){ }; this.ajax = function(pParams){ - /* if on webkit - */ + /* if on webkit */ if(window.XMLHttpRequest){ if(!lXMLHTTP) lXMLHTTP = new XMLHttpRequest(); @@ -177,7 +174,7 @@ CloudClient.Util = (function(){ lXMLHTTP.open(lMethod, pParams.url, true); lXMLHTTP.send(null); - + var lSuccess_f = pParams.success; if(typeof lSuccess_f !== 'function') console.log('error in Util.ajax onSuccess:', pParams); @@ -251,7 +248,7 @@ CloudClient.Util = (function(){ }, this.loadOnload = function(pFunc_a){ - if(pFunc_a instanceof Array) { + if( this.isArray(pFunc_a) ) { var lFunc_f = pFunc_a.pop(); @@ -265,16 +262,36 @@ CloudClient.Util = (function(){ }; this.anyLoadOnload = function(pParams_a){ - if(pParams_a instanceof Array) { + if( this.isArray(pParams_a) ) { + var lParam = pParams_a.pop(); + + if(lParam && !lParam.func) + lParam.func = function(){ + lThis.anyLoadOnload(pParams_a); + }; - var lParams_o = pParams_a.pop(); - - if(!lParams_o.func) - lParams_o.func = function(){ - lThis.anyLoadOnload(pParams_a); - }; - - return this.anyload(lParams_o); + this.anyload(lParam); + } + }; + + /** + * function loads a couple js files one-by-one + * @pSrc_a ([String1, ..., StringN]) + * @pCallBack (functin) + */ + this.jsLoadOnLoad = function (pSrc_a, pCallBack){ + if( this.isArray(pSrc_a) ) { + var n = pSrc_a.length; + + for(var i=0; i= 0 ); }; + /** + * functions check is pVarible is array + * @param pVarible + */ + this.isArray = function(pVarible){ + return pVarible instanceof Array; + }; + /** * functions check is pVarible is boolean * @param pVarible diff --git a/config.json b/config.json index 8ce7879f..75bcf6c6 100644 --- a/config.json +++ b/config.json @@ -1,8 +1,8 @@ { - "cache" : {"allowed" : true}, + "cache" : {"allowed" : false}, "appcache" : false, "minification" : { - "js" : true, + "js" : false, "css" : true, "html" : true, "img" : true From 00391f183c5969bed86fe4ed3ea720d3212233e6 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 12 Oct 2012 07:27:26 -0400 Subject: [PATCH 038/281] minor changes --- client.js | 4 ++-- lib/client/editor/_codemirror.js | 25 +++++++++++++------------ 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/client.js b/client.js index 2f1c5199..5194da7c 100644 --- a/client.js +++ b/client.js @@ -279,7 +279,7 @@ CloudClient.Util = (function(){ * @pSrc_a ([String1, ..., StringN]) * @pCallBack (functin) */ - this.jsLoadOnLoad = function (pSrc_a, pCallBack){ + this.jsloadOnLoad = function (pSrc_a, pCallBack){ if( this.isArray(pSrc_a) ) { var n = pSrc_a.length; @@ -289,7 +289,7 @@ CloudClient.Util = (function(){ src : pSrc_a[i] }; - pSrc_a[n-1].func = pCallBack; + pSrc_a[0].func = pCallBack; this.anyLoadOnload(pSrc_a); } diff --git a/lib/client/editor/_codemirror.js b/lib/client/editor/_codemirror.js index 005b7c74..aee81cf0 100644 --- a/lib/client/editor/_codemirror.js +++ b/lib/client/editor/_codemirror.js @@ -60,11 +60,13 @@ var CloudCommander, CloudFunc, CodeMirror; * function loads CodeMirror js and css files */ function load(){ - /* function loads css files of CodeMirror */ - var loadAll = function() { + var lDir = CodeMirrorEditor.dir; + + /* function loads css files of CodeMirror */ + var loadAll = function() { Util.cssLoad([ - { src : CodeMirrorEditor.dir + 'codemirror.css'}, - { src : CodeMirrorEditor.dir + 'theme/night.css'} + { src : lDir + 'codemirror.css'}, + { src : lDir + 'theme/night.css'} ]); @@ -82,17 +84,16 @@ var CloudCommander, CloudFunc, CodeMirror; //'#CodeMirrorEditor{' + // 'padding :20px 20px 20px 20px;' + // '}' - }); - - Util.jsload(CodeMirrorEditor.dir + - 'mode/javascript.js', function(){ - CodeMirrorLoaded = true; - CodeMirrorEditor.show(); - }); + }); + + Util.jsload(lDir + 'mode/javascript.js', function(){ + CodeMirrorLoaded = true; + CodeMirrorEditor.show(); + }); }; /* load CodeMirror main module */ - Util.jsload(CodeMirrorEditor.dir + 'codemirror.js', loadAll); + Util.jsload(lDir + 'codemirror.js', loadAll); } /** From 8a255deaacc7c30eecb6d0709941ff2355289ecc Mon Sep 17 00:00:00 2001 From: coderaiser Date: Sat, 20 Oct 2012 08:47:56 -0400 Subject: [PATCH 039/281] refactored anyLoadOnload --- client.js | 96 ++++++++++++++++++++------------ lib/client/editor/_codemirror.js | 41 +++++++------- server.js | 4 +- 3 files changed, 81 insertions(+), 60 deletions(-) diff --git a/client.js b/client.js index 5194da7c..4d31f77d 100644 --- a/client.js +++ b/client.js @@ -257,20 +257,25 @@ CloudClient.Util = (function(){ return this.loadOnload(pFunc_a); } - else if(typeof pFunc_a === 'function') + else if(typeof pFunc_a === 'function') return pFunc_a(); }; - this.anyLoadOnload = function(pParams_a){ + this.anyLoadOnload = function(pParams_a, pFunc){ if( this.isArray(pParams_a) ) { var lParam = pParams_a.pop(); - - if(lParam && !lParam.func) + + if(Util.isString(lParam) ) + lParam = { src : lParam }; + + if(lParam && !lParam.func){ lParam.func = function(){ - lThis.anyLoadOnload(pParams_a); + Util.anyLoadOnload(pParams_a, pFunc); }; - - this.anyload(lParam); + this.anyload(lParam); + + }else if( this.isFunction(pFunc) ) + pFunc(); } }; @@ -279,7 +284,7 @@ CloudClient.Util = (function(){ * @pSrc_a ([String1, ..., StringN]) * @pCallBack (functin) */ - this.jsloadOnLoad = function (pSrc_a, pCallBack){ + this.jsloadOnLoad = function (pSrc_a, pCallBack){ if( this.isArray(pSrc_a) ) { var n = pSrc_a.length; @@ -292,7 +297,7 @@ CloudClient.Util = (function(){ pSrc_a[0].func = pCallBack; this.anyLoadOnload(pSrc_a); - } + } }; /* @@ -323,15 +328,17 @@ CloudClient.Util = (function(){ lElements_a[i] = this.anyload(pParams_o[i]); return lElements_a; - } + } /* убираем путь к файлу, оставляя только название файла */ - var lID = pParams_o.id; - var lClass = pParams_o.className; - var lSrc = pParams_o.src; - var lFunc = pParams_o.func; - var lAsync = pParams_o.async; - + var lName = pParams_o.name, + lID = pParams_o.id, + lClass = pParams_o.className, + lSrc = pParams_o.src, + lFunc = pParams_o.func, + lAsync = pParams_o.async, + lParent = pParams_o.parent; + if(!lID && lSrc) lID = this.getIdBySrc(lSrc); @@ -339,10 +346,23 @@ CloudClient.Util = (function(){ /* если скрипт еще не загружен */ if(!element) { - if(!pParams_o.name) - return {code: -1, text: 'name can not be empty'}; - - element = document.createElement(pParams_o.name); + if(!lName && lSrc){ + + var lDot = lSrc.lastIndexOf('.'); + var 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'}; + } + } + element = document.createElement(lName); if(lID) element.id = lID; @@ -353,15 +373,15 @@ CloudClient.Util = (function(){ * using href in any other case * using src */ - pParams_o.name === 'link' ? + lName === 'link' ? ((element.href = lSrc) && (element.rel = 'stylesheet')) : element.src = lSrc; /* if passed arguments function * then it's onload by default */ - if(pParams_o.func) - if(typeof lFunc === 'function'){ + if(lFunc){ + if( Util.isFunction(lFunc) ) element.onload = lFunc; /* element.onreadystatechange = function(){ @@ -370,18 +390,15 @@ CloudClient.Util = (function(){ };*/ /* ie */ /* if object - then onload or onerror */ - }else if ( this.isObject(lFunc) ) { - if(lFunc.onload && - this.isFunction(lFunc.onload)){ + else if ( this.isObject(lFunc) ) + if(lFunc.onload && this.isFunction(lFunc.onload)) element.onload = lFunc.onload; /* element.onreadystatechange = function(){ if(this.readyState === 'loaded') lFunc(); - };*/ /* ie */ - } - } - + };*/ /* ie */ + } /* if element (js/css) will not loaded * it would be removed from DOM tree * and error image would be shown @@ -397,20 +414,19 @@ CloudClient.Util = (function(){ status : 404 }); - if(lFunc && lFunc.onerror && - typeof lFunc.onerror === 'function') - lFunc.onerror(); + if(lFunc && lFunc.onerror && Util.isFunction(lFunc.onerror) ) + lFunc.onerror(); }); if(pParams_o.style){ - element.style.cssText=pParams_o.style; + element.style.cssText = pParams_o.style; } if(lAsync || lAsync === undefined) element.async = true; if(!pParams_o.not_append) - (pParams_o.parent || document.body).appendChild(element); + (lParent || document.body).appendChild(element); if(pParams_o.inner){ element.innerHTML = pParams_o.inner; @@ -420,7 +436,7 @@ CloudClient.Util = (function(){ * запускаем функцию onload */ else if(lFunc){ - if(typeof lFunc === 'function') + if( this.isFunction(lFunc) ) lFunc(); else if( this.isObject(lFunc) && this.isFunction(lFunc.onload) ) @@ -771,7 +787,13 @@ CloudClient.Util = (function(){ this.isObject = function(pVarible){ return this.isType(pVarible, 'object'); }; - + /** + * functions check is pVarible is string + * @param pVarible + */ + this.isString = function(pVarible){ + return this.isType(pVarible, 'string'); + }; /** * functions check is pVarible is function * @param pVarible diff --git a/lib/client/editor/_codemirror.js b/lib/client/editor/_codemirror.js index aee81cf0..7c50a898 100644 --- a/lib/client/editor/_codemirror.js +++ b/lib/client/editor/_codemirror.js @@ -60,17 +60,13 @@ var CloudCommander, CloudFunc, CodeMirror; * function loads CodeMirror js and css files */ function load(){ + console.time('codemirror load'); var lDir = CodeMirrorEditor.dir; - /* function loads css files of CodeMirror */ - var loadAll = function() { - Util.cssLoad([ - { src : lDir + 'codemirror.css'}, - { src : lDir + 'theme/night.css'} - ]); - - - Util.cssSet({id:'editor', + Util.anyLoadOnload( + [{ + name: 'style', + id:'editor', inner : '.CodeMirror{' + 'font-family :\'Droid Sans Mono\';' + 'font-size :15px;' + @@ -79,27 +75,30 @@ var CloudCommander, CloudFunc, CodeMirror; '}' + '.CodeMirror-scroll{' + 'height : ' + (cloudcmd.HEIGHT) + 'px' + - '}' //+ + '}', //+ /* codemirror v3 */ //'#CodeMirrorEditor{' + // 'padding :20px 20px 20px 20px;' + // '}' - }); + parent: document.head + }, + lDir + 'mode/javascript.js', + lDir + 'codemirror.css', + lDir + 'theme/night.css', + lDir + 'codemirror.js'], - Util.jsload(lDir + 'mode/javascript.js', function(){ - CodeMirrorLoaded = true; - CodeMirrorEditor.show(); - }); - }; - - /* load CodeMirror main module */ - Util.jsload(lDir + 'codemirror.js', loadAll); - } + function(){ + console.timeEnd('codemirror load'); + CodeMirrorLoaded = true; + CodeMirrorEditor.show(); + } + ); + } /** * function shows CodeMirror editor */ - CodeMirrorEditor.show = function(pReadOnly){ + CodeMirrorEditor.show = function(pReadOnly){ if( Util.isBoolean(pReadOnly) ) ReadOnly = pReadOnly; diff --git a/server.js b/server.js index e7ddb1a2..d9a2dc0c 100644 --- a/server.js +++ b/server.js @@ -214,8 +214,8 @@ CloudServer.generateHeaders = function(pName, pGzip){ var lCacheControl = 0; var lContentEncoding = ''; - /* высылаем заголовок в зависимости от типа файла */ - var lDot = pName.lastIndexOf('.'); + /* высылаем заголовок в зависимости от типа файла */ + var lDot = pName.lastIndexOf('.'); var lExt = pName.substr(lDot); if(lExt === '.appcache') From 5048f9b1c5807c72e728f85f4083987fc2471209 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Sat, 20 Oct 2012 11:31:17 -0400 Subject: [PATCH 040/281] refactored --- client.js | 4 +++- lib/client/editor/_codemirror.js | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/client.js b/client.js index 4d31f77d..19516972 100644 --- a/client.js +++ b/client.js @@ -268,10 +268,12 @@ CloudClient.Util = (function(){ if(Util.isString(lParam) ) lParam = { src : lParam }; + if(lParam && !lParam.func){ lParam.func = function(){ Util.anyLoadOnload(pParams_a, pFunc); - }; + }; + this.anyload(lParam); }else if( this.isFunction(pFunc) ) diff --git a/lib/client/editor/_codemirror.js b/lib/client/editor/_codemirror.js index 7c50a898..23ecc1c0 100644 --- a/lib/client/editor/_codemirror.js +++ b/lib/client/editor/_codemirror.js @@ -74,7 +74,7 @@ var CloudCommander, CloudFunc, CodeMirror; //'height : ' + cloudcmd.HEIGHT + 'px' + '}' + '.CodeMirror-scroll{' + - 'height : ' + (cloudcmd.HEIGHT) + 'px' + + 'height : ' + (cloudcmd.HEIGHT) + 'px' + '}', //+ /* codemirror v3 */ //'#CodeMirrorEditor{' + @@ -82,9 +82,9 @@ var CloudCommander, CloudFunc, CodeMirror; // '}' parent: document.head }, - lDir + 'mode/javascript.js', lDir + 'codemirror.css', lDir + 'theme/night.css', + lDir + 'mode/javascript.js', lDir + 'codemirror.js'], function(){ From 34987517c216340e1e2e1025dead07d46e277433 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Sat, 20 Oct 2012 14:07:06 -0400 Subject: [PATCH 041/281] refactored --- client.js | 56 +++++++++++++++----------------------------- lib/client/viewer.js | 52 ++++++++++++++++++++-------------------- 2 files changed, 44 insertions(+), 64 deletions(-) diff --git a/client.js b/client.js index 19516972..d191a511 100644 --- a/client.js +++ b/client.js @@ -282,27 +282,6 @@ CloudClient.Util = (function(){ }; /** - * function loads a couple js files one-by-one - * @pSrc_a ([String1, ..., StringN]) - * @pCallBack (functin) - */ - this.jsloadOnLoad = function (pSrc_a, pCallBack){ - if( this.isArray(pSrc_a) ) { - var n = pSrc_a.length; - - for(var i=0; i Date: Sat, 20 Oct 2012 14:20:10 -0400 Subject: [PATCH 042/281] refactored --- client.js | 13 ++++++---- lib/client/socket.js | 2 +- lib/client/terminal.js | 56 ++++++++++++++++++++++-------------------- lib/client/viewer.js | 2 +- 4 files changed, 39 insertions(+), 34 deletions(-) diff --git a/client.js b/client.js index d191a511..a59e8b93 100644 --- a/client.js +++ b/client.js @@ -465,17 +465,20 @@ CloudClient.Util = (function(){ * все параметры опциональны */ this.cssLoad = function(pParams_o){ - if(pParams_o instanceof Array){ - for(var i=0; i < pParams_o.length; i++){ + if( this.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 this.anyload(pParams_o); - } + } + else if( this.isString(pParams_o) ) + pParams_o = { src: pParams_o }; + pParams_o.name = 'link'; - pParams_o.parent = pParams_o.parent || document.head; + pParams_o.parent = pParams_o.parent || document.head; return this.anyload(pParams_o); }; @@ -1631,7 +1634,7 @@ return CloudClient; try{ window.onload = function(){ - 'use strict'; + 'use strict'; /* базовая инициализация*/ CloudCommander.init(); diff --git a/lib/client/socket.js b/lib/client/socket.js index 4cca6819..9300dec6 100644 --- a/lib/client/socket.js +++ b/lib/client/socket.js @@ -9,7 +9,7 @@ var CloudCommander, io; socket, JqueryTerminal = cloudcmd.Terminal.JqueryTerminal; - Util.jsload("/socket.io/lib/socket.io.js", { + Util.jsload('/socket.io/lib/socket.io.js', { onload : function(){ socket = io.connect(document.location.hostname); diff --git a/lib/client/terminal.js b/lib/client/terminal.js index 4358d07e..0bab5ac5 100644 --- a/lib/client/terminal.js +++ b/lib/client/terminal.js @@ -22,38 +22,40 @@ var CloudCommander, $; * function loads jquery-terminal */ function load(){ - Util.cssLoad({ - src : 'lib/client/terminal/jquery-terminal/jquery.terminal.css' - }); + console.time('terminal load'); + + var lDir = 'lib/client/terminal/jquery-terminal/jquery.'; + + Util.cssLoad(lDir + 'terminal.css'); Util.jsload('lib/client/socket.js'); - var lLoadTerm_func = function(){ - Util.jsload('lib/client/terminal/jquery-terminal/jquery.terminal.js', - function(){ - init(); - - $(function($, undefined) { - Term = JqueryTerminal.Term = $('#terminal').terminal(function(command, term){ - term.echo(''); - cloudcmd.Socket.send(command); - }, { - greetings : '[[;#729FCF;]Cloud Commander Terminal]', - prompt : '[[;#729FCF;]cloudcmd> ]', - color : '#729FCF;' - }); + + Util.anyLoadOnload([ + lDir + 'terminal.js', + lDir + 'mousewheel.js'], + + function(){ + console.timeEnd('terminal load'); + init(); + + $(function($, undefined) { + Term = JqueryTerminal.Term = $('#terminal').terminal(function(command, term){ + term.echo(''); + cloudcmd.Socket.send(command); + }, { + greetings : '[[;#729FCF;]Cloud Commander Terminal]', + prompt : '[[;#729FCF;]cloudcmd> ]', + color : '#729FCF;' }); - /* removing resize function, no need for us */ - Term.resize = function(pEvent){}; - - $(window).unbind('resize'); - - JqueryTerminal.show(); }); - }; - - Util.jsload('lib/client/terminal/jquery-terminal/jquery.mousewheel.js', - lLoadTerm_func); + /* removing resize function, no need for us */ + Term.resize = function(){}; + + $(window).unbind('resize'); + + JqueryTerminal.show(); + }); } /** diff --git a/lib/client/viewer.js b/lib/client/viewer.js index 6264b231..725a99e2 100644 --- a/lib/client/viewer.js +++ b/lib/client/viewer.js @@ -105,7 +105,7 @@ var CloudCommander, CloudFunc, $; * @pCallBack - executes, when everything loaded */ FancyBox.load = function(pCallBack){ - console.time('fancybox load'); + console.time('fancybox load'); var lDir = FancyBox.dir; Util.anyLoadOnload([ From 4f88055899536d4bf02d74384ff7e9c306a23594 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Sat, 20 Oct 2012 14:42:22 -0400 Subject: [PATCH 043/281] added function socketLoad --- client.js | 7 +++++++ lib/client/socket.js | 16 ++++++++++++++-- lib/client/terminal.js | 2 +- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/client.js b/client.js index a59e8b93..6165ed8e 100644 --- a/client.js +++ b/client.js @@ -510,6 +510,10 @@ CloudClient.Util = (function(){ } }); }; + + this.socketLoad = function(){ + Util.jsload('lib/client/socket.js'); + }; this.getByTag = function(pTag, pElement){ return (pElement || document).getElementsByTagName(pTag); @@ -1207,6 +1211,9 @@ CloudClient.init = (function(){ getByClass = Util.getByClass; getById = Util.getById; + + Util.socketLoad(); + if(!document.body.scrollIntoViewIfNeeded){ this.OLD_BROWSER = true; Util.jsload(CloudClient.LIBDIRCLIENT + 'ie.js', diff --git a/lib/client/socket.js b/lib/client/socket.js index 9300dec6..1623e760 100644 --- a/lib/client/socket.js +++ b/lib/client/socket.js @@ -7,8 +7,12 @@ var CloudCommander, io; Util = cloudcmd.Util, Messages = [], socket, - JqueryTerminal = cloudcmd.Terminal.JqueryTerminal; + JqueryTerminal; + function getJqueryTerminal(){ + return cloudcmd.Terminal.JqueryTerminal; + } + Util.jsload('/socket.io/lib/socket.io.js', { onload : function(){ socket = io.connect(document.location.hostname); @@ -16,6 +20,8 @@ var CloudCommander, io; cloudcmd.Socket = socket; socket.on('connect', function () { + JqueryTerminal = getJqueryTerminal(); + outToTerminal({stdout: 'socket connected'}); JqueryTerminal.Term.resume(); @@ -29,6 +35,8 @@ var CloudCommander, io; }); socket.on('disconnect', function () { + JqueryTerminal = getJqueryTerminal(); + outToTerminal({stderr: 'socket disconected'}); JqueryTerminal.Term.pause(); @@ -42,9 +50,13 @@ var CloudCommander, io; }); function outToTerminal(pMsg){ - var lTerm = JqueryTerminal.Term, + var lTerm, lResult = true; + JqueryTerminal = getJqueryTerminal(); + if(JqueryTerminal) + lTerm = JqueryTerminal.Term; + if(lTerm){ var lStdout, lStderr; diff --git a/lib/client/terminal.js b/lib/client/terminal.js index 0bab5ac5..d0fa43b9 100644 --- a/lib/client/terminal.js +++ b/lib/client/terminal.js @@ -28,7 +28,7 @@ var CloudCommander, $; Util.cssLoad(lDir + 'terminal.css'); - Util.jsload('lib/client/socket.js'); + Util.socketLoad(); Util.anyLoadOnload([ From a57df4b5276d49f3fedde23a1cbca98c9d90e1a8 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Sun, 21 Oct 2012 06:45:41 -0400 Subject: [PATCH 044/281] minor changes --- client.js | 13 ++++++------- server.js | 19 ++++++++++--------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/client.js b/client.js index 6165ed8e..bf955d31 100644 --- a/client.js +++ b/client.js @@ -134,9 +134,6 @@ CloudClient.Cache.clear = (function(){ /* Object contain additional system functional */ CloudClient.Util = (function(){ - - /* Load file countent thrue ajax - */ var lXMLHTTP; this.addClass = function(pElement, pClass){ @@ -162,6 +159,7 @@ CloudClient.Util = (function(){ return lRet_b; }; + /* Load file countent thrue ajax */ this.ajax = function(pParams){ /* if on webkit */ if(window.XMLHttpRequest){ @@ -311,7 +309,6 @@ CloudClient.Util = (function(){ return lElements_a; } - /* убираем путь к файлу, оставляя только название файла */ var lName = pParams_o.name, lID = pParams_o.id, lClass = pParams_o.className, @@ -322,17 +319,19 @@ CloudClient.Util = (function(){ lInner = pParams_o.inner, lNotAppend = pParams_o.not_append; + /* убираем путь к файлу, оставляя только название файла */ if(!lID && lSrc) lID = this.getIdBySrc(lSrc); var element = getById(lID); + /* если скрипт еще не загружен */ if(!element) { if(!lName && lSrc){ - var lDot = lSrc.lastIndexOf('.'); - var lExt = lSrc.substr(lDot); + var lDot = lSrc.lastIndexOf('.'), + lExt = lSrc.substr(lDot); switch(lExt){ case '.js': lName = 'script'; @@ -345,7 +344,7 @@ CloudClient.Util = (function(){ return {code: -1, text: 'name can not be empty'}; } } - element = document.createElement(lName); + element = document.createElement(lName); if(lID) element.id = lID; diff --git a/server.js b/server.js index d9a2dc0c..472bbdea 100644 --- a/server.js +++ b/server.js @@ -210,13 +210,12 @@ CloudServer.start = function (pConfig) { * @param pGzip - данные сжаты gzip'ом */ CloudServer.generateHeaders = function(pName, pGzip){ - var lType = ''; - var lCacheControl = 0; - var lContentEncoding = ''; - - /* высылаем заголовок в зависимости от типа файла */ - var lDot = pName.lastIndexOf('.'); - var lExt = pName.substr(lDot); + var lType = '', + lCacheControl = 0, + lContentEncoding = '', + + lDot = pName.lastIndexOf('.'), + lExt = pName.substr(lDot); if(lExt === '.appcache') lCacheControl = 1; @@ -234,7 +233,7 @@ CloudServer.generateHeaders = function(pName, pGzip){ if(!lCacheControl) lCacheControl = 31337 * 21; - + return { /* if type of file any, but img - * then we shoud specify charset @@ -697,7 +696,9 @@ CloudServer.indexReaded = function(pList){ } var lHeader; - /* если браузер поддерживает gzip-сжатие*/ + /* если браузер поддерживает gzip-сжатие + * высылаем заголовок в зависимости от типа файла + */ lHeader = CloudServer.generateHeaders('index.html', CloudServer.Gzip); /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ From 65f5a5030bdca361c654595a2d91ab466741b780 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Sun, 21 Oct 2012 06:50:07 -0400 Subject: [PATCH 045/281] minor changes --- lib/client/editor/_codemirror.js | 6 +++--- lib/client/terminal.js | 12 ++++++------ server.js | 1 + 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/client/editor/_codemirror.js b/lib/client/editor/_codemirror.js index 23ecc1c0..7dccd8d2 100644 --- a/lib/client/editor/_codemirror.js +++ b/lib/client/editor/_codemirror.js @@ -95,9 +95,9 @@ var CloudCommander, CloudFunc, CodeMirror; ); } - /** - * function shows CodeMirror editor - */ + /** + * function shows CodeMirror editor + */ CodeMirrorEditor.show = function(pReadOnly){ if( Util.isBoolean(pReadOnly) ) ReadOnly = pReadOnly; diff --git a/lib/client/terminal.js b/lib/client/terminal.js index d0fa43b9..2c4c2e5f 100644 --- a/lib/client/terminal.js +++ b/lib/client/terminal.js @@ -76,7 +76,7 @@ var CloudCommander, $; console.log('Error. Something went wrong FM not found'); } - /* PUBLICK FUNCTIONS */ + /* PUBLIC FUNCTIONS */ /** * functin show jquery-terminal @@ -113,9 +113,9 @@ var CloudCommander, $; /** * function bind keys */ - cloudcmd.Terminal.Keys = (function(){ + cloudcmd.Terminal.Keys = function(){ /* loading js and css*/ - Util.jqueryLoad(load); + Util.jqueryLoad( load ); var key_event = function(event){ /* если клавиши можно обрабатывать */ @@ -131,14 +131,14 @@ var CloudCommander, $; }; - + /* добавляем обработчик клавишь */ if (document.addEventListener) document.addEventListener('keydown', key_event, false); - + else document.onkeypress = key_event; - }); + }; cloudcmd.Terminal.JqueryTerminal = JqueryTerminal; diff --git a/server.js b/server.js index 472bbdea..f62cd4ac 100644 --- a/server.js +++ b/server.js @@ -706,6 +706,7 @@ CloudServer.indexReaded = function(pList){ Zlib.gzip(pIndex, CloudServer.getGzipDataFunc(lHeader, CloudServer.INDEX)); } + /* если не поддерживаеться - отсылаем данные без сжатия*/ else CloudServer.sendResponse(lHeader, pIndex, CloudServer.INDEX); From ded0980487c9555d229883d4bba12a9fd37d8f5e Mon Sep 17 00:00:00 2001 From: coderaiser Date: Sun, 21 Oct 2012 06:54:16 -0400 Subject: [PATCH 046/281] minor changes --- cloudcmd.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 54334b75..415c15e3 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -8,7 +8,7 @@ var LIBDIRSERVER = './lib/server/', var Config = readConfig(); -Config ? Server.start(Config) : Server.start(); +Server.start(Config); if(update) update.get(); @@ -52,11 +52,10 @@ function readConfig(){ console.log('log param setted up in config.json\n' + 'from now all logs will be writed to log.txt'); writeLogsToFile(); - } - - return lConfig; + } } - else return false; + + return lConfig; } /* function sets stdout to file log.txt */ From 4e41cdea201a3f39b8830a9259ea925d1567db8e Mon Sep 17 00:00:00 2001 From: coderaiser Date: Sun, 21 Oct 2012 06:56:16 -0400 Subject: [PATCH 047/281] minor changes --- server.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/server.js b/server.js index f62cd4ac..6b479741 100644 --- a/server.js +++ b/server.js @@ -3,7 +3,7 @@ /* Обьект содержащий все функции и переменные * серверной части Cloud Commander'а */ -var CloudServer = { +var CloudServer = { /* main Cloud Commander configuration * readed from config.json if it's * exist @@ -90,17 +90,17 @@ var CloudServer = { '.appcache' : 'text/cache-manifest', '.mp3' : 'audio/mpeg' } -}; +}, -var DirPath = '/'; + DirPath = '/', /* модуль для работы с путями*/ -var Path = cloudRequire('path'), + Path = cloudRequire('path'), Fs = cloudRequire('fs'), /* модуль для работы с файловой системой*/ - Querystring = cloudRequire('querystring'); + Querystring = cloudRequire('querystring'), /* node v0.4 not contains zlib */ -var Zlib = cloudRequire('zlib'); /* модуль для сжатия данных gzip-ом*/ + Zlib = cloudRequire('zlib'); /* модуль для сжатия данных gzip-ом*/ if(!Zlib) console.log('to use gzip-commpression' + 'you should use newer node version\n'); From 5302f7962cbaba9cbfc2ca5ab5e84f947095777a Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 22 Oct 2012 05:02:24 -0400 Subject: [PATCH 048/281] rewrited with method anyLoadOnload --- lib/client/menu.js | 52 ++++++++++++++++++-------------------------- lib/client/socket.js | 14 +++++++----- 2 files changed, 30 insertions(+), 36 deletions(-) diff --git a/lib/client/menu.js b/lib/client/menu.js index 320790e7..cd7d4b90 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -103,25 +103,17 @@ var CloudCommander, $; * @param pPosition - position of menu */ function load(){ - var ljsLoad_f = function(){ - var lUISrc = Menu.dir + 'ui.position.js'; - var lMenuSrc = Menu.dir + 'contextMenu.js'; - - Util.jsload(lUISrc, function(){ - Util.jsload(lMenuSrc, Menu.show()); + console.time('menu load'); + + var lDir = Menu.dir; + + Util.anyLoadOnload([ + lDir + 'contextMenu.js', + lDir + 'contextMenu.css'], + function(){ + console.timeEnd('menu load'); + Menu.show(); }); - }; - - var lSrc = Menu.dir + 'contextMenu.css'; - - Util.cssLoad({ - src : lSrc, - func : { - onload: function(){ - Util.jqueryLoad(ljsLoad_f); - } - } - }); } function set(){ @@ -193,16 +185,14 @@ var CloudCommander, $; * right away after loading */ Menu.show = function(){ - return function(){ - set(); - - Util.Images.hideLoad(); - - if(Position && Position.x && Position.y) - $('li').contextMenu(Position); - else - $('li').contextMenu(); - }; + set(); + + Util.Images.hideLoad(); + + if(Position && Position.x && Position.y) + $('li').contextMenu(Position); + else + $('li').contextMenu(); }; /* key binding function */ @@ -246,11 +236,11 @@ var CloudCommander, $; key_event(); }; - /* showing context menu preview*/ - Menu.show(); + /* showing context menu preview*/ + Menu.show(); } - load(); + Util.jqueryLoad( load ); }; cloudcmd.Menu = Menu; diff --git a/lib/client/socket.js b/lib/client/socket.js index 1623e760..8b7170b0 100644 --- a/lib/client/socket.js +++ b/lib/client/socket.js @@ -22,9 +22,11 @@ var CloudCommander, io; socket.on('connect', function () { JqueryTerminal = getJqueryTerminal(); - outToTerminal({stdout: 'socket connected'}); + if(JqueryTerminal){ + outToTerminal({stdout: 'socket connected'}); - JqueryTerminal.Term.resume(); + JqueryTerminal.Term.resume(); + } }); socket.on('message', function (msg) { @@ -37,9 +39,11 @@ var CloudCommander, io; socket.on('disconnect', function () { JqueryTerminal = getJqueryTerminal(); - outToTerminal({stderr: 'socket disconected'}); - - JqueryTerminal.Term.pause(); + if(JqueryTerminal){ + outToTerminal({stderr: 'socket disconected'}); + + JqueryTerminal.Term.pause(); + } }); }, From e41ed386d8555dd47870b7bed5495be666df97de Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 24 Oct 2012 03:43:26 -0400 Subject: [PATCH 049/281] added functions for work with strings --- client.js | 50 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/client.js b/client.js index bf955d31..205830a2 100644 --- a/client.js +++ b/client.js @@ -513,17 +513,28 @@ CloudClient.Util = (function(){ this.socketLoad = function(){ Util.jsload('lib/client/socket.js'); }; - + + /* DOM */ + + /** + * Function search element by tag + * @pTag - className + * @pElement - element + */ this.getByTag = function(pTag, pElement){ return (pElement || document).getElementsByTagName(pTag); }; + /** + * Function search element by id + * @Id - className + * @pElement - element + */ this.getById = function(pId, pElement){ return (pElement || document).getElementById(pId); }; - - /* + /** * Function search element by class name * @pClass - className * @pElement - element @@ -531,7 +542,38 @@ CloudClient.Util = (function(){ this.getByClass = function(pClass, pElement){ return (pElement || document).getElementsByClassName(pClass); }; - + + /* STRINGS */ + + /** + * function check is strings are equal + * @param pStr1 + * @param pStr2 + */ + this.strCmp = function (pStr1, pStr2){ + return isContainStr(pStr1, pStr2) && + pStr1.length == pStr2.length; + } + /** + * function returns is pStr1 contains pStr2 + * @param pStr1 + * @param pStr2 + */ + function isContainStr(pStr1, pStr2){ + return str1 && + str2 && + str1.indexOf(str2) === 0; + } + + /** + * function remove substring from string + * @param pStr + * @param pSubStr + */ + function removeStr(pStr, pSubStr){ + return pStr.replace(pSubStr,''); + } + /* private members */ var lLoadingImage; var lErrorImage; From 708ff794799aedc9db2395cf1e4796f001e70c00 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 24 Oct 2012 07:11:42 -0400 Subject: [PATCH 050/281] minor changes --- client.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client.js b/client.js index 205830a2..89779387 100644 --- a/client.js +++ b/client.js @@ -560,9 +560,9 @@ CloudClient.Util = (function(){ * @param pStr2 */ function isContainStr(pStr1, pStr2){ - return str1 && - str2 && - str1.indexOf(str2) === 0; + return pStr1 && + pStr2 && + pStr1.indexOf(pStr2) > 0; } /** From 8b2c3d2faafbb3f396b7d625c240dd0b1fa21e62 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 25 Oct 2012 04:40:14 -0400 Subject: [PATCH 051/281] added github module --- client.js | 146 +++++++++++++++++++++------------------ lib/client/keyBinding.js | 6 ++ 2 files changed, 84 insertions(+), 68 deletions(-) diff --git a/client.js b/client.js index 89779387..5adb0b45 100644 --- a/client.js +++ b/client.js @@ -16,6 +16,7 @@ var CloudClient = { KeyBinding : null, /* обьект обработки нажатий клавишь */ Config : null, /* function loads and shows config */ Editor : null, /* function loads and shows editor */ + Storage : null, /* function loads storage */ Viewer : null, /* function loads and shows viewer */ Terminal : null, /* function loads and shows terminal*/ Menu : null, /* function loads and shows menu */ @@ -60,6 +61,7 @@ var cloudcmd = CloudClient; /* глобальные переменные */ var CloudFunc, $, Util, KeyBinding, + /* short names used all the time functions */ getByClass, getById; @@ -69,7 +71,7 @@ var CloudFunc, $, Util, KeyBinding, * работы с LocalStorage, webdb, * indexed db etc. */ -CloudClient.Cache = { +CloudClient.Cache = { _allowed : true, /* приватный переключатель возможности работы с кэшем */ /* функция проверяет возможно ли работать с кэшем каким-либо образом */ @@ -89,8 +91,8 @@ CloudClient.Cache = { }; -/* функция проверяет поддерживаеться ли localStorage */ -CloudClient.Cache.isAllowed = (function(){ +/** функция проверяет поддерживаеться ли localStorage */ +CloudClient.Cache.isAllowed = (function(){ if(window.localStorage && localStorage.setItem && localStorage.getItem){ @@ -109,31 +111,34 @@ CloudClient.Cache.isAllowed = (function(){ */ } }); - /* если доступен localStorage и + + /** если доступен localStorage и * в нём есть нужная нам директория - * записываем данные в него */ -CloudClient.Cache.set = (function(pName, pData){ +CloudClient.Cache.set = function(pName, pData){ if(CloudClient.Cache._allowed && pName && pData){ localStorage.setItem(pName,pData); } -}); -/* Если доступен Cache принимаем из него данные*/ -CloudClient.Cache.get = (function(pName){ +}; + +/** Если доступен Cache принимаем из него данные*/ +CloudClient.Cache.get = function(pName){ if(CloudClient.Cache._allowed && pName){ return localStorage.getItem(pName); } else return null; -}); -/* Функция очищает кэш*/ -CloudClient.Cache.clear = (function(){ +}; + +/** Функция очищает кэш */ +CloudClient.Cache.clear = function(){ if(CloudClient.Cache._allowed){ localStorage.clear(); } -}); +}; /* Object contain additional system functional */ -CloudClient.Util = (function(){ +CloudClient.Util = (function(){ var lXMLHTTP; this.addClass = function(pElement, pClass){ @@ -987,20 +992,20 @@ CloudClient.Util = (function(){ }; }); -CloudClient.Util = new CloudClient.Util(); +CloudClient.Util = new CloudClient.Util(); -/* функция обработки нажатий клавишь */ -CloudClient.KeyBinding = (function(){ +/** функция обработки нажатий клавишь */ +CloudClient.KeyBinding = function(){ /* loading keyBinding module and start it */ Util.jsload(CloudClient.LIBDIRCLIENT+'keyBinding.js', function(){ cloudcmd.KeyBinding.init(); KeyBinding = cloudcmd.KeyBinding; }); -}); +}; -/* function loads and shows editor */ -CloudClient.Editor = (function(pIsReadOnly) { +/** function loads and shows editor */ +CloudClient.Editor = function(pIsReadOnly) { /* loading CloudMirror plagin */ Util.jsload(CloudClient.LIBDIRCLIENT + 'editor/_codemirror.js',{ @@ -1009,10 +1014,10 @@ CloudClient.Editor = (function(pIsReadOnly) { cloudcmd.Editor.Keys(pIsReadOnly); }) }); -}); +}; -CloudClient.Config = (function() { +CloudClient.Config = function() { Util.Images.showLoad({top: true}); Util.jsload(CloudClient.LIBDIRCLIENT + 'config.js',{ @@ -1020,9 +1025,9 @@ CloudClient.Config = (function() { cloudcmd.Config.Keys(); } }); -}); +}; -CloudClient.GoogleAnalytics = (function(){ +CloudClient.GoogleAnalytics = function(){ /* google analytics */ var lFunc = document.onmousemove; document.onmousemove = function(){ @@ -1035,45 +1040,53 @@ CloudClient.GoogleAnalytics = (function(){ document.onmousemove = lFunc; }; -}); +}; -/* function loads and shows viewer */ -CloudClient.Viewer = (function(pCurrentFile){ +/** function loads and shows viewer */ +CloudClient.Viewer = function(pCurrentFile){ Util.jsload(CloudClient.LIBDIRCLIENT + 'viewer.js',{ onload: (function(){ cloudcmd.Viewer.Keys(pCurrentFile); }) }); -}); +}; -/* function loads and shows terminal */ -CloudClient.Terminal = (function(){ +/** function loads and shows storage */ +CloudClient.Storage = function(){ + Util.jsload(CloudClient.LIBDIRCLIENT + 'storage/_github.js', + function(){ + cloudcmd.Storage.Keys(); + }); +}; + +/** function loads and shows terminal */ +CloudClient.Terminal = function(){ Util.jsload(CloudClient.LIBDIRCLIENT + 'terminal.js',{ onload: (function(){ cloudcmd.Terminal.Keys(); }) }); -}); +}; -/* function loads and shows menu - * @pPosition - coordinates of menu {x, y} +/** function loads and shows menu + * @param Position - coordinates of menu {x, y} */ -CloudClient.Menu = (function(pPosition){ +CloudClient.Menu = function(pPosition){ Util.jsload(CloudClient.LIBDIRCLIENT + 'menu.js',{ onload: (function(){ cloudcmd.Menu.Keys(pPosition); }) }); -}); +}; -/* +/** * Функция привязываеться ко всем ссылкам и * загружает содержимое каталогов */ -CloudClient._loadDir = (function(pLink,pNeedRefresh){ +CloudClient._loadDir = function(pLink,pNeedRefresh){ /* @pElem - элемент, * для которого нужно * выполнить загрузку @@ -1120,16 +1133,16 @@ CloudClient._loadDir = (function(pLink,pNeedRefresh){ */ return false; }; - }); - + }; -/* + +/** * Function edits file name * - * @pParent - parent element - * @pEvent + * @param pParent - parent element + * @param pEvent */ -CloudClient._editFileName = (function(pParent){ +CloudClient._editFileName = function(pParent){ var lA = Util.getCurrentLink(pParent); if (lA && lA.textContent !== '..'){ @@ -1160,12 +1173,12 @@ CloudClient._editFileName = (function(pParent){ }); } -}); +}; /* Функция устанавливает текущим файлом, тот * на который кликнули единожды */ -CloudClient._setCurrent=(function(){ +CloudClient._setCurrent = function(){ /* * @pFromEnter - если мы сюда попали * из события нажатия на энтер - @@ -1215,14 +1228,14 @@ CloudClient._setCurrent=(function(){ */ return false; }; - }); + }; -/* функция устанавливает курсор на каталог +/** функция устанавливает курсор на каталог * с которого мы пришли, если мы поднялись * в верх по файловой структуре - * @pDirName - имя каталога с которого мы пришли + * @param pDirName - имя каталога с которого мы пришли */ -CloudClient._currentToParent = (function(pDirName){ +CloudClient._currentToParent = function(pDirName){ /* опредиляем в какой мы панели: * правой или левой */ @@ -1240,13 +1253,13 @@ CloudClient._currentToParent = (function(pDirName){ Util.setCurrentFile(lRootDir); Util.scrollIntoViewIfNeeded(lRootDir, true); } -}); +}; -/* Конструктор CloudClient, который +/** Конструктор CloudClient, который * выполняет весь функционал по * инициализации */ -CloudClient.init = (function(){ +CloudClient.init = function(){ Util = cloudcmd.Util; KeyBinding = cloudcmd.KeyBinding; getByClass = Util.getByClass; @@ -1263,9 +1276,9 @@ CloudClient.init = (function(){ }); } else CloudClient.baseInit(); -}); +}; -CloudClient.baseInit = (function(){ +CloudClient.baseInit = function(){ if(applicationCache){ var lFunc = applicationCache.onupdateready; applicationCache.onupdateready = function(){ @@ -1333,10 +1346,10 @@ CloudClient.baseInit = (function(){ 'height:' + lHeight +'px;' + '}' }); -}); +}; /* функция меняет ссыки на ajax-овые */ -CloudClient._changeLinks = function(pPanelID){ +CloudClient._changeLinks = function(pPanelID){ /* назначаем кнопку очистить кэш и показываем её */ var lClearcache = getById('clear-cache'); if(lClearcache) @@ -1477,14 +1490,13 @@ CloudClient._changeLinks = function(pPanelID){ } }; -/* +/** * Функция загружает json-данные о Файловой Системе * через ajax-запрос. - * @path - каталог для чтения - * @pNeedRefresh - необходимость обновить данные о каталоге + * @param path - каталог для чтения + * @param pNeedRefresh - необходимость обновить данные о каталоге */ -CloudClient._ajaxLoad = function(path, pNeedRefresh) -{ +CloudClient._ajaxLoad = function(path, pNeedRefresh){ /* Отображаем красивые пути */ /* added supporting of russian language */ var lPath = decodeURI(path); @@ -1567,13 +1579,12 @@ CloudClient._ajaxLoad = function(path, pNeedRefresh) }catch(err){console.log(err);} }; -/* +/** * Функция строит файловую таблицу - * @pEleme - родительский элемент - * @pJSON - данные о файлах + * @param pEleme - родительский элемент + * @param pJSON - данные о файлах */ -CloudClient._createFileTable = function(pElem, pJSON) -{ +CloudClient._createFileTable = function(pElem, pJSON){ var lElem = getById(pElem); /* getting current element if was refresh */ @@ -1608,12 +1619,11 @@ CloudClient._createFileTable = function(pElem, pJSON) } }; -/* +/** * Функция генерирует JSON из html-таблицы файлов и * используеться при первом заходе в корень */ -CloudClient._getJSONfromFileTable=function() -{ +CloudClient._getJSONfromFileTable = function(){ var lLeft = getById('left'); var lPath = getByClass('path')[0].textContent; var lFileTable = [{path:lPath,size:'dir'}]; diff --git a/lib/client/keyBinding.js b/lib/client/keyBinding.js index 08965991..a94bea43 100644 --- a/lib/client/keyBinding.js +++ b/lib/client/keyBinding.js @@ -24,6 +24,8 @@ var CloudCommander; D : 68, + G : 71, + O : 79, Q : 81, R : 82, @@ -78,6 +80,10 @@ var CloudCommander; } + else if(lKeyCode === KEY.G && event.altKey){ + + } + /* если нажали таб: * переносим курсор на * правую панель, если From 3de4a94a924f1b532a4a26fd543de2d76f35202c Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 25 Oct 2012 04:44:53 -0400 Subject: [PATCH 052/281] minor changes --- lib/client/keyBinding.js | 3 +++ lib/client/viewer.js | 9 ++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/client/keyBinding.js b/lib/client/keyBinding.js index a94bea43..f863869e 100644 --- a/lib/client/keyBinding.js +++ b/lib/client/keyBinding.js @@ -81,7 +81,10 @@ var CloudCommander; } else if(lKeyCode === KEY.G && event.altKey){ + var lStorage = cloudcmd.Storage; + if( Util.isFunction(lStorage) ) + lStorage(); } /* если нажали таб: diff --git a/lib/client/viewer.js b/lib/client/viewer.js index 725a99e2..d80c83ca 100644 --- a/lib/client/viewer.js +++ b/lib/client/viewer.js @@ -205,10 +205,9 @@ var CloudCommander, CloudFunc, $; }; cloudcmd.Viewer.Keys = function(){ - var lCallBack_f = (function(){ - - var lF3 = cloudcmd.KEY.F3; - var key_event = function(pEvent){ + var lCallBack_f = function(){ + var lF3 = cloudcmd.KEY.F3, + key_event = function(pEvent){ /* если клавиши можно обрабатывать */ if( KeyBinding.get() ) if(pEvent.keyCode === lF3 && pEvent.shiftKey){ @@ -239,7 +238,7 @@ var CloudCommander, CloudFunc, $; /* showing images preview*/ FancyBox.show(); Util.Images.hideLoad(); - }); + }; Util.jqueryLoad(function(){ FancyBox.load(lCallBack_f); From a8e83e16868ebfe0f1d58992ca9e478ffcac1fb2 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 25 Oct 2012 05:25:11 -0400 Subject: [PATCH 053/281] fixed github module --- client.js | 4 ++-- lib/client/editor/_codemirror.js | 2 +- lib/client/menu.js | 2 +- lib/client/terminal.js | 2 +- lib/client/viewer.js | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/client.js b/client.js index 5adb0b45..88886a09 100644 --- a/client.js +++ b/client.js @@ -264,7 +264,7 @@ CloudClient.Util = (function(){ return pFunc_a(); }; - this.anyLoadOnload = function(pParams_a, pFunc){ + this.anyLoadOnLoad = function(pParams_a, pFunc){ if( this.isArray(pParams_a) ) { var lParam = pParams_a.pop(); @@ -274,7 +274,7 @@ CloudClient.Util = (function(){ if(lParam && !lParam.func){ lParam.func = function(){ - Util.anyLoadOnload(pParams_a, pFunc); + Util.anyLoadOnLoad(pParams_a, pFunc); }; this.anyload(lParam); diff --git a/lib/client/editor/_codemirror.js b/lib/client/editor/_codemirror.js index 7dccd8d2..c782b0db 100644 --- a/lib/client/editor/_codemirror.js +++ b/lib/client/editor/_codemirror.js @@ -63,7 +63,7 @@ var CloudCommander, CloudFunc, CodeMirror; console.time('codemirror load'); var lDir = CodeMirrorEditor.dir; - Util.anyLoadOnload( + Util.anyLoadOnLoad( [{ name: 'style', id:'editor', diff --git a/lib/client/menu.js b/lib/client/menu.js index cd7d4b90..caf2fded 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -107,7 +107,7 @@ var CloudCommander, $; var lDir = Menu.dir; - Util.anyLoadOnload([ + Util.anyLoadOnLoad([ lDir + 'contextMenu.js', lDir + 'contextMenu.css'], function(){ diff --git a/lib/client/terminal.js b/lib/client/terminal.js index 2c4c2e5f..d5c13ca8 100644 --- a/lib/client/terminal.js +++ b/lib/client/terminal.js @@ -31,7 +31,7 @@ var CloudCommander, $; Util.socketLoad(); - Util.anyLoadOnload([ + Util.anyLoadOnLoad([ lDir + 'terminal.js', lDir + 'mousewheel.js'], diff --git a/lib/client/viewer.js b/lib/client/viewer.js index d80c83ca..4ae1afd1 100644 --- a/lib/client/viewer.js +++ b/lib/client/viewer.js @@ -108,7 +108,7 @@ var CloudCommander, CloudFunc, $; console.time('fancybox load'); var lDir = FancyBox.dir; - Util.anyLoadOnload([ + Util.anyLoadOnLoad([ lDir + 'jquery.fancybox.css', lDir + 'jquery.fancybox.js'], From 15660eb8185407fe806a5160465b1065374a084c Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 25 Oct 2012 06:38:03 -0400 Subject: [PATCH 054/281] added private function loadModule --- client.js | 132 +++++++++++++++++++++++++++--------------------------- 1 file changed, 67 insertions(+), 65 deletions(-) diff --git a/client.js b/client.js index 88886a09..a1a5c7d1 100644 --- a/client.js +++ b/client.js @@ -994,43 +994,85 @@ CloudClient.Util = (function(){ CloudClient.Util = new CloudClient.Util(); + +/** + * function load modules + * @pParams = {name, src, callback, arg} + */ +var loadModule = function(pParams){ + var lName = pParams.name, + lSrc = pParams.src, + lFunc = pParams.func; + + return function(pArg){ + Util.jsload(CloudClient.LIBDIRCLIENT + lSrc, lFunc || + function(){ + cloudcmd[lName].Keys(pArg); + }); + }; +}; + /** функция обработки нажатий клавишь */ -CloudClient.KeyBinding = function(){ - /* loading keyBinding module and start it */ - Util.jsload(CloudClient.LIBDIRCLIENT+'keyBinding.js', function(){ +CloudClient.KeyBinding = loadModule({ + name : 'KeyBinding', + src : 'keyBinding.js', + func : function(){ cloudcmd.KeyBinding.init(); KeyBinding = cloudcmd.KeyBinding; - }); -}; + } +}); /** function loads and shows editor */ -CloudClient.Editor = function(pIsReadOnly) { - /* loading CloudMirror plagin */ - Util.jsload(CloudClient.LIBDIRCLIENT + - 'editor/_codemirror.js',{ - //'editor/_ace.js',{ - onload:(function(){ - cloudcmd.Editor.Keys(pIsReadOnly); - }) - }); -}; +CloudClient.Editor = loadModule({ + name : 'Editor', + src :'editor/_codemirror.js' +}); -CloudClient.Config = function() { - Util.Images.showLoad({top: true}); - Util.jsload(CloudClient.LIBDIRCLIENT + - 'config.js',{ - onload: function(){ - cloudcmd.Config.Keys(); - } - }); -}; +/** function loads and shows viewer */ +CloudClient.Viewer = loadModule({ + name : 'Viewer', + src : 'viewer.js' +}); + +/** function loads and shows storage */ +CloudClient.Storage = loadModule({ + name : 'Storage', + src : 'storage/_github.js' +}); + +/** function loads and shows terminal */ +CloudClient.Terminal = loadModule({ + name : 'Terminal', + src : 'terminal.js' +}); + +/** function loads and shows menu + * @param Position - coordinates of menu {x, y} + */ +CloudClient.Menu = loadModule({ + name : 'Menu', + src : 'menu.js' +}); + +CloudClient.Config = loadModule({ + src : 'config.js', + func : function(){ + Util.Images.showLoad({top: true}); + Util.jsload(CloudClient.LIBDIRCLIENT + + 'config.js',{ + onload: function(){ + cloudcmd.Config.Keys(); + } + }); + } +}); CloudClient.GoogleAnalytics = function(){ /* google analytics */ var lFunc = document.onmousemove; - document.onmousemove = function(){ + document.onmousemove = function(){ setTimeout(function(){ Util.jsload('lib/client/google_analytics.js'); },5000); @@ -1042,46 +1084,6 @@ CloudClient.GoogleAnalytics = function(){ }; }; -/** function loads and shows viewer */ -CloudClient.Viewer = function(pCurrentFile){ - Util.jsload(CloudClient.LIBDIRCLIENT + - 'viewer.js',{ - onload: (function(){ - cloudcmd.Viewer.Keys(pCurrentFile); - }) - }); -}; - -/** function loads and shows storage */ -CloudClient.Storage = function(){ - Util.jsload(CloudClient.LIBDIRCLIENT + 'storage/_github.js', - function(){ - cloudcmd.Storage.Keys(); - }); -}; - -/** function loads and shows terminal */ -CloudClient.Terminal = function(){ - Util.jsload(CloudClient.LIBDIRCLIENT + - 'terminal.js',{ - onload: (function(){ - cloudcmd.Terminal.Keys(); - }) - }); -}; - -/** function loads and shows menu - * @param Position - coordinates of menu {x, y} - */ -CloudClient.Menu = function(pPosition){ - Util.jsload(CloudClient.LIBDIRCLIENT + - 'menu.js',{ - onload: (function(){ - cloudcmd.Menu.Keys(pPosition); - }) - }); -}; - /** * Функция привязываеться ко всем ссылкам и * загружает содержимое каталогов From b58b298319d661280e1906112c4ae730e724614b Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 25 Oct 2012 06:42:48 -0400 Subject: [PATCH 055/281] refactored --- client.js | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/client.js b/client.js index a1a5c7d1..0c330047 100644 --- a/client.js +++ b/client.js @@ -997,14 +997,18 @@ CloudClient.Util = new CloudClient.Util(); /** * function load modules - * @pParams = {name, src, callback, arg} + * @pParams = {name, src, func, dobefore, arg} */ var loadModule = function(pParams){ var lName = pParams.name, lSrc = pParams.src, - lFunc = pParams.func; + lFunc = pParams.func, + lDoBefore = pParams.dobefore; return function(pArg){ + if( Util.isFunction(lDoBefore) ) + lDoBefore(); + Util.jsload(CloudClient.LIBDIRCLIENT + lSrc, lFunc || function(){ cloudcmd[lName].Keys(pArg); @@ -1017,9 +1021,8 @@ CloudClient.KeyBinding = loadModule({ name : 'KeyBinding', src : 'keyBinding.js', func : function(){ - cloudcmd.KeyBinding.init(); - - KeyBinding = cloudcmd.KeyBinding; + KeyBinding = cloudcmd.KeyBinding; + KeyBinding.init(); } }); @@ -1057,21 +1060,17 @@ CloudClient.Menu = loadModule({ }); CloudClient.Config = loadModule({ - src : 'config.js', - func : function(){ + name : 'Config', + src : 'config.js', + dobefore : function(){ Util.Images.showLoad({top: true}); - Util.jsload(CloudClient.LIBDIRCLIENT + - 'config.js',{ - onload: function(){ - cloudcmd.Config.Keys(); - } - }); } }); CloudClient.GoogleAnalytics = function(){ /* google analytics */ var lFunc = document.onmousemove; + document.onmousemove = function(){ setTimeout(function(){ Util.jsload('lib/client/google_analytics.js'); From 79d7124be27261a8efa98939a04f2f52a41e2b24 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 25 Oct 2012 06:58:33 -0400 Subject: [PATCH 056/281] minor changes --- ChangeLog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ChangeLog b/ChangeLog index 59ff7f0a..74597729 100644 --- a/ChangeLog +++ b/ChangeLog @@ -29,6 +29,12 @@ started. * Added function jsLoadOnLoad, thet loads js files one-by-one, and then calls callback if needed. +* Added function anyloadOnLoad thet loads any files or +elements one-by-one. + +* Added function loadModule thet make it easier to connect +new module. + 2012.10.01, Version 0.1.7 From 8ea555e9ca662f8a0cff61050a345cde23c3f265 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 25 Oct 2012 07:55:55 -0400 Subject: [PATCH 057/281] improved module loading mechanism --- client.js | 156 +++++++++++++++++++++++++++--------------------------- 1 file changed, 78 insertions(+), 78 deletions(-) diff --git a/client.js b/client.js index 0c330047..a5b79f64 100644 --- a/client.js +++ b/client.js @@ -65,6 +65,71 @@ var CloudFunc, $, Util, KeyBinding, /* short names used all the time functions */ getByClass, getById; + +/** + * function load modules + * @pParams = {name, path, func, dobefore, arg} + */ +var loadModule = function(pParams){ + if(!pParams) return; + + var lName = pParams.name, + lPath = pParams.path, + lFunc = pParams.func, + lDoBefore = pParams.dobefore; + + if( Util.isString(pParams) ) + lPath = pParams; + + if(lPath && !lName){ + lName = lPath[0].toUpperCase() + lPath.substring(1); + lName = lName.replace('.js', ''); + + var lSlash = lName.indexOf('/'); + if(lSlash > 0){ + var lAfterSlash = lName.substr(lSlash); + lName = lName.replace(lAfterSlash, ''); + } + } + + if( !Util.isContainStr(lPath, '.js') ) + lPath += '.js'; + + cloudcmd[lName] = function(pArg){ + if( Util.isFunction(lDoBefore) ) + lDoBefore(); + + Util.jsload(CloudClient.LIBDIRCLIENT + lPath, lFunc || + function(){ + cloudcmd[lName].Keys(pArg); + }); + }; +}; + +var Modules = [{ + path : 'keyBinding.js', + func : function(){ + KeyBinding = cloudcmd.KeyBinding; + KeyBinding.init(); + } + }, + 'editor/_codemirror', + 'viewer', + 'storage/_github', + 'terminal', + 'menu', + { + path : 'config', + dobefore : function(){ + Util.Images.showLoad({top: true}); + } + } +]; + + + + + /* * Обьект для работы с кэшем * в него будут включены функции для @@ -556,28 +621,29 @@ CloudClient.Util = (function(){ * @param pStr2 */ this.strCmp = function (pStr1, pStr2){ - return isContainStr(pStr1, pStr2) && + return this.isContainStr(pStr1, pStr2) && pStr1.length == pStr2.length; - } + }; + /** * function returns is pStr1 contains pStr2 * @param pStr1 * @param pStr2 */ - function isContainStr(pStr1, pStr2){ + this.isContainStr = function(pStr1, pStr2){ return pStr1 && pStr2 && pStr1.indexOf(pStr2) > 0; - } + }; /** * function remove substring from string * @param pStr * @param pSubStr */ - function removeStr(pStr, pSubStr){ + this.removeStr = function(pStr, pSubStr){ return pStr.replace(pSubStr,''); - } + }; /* private members */ var lLoadingImage; @@ -995,78 +1061,6 @@ CloudClient.Util = (function(){ CloudClient.Util = new CloudClient.Util(); -/** - * function load modules - * @pParams = {name, src, func, dobefore, arg} - */ -var loadModule = function(pParams){ - var lName = pParams.name, - lSrc = pParams.src, - lFunc = pParams.func, - lDoBefore = pParams.dobefore; - - return function(pArg){ - if( Util.isFunction(lDoBefore) ) - lDoBefore(); - - Util.jsload(CloudClient.LIBDIRCLIENT + lSrc, lFunc || - function(){ - cloudcmd[lName].Keys(pArg); - }); - }; -}; - -/** функция обработки нажатий клавишь */ -CloudClient.KeyBinding = loadModule({ - name : 'KeyBinding', - src : 'keyBinding.js', - func : function(){ - KeyBinding = cloudcmd.KeyBinding; - KeyBinding.init(); - } -}); - -/** function loads and shows editor */ -CloudClient.Editor = loadModule({ - name : 'Editor', - src :'editor/_codemirror.js' -}); - - -/** function loads and shows viewer */ -CloudClient.Viewer = loadModule({ - name : 'Viewer', - src : 'viewer.js' -}); - -/** function loads and shows storage */ -CloudClient.Storage = loadModule({ - name : 'Storage', - src : 'storage/_github.js' -}); - -/** function loads and shows terminal */ -CloudClient.Terminal = loadModule({ - name : 'Terminal', - src : 'terminal.js' -}); - -/** function loads and shows menu - * @param Position - coordinates of menu {x, y} - */ -CloudClient.Menu = loadModule({ - name : 'Menu', - src : 'menu.js' -}); - -CloudClient.Config = loadModule({ - name : 'Config', - src : 'config.js', - dobefore : function(){ - Util.Images.showLoad({top: true}); - } -}); - CloudClient.GoogleAnalytics = function(){ /* google analytics */ var lFunc = document.onmousemove; @@ -1347,6 +1341,12 @@ CloudClient.baseInit = function(){ 'height:' + lHeight +'px;' + '}' }); + + + for(var i = 0, n = Modules.length; i < n ; i++){ + loadModule(Modules[i]); + } + }; /* функция меняет ссыки на ajax-овые */ From aea3d8d9ffa4018cf6995d53caf62631d6a636a2 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 25 Oct 2012 09:20:48 -0400 Subject: [PATCH 058/281] modules moved to modules.json --- client.js | 60 ++++++++++++++++++---------------------- lib/client/keyBinding.js | 2 +- modules.json | 8 ++++++ 3 files changed, 36 insertions(+), 34 deletions(-) create mode 100644 modules.json diff --git a/client.js b/client.js index a5b79f64..0941ec14 100644 --- a/client.js +++ b/client.js @@ -106,30 +106,6 @@ var loadModule = function(pParams){ }; }; -var Modules = [{ - path : 'keyBinding.js', - func : function(){ - KeyBinding = cloudcmd.KeyBinding; - KeyBinding.init(); - } - }, - 'editor/_codemirror', - 'viewer', - 'storage/_github', - 'terminal', - 'menu', - { - path : 'config', - dobefore : function(){ - Util.Images.showLoad({top: true}); - } - } -]; - - - - - /* * Обьект для работы с кэшем * в него будут включены функции для @@ -1267,10 +1243,34 @@ CloudClient.init = function(){ this.OLD_BROWSER = true; Util.jsload(CloudClient.LIBDIRCLIENT + 'ie.js', function(){ - Util.jqueryLoad(CloudClient.baseInit); + Util.jqueryLoad( CloudClient.initModules ); }); } - else CloudClient.baseInit(); + else CloudClient.initModules(); +}; + +CloudClient.initModules = function(){ + + loadModule({ + path : 'keyBinding.js', + func : function(){ + KeyBinding = cloudcmd.KeyBinding; + KeyBinding.init(); + } + }); + + Util.ajax({ + url:'/modules.json', + success: function(pModules){ + if( Util.isArray(pModules) ) + for(var i = 0, n = pModules.length; i < n ; i++) + loadModule(pModules[i]); + + CloudClient.baseInit(); + }, + + error: CloudClient.baseInit + }); }; CloudClient.baseInit = function(){ @@ -1340,13 +1340,7 @@ CloudClient.baseInit = function(){ '.panel{' + 'height:' + lHeight +'px;' + '}' - }); - - - for(var i = 0, n = Modules.length; i < n ; i++){ - loadModule(Modules[i]); - } - + }); }; /* функция меняет ссыки на ajax-овые */ diff --git a/lib/client/keyBinding.js b/lib/client/keyBinding.js index f863869e..7917ec73 100644 --- a/lib/client/keyBinding.js +++ b/lib/client/keyBinding.js @@ -74,7 +74,7 @@ var CloudCommander; if(lKeyCode === KEY.O && event.altKey){ console.log('openning config window...'); - Util.Images.showLoad(); + Util.Images.showLoad({top: true}); if ( Util.isFunction(cloudcmd.Config) ) cloudcmd.Config(); diff --git a/modules.json b/modules.json new file mode 100644 index 00000000..f3b987cc --- /dev/null +++ b/modules.json @@ -0,0 +1,8 @@ +[ + "editor/_codemirror", + "viewer", + "storage/_github", + "terminal", + "menu", + "config" +] \ No newline at end of file From 5d99d5750caaa5ae77710384728c77d4bb16dcc3 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 25 Oct 2012 09:35:24 -0400 Subject: [PATCH 059/281] fixed bug with context menu hiding (on Esc) --- ChangeLog | 2 ++ client.js | 16 ++++++++-------- lib/client/menu.js | 18 ++++++++++-------- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/ChangeLog b/ChangeLog index 74597729..0910cd21 100644 --- a/ChangeLog +++ b/ChangeLog @@ -35,6 +35,8 @@ elements one-by-one. * Added function loadModule thet make it easier to connect new module. +* Fixed bug with context menu hiding (on Esc). Keys was unbinded. + 2012.10.01, Version 0.1.7 diff --git a/client.js b/client.js index 0941ec14..287bbbc0 100644 --- a/client.js +++ b/client.js @@ -1243,13 +1243,13 @@ CloudClient.init = function(){ this.OLD_BROWSER = true; Util.jsload(CloudClient.LIBDIRCLIENT + 'ie.js', function(){ - Util.jqueryLoad( CloudClient.initModules ); + Util.jqueryLoad( initModules ); }); } - else CloudClient.initModules(); + else initModules(); }; -CloudClient.initModules = function(){ +function initModules(){ loadModule({ path : 'keyBinding.js', @@ -1266,14 +1266,14 @@ CloudClient.initModules = function(){ for(var i = 0, n = pModules.length; i < n ; i++) loadModule(pModules[i]); - CloudClient.baseInit(); + baseInit(); }, - error: CloudClient.baseInit + error: baseInit }); -}; +} -CloudClient.baseInit = function(){ +function baseInit(){ if(applicationCache){ var lFunc = applicationCache.onupdateready; applicationCache.onupdateready = function(){ @@ -1341,7 +1341,7 @@ CloudClient.baseInit = function(){ 'height:' + lHeight +'px;' + '}' }); -}; +} /* функция меняет ссыки на ajax-овые */ CloudClient._changeLinks = function(pPanelID){ diff --git a/lib/client/menu.js b/lib/client/menu.js index caf2fded..dd8259b8 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -47,6 +47,7 @@ var CloudCommander, $; KeyBinding.set(); }, + // define the elements of the menu items: { view: {name: 'View', callback: function(key, opt){ @@ -208,16 +209,17 @@ var CloudCommander, $; var key_event = (function(pEvent){ /* если клавиши можно обрабатывать */ - if( KeyBinding.get() ) + if( KeyBinding.get() ){ /* if shift + F10 pressed */ - if(pEvent.keyCode === cloudcmd.KEY.F10 && - pEvent.shiftKey){ - var lCurrent = Util.getCurrentFile(); - if(lCurrent) - $(lCurrent).contextMenu(); - - pEvent.preventDefault(); + if(pEvent.keyCode === cloudcmd.KEY.F10 && pEvent.shiftKey){ + var lCurrent = Util.getCurrentFile(); + if(lCurrent) + $(lCurrent).contextMenu(); + pEvent.preventDefault(); + } } + else if (pEvent.keyCode === cloudcmd.KEY.ESC) + KeyBinding.set(); }); /* добавляем обработчик клавишь */ From 518903566f6d283bd97a2214927e6364b9e18e9c Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 25 Oct 2012 10:01:36 -0400 Subject: [PATCH 060/281] added ability for basic autorithation on github --- lib/client/storage/_github.js | 62 + lib/client/storage/github/LICENSE | 25 + lib/client/storage/github/README.md | 272 ++++ lib/client/storage/github/github.js | 464 +++++++ lib/client/storage/github/lib/base64.js | 82 ++ .../storage/github/lib/underscore-min.js | 31 + lib/client/storage/github/lib/underscore.js | 1189 +++++++++++++++++ 7 files changed, 2125 insertions(+) create mode 100644 lib/client/storage/_github.js create mode 100644 lib/client/storage/github/LICENSE create mode 100644 lib/client/storage/github/README.md create mode 100644 lib/client/storage/github/github.js create mode 100644 lib/client/storage/github/lib/base64.js create mode 100644 lib/client/storage/github/lib/underscore-min.js create mode 100644 lib/client/storage/github/lib/underscore.js diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js new file mode 100644 index 00000000..95079867 --- /dev/null +++ b/lib/client/storage/_github.js @@ -0,0 +1,62 @@ +var CloudCommander; + +var CloudCommander, Github; +/* object contains terminal jqconsole */ + +(function(){ + "use strict"; + + var cloudcmd = CloudCommander, + Util = cloudcmd.Util; + + cloudcmd.Storage = {}; + + var GithubStore = {}; + + /* PRIVATE FUNCTIONS */ + + /** + * function loads github.js + */ + function load(){ + console.time('github load'); + + var lDir = './lib/client/storage/github/'; + Util.anyLoadOnLoad([ + lDir + 'github.js', + lDir + 'lib/base64.js', + lDir + 'lib/underscore.js'], + + function(){ + console.timeEnd('github load'); + Util.Images.hideLoad(); + }); + } + + function callback(pError, pDate){ + if(pError) + console.log(pError); + + if(pDate) + console.log(pDate); + } + + GithubStore.login = function(pUser, pPasswd){ + cloudcmd.Storage.Github = new Github({ + username: pUser, + password: pPasswd, + auth : 'oauth' + }); + }; + /* PUBLICK FUNCTIONS */ + + /** + * function bind keys + */ + cloudcmd.Storage.Keys = function(){ + load(); + }; + + cloudcmd.Storage.Github = GithubStore; + +})(); \ No newline at end of file diff --git a/lib/client/storage/github/LICENSE b/lib/client/storage/github/LICENSE new file mode 100644 index 00000000..6f66ed81 --- /dev/null +++ b/lib/client/storage/github/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2012 Michael Aufreiter, Development Seed +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. +- Neither the name "Development Seed" nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/lib/client/storage/github/README.md b/lib/client/storage/github/README.md new file mode 100644 index 00000000..9e832117 --- /dev/null +++ b/lib/client/storage/github/README.md @@ -0,0 +1,272 @@ +# Github.js + +Github.js provides a minimal higher-level wrapper around git's [plumbing commands](http://git-scm.com/book/en/Git-Internals-Plumbing-and-Porcelain), exposing an API for manipulating GitHub repositories on the file level. It is being developed in the context of [Prose](http://prose.io), a content editor for GitHub. + +## Usage + +Create a Github instance. + +```js +var github = new Github({ + username: "YOU_USER", + password: "YOUR_PASSWORD", + auth: "basic" +}); +``` + +Or if you prefer OAuth, it looks like this: + +```js +var github = new Github({ + token: "OAUTH_TOKEN" + auth: "oauth" +}); +``` + +## Repository API + + +```js +var repo = github.getRepo(username, reponame); +``` + +Show repository information + +```js +repo.show(function(err, repo) {}); +``` + +Get contents at a particular path. + +```js +repo.contents("path/to/dir", function(err, contents) {}); +``` + +Fork repository. This operation runs asynchronously. You may want to poll for `repo.contents` until the forked repo is ready. + +```js +repo.fork(function(err) {}); +``` + +Create Pull Request. + +```js +var pull = { + title: message, + body: "This pull request has been automatically generated by Prose.io.", + base: "gh-pages", + head: "michael" + ":" + "prose-patch", +}; +repo.createPullRequest(pull, function(err, pullRequest) {}); +``` + + +Retrieve all available branches (aka heads) of a repository. + +```js +repo.listBranches(function(err, branches) {}); +``` + +Store contents at a certain path, where files that don't yet exist are created on the fly. + +```js +repo.write('master', 'path/to/file', 'YOUR_NEW_CONTENTS', 'YOUR_COMMIT_MESSAGE', function(err) {}); +``` + +Not only can you can write files, you can of course read them. + +```js +repo.read('master', 'path/to/file', function(err, data) {}); +``` + +Move a file from A to B. + +```js +repo.move('master', 'path/to/file', 'path/to/new_file', function(err) {}); +``` + +Remove a file. + +```js +repo.remove('master', 'path/to/file', function(err) {}); +``` + +Exploring files of a repository is easy too by accessing the top level tree object. + +```js +repo.getTree('master', function(err, tree) {}); +``` + +If you want to access all blobs and trees recursively, you can add `?recursive=true`. + +```js +repo.getTree('master?recursive=true', function(err, tree) {}); +``` + +Given a filepath, retrieve the reference blob or tree sha. + +```js +repo.getSha('master', '/path/to/file', function(err, sha) {}); +``` + +For a given reference, get the corresponding commit sha. + +```js +repo.getRef('heads/master', function(err, sha) {}); +``` + +Create a new reference. + +```js +var refSpec = { + "ref": "refs/heads/my-new-branch-name", + "sha": "827efc6d56897b048c772eb4087f854f46256132" +}; +repo.createRef(refSpec, function(err) {}); +``` + +Delete a reference. + +```js +repo.deleteRef('heads/gh-pages', function(err) {}); +``` + + +## User API + + +```js +var user = github.getUser(); +``` + +List all repositories of the authenticated user. + +```js +user.repos(username, function(err, repos) {}); +``` + +List organizations the autenticated user belongs to. + +```js +user.orgs(function(err, orgs) {}); +``` + +List authenticated user's gists. + +```js +user.gists(username, function(err, gists) {}); +``` + +Show user information for a particular username. Also works for organizations. + +```js +user.show(username, function(err, user) {}); +``` + +List public repositories for a particular user. + +```js +user.userRepos(username, function(err, repos) {}); +``` + +List repositories for a particular organization. Includes private repositories if you are authorized. + +```js +user.orgRepos(orgname, function(err, repos) {}); +``` + +List all gists of a particular user. If username is ommitted gists of the current authenticated user are returned. + +```js +user.userGists(username, function(err, gists) {}); +``` + +## Gist API + +```js +var gist = github.getGist(3165654); +``` + +Read the contents of a Gist. + +```js +gist.read(function(err, gist) { + +}); +``` + +Updating the contents of a Git. Please consult the documentation on [GitHub](http://developer.github.com/v3/gists/). + +```js +var delta = { + "description": "the description for this gist", + "files": { + "file1.txt": { + "content": "updated file contents" + }, + "old_name.txt": { + "filename": "new_name.txt", + "content": "modified contents" + }, + "new_file.txt": { + "content": "a new file" + }, + "delete_this_file.txt": null + } +}; + +gist.update(delta, function(err, gist) { + +}); +``` + + +## Tests + +Github.js is automatically™ tested by the users of [Prose](http://prose.io). Because of that, we decided to save some time by not maintaining a test suite. Yes, you heard right. :) However, you can still consider it stable since it is used in production. + +##Setup + +Github.js has the following dependencies: + +- Underscore +- Base64 (for basic auth). You can leave this if you are not using basic auth. + +Include these before github.js : + +``` + ")); - else if (stream.match("DOCTYPE", true, true)) { - stream.eatWhile(/[\w\._\-]/); - return chain(doctype(1)); - } - else return null; - } - else if (stream.eat("?")) { - stream.eatWhile(/[\w\._\-]/); - state.tokenize = inBlock("meta", "?>"); - return "meta"; - } - else { - type = stream.eat("/") ? "closeTag" : "openTag"; - stream.eatSpace(); - tagName = ""; - var c; - while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c; - state.tokenize = inTag; - return "tag"; - } - } - else if (ch == "&") { - var ok; - if (stream.eat("#")) { - if (stream.eat("x")) { - ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";"); - } else { - ok = stream.eatWhile(/[\d]/) && stream.eat(";"); - } - } else { - ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";"); - } - return ok ? "atom" : "error"; - } - else { - stream.eatWhile(/[^&<]/); - return null; - } - } - - function inTag(stream, state) { - var ch = stream.next(); - if (ch == ">" || (ch == "/" && stream.eat(">"))) { - state.tokenize = inText; - type = ch == ">" ? "endTag" : "selfcloseTag"; - return "tag"; - } - else if (ch == "=") { - type = "equals"; - return null; - } - else if (/[\'\"]/.test(ch)) { - state.tokenize = inAttribute(ch); - return state.tokenize(stream, state); - } - else { - stream.eatWhile(/[^\s\u00a0=<>\"\'\/?]/); - return "word"; - } - } - - function inAttribute(quote) { - return function(stream, state) { - while (!stream.eol()) { - if (stream.next() == quote) { - state.tokenize = inTag; - break; - } - } - return "string"; - }; - } - - function inBlock(style, terminator) { - return function(stream, state) { - while (!stream.eol()) { - if (stream.match(terminator)) { - state.tokenize = inText; - break; - } - stream.next(); - } - return style; - }; - } - function doctype(depth) { - return function(stream, state) { - var ch; - while ((ch = stream.next()) != null) { - if (ch == "<") { - state.tokenize = doctype(depth + 1); - return state.tokenize(stream, state); - } else if (ch == ">") { - if (depth == 1) { - state.tokenize = inText; - break; - } else { - state.tokenize = doctype(depth - 1); - return state.tokenize(stream, state); - } - } - } - return "meta"; - }; - } - - var curState, setStyle; - function pass() { - for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]); - } - function cont() { - pass.apply(null, arguments); - return true; - } - - function pushContext(tagName, startOfLine) { - var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent); - curState.context = { - prev: curState.context, - tagName: tagName, - indent: curState.indented, - startOfLine: startOfLine, - noIndent: noIndent - }; - } - function popContext() { - if (curState.context) curState.context = curState.context.prev; - } - - function element(type) { - if (type == "openTag") { - curState.tagName = tagName; - return cont(attributes, endtag(curState.startOfLine)); - } else if (type == "closeTag") { - var err = false; - if (curState.context) { - if (curState.context.tagName != tagName) { - if (Kludges.implicitlyClosed.hasOwnProperty(curState.context.tagName.toLowerCase())) { - popContext(); - } - err = !curState.context || curState.context.tagName != tagName; - } - } else { - err = true; - } - if (err) setStyle = "error"; - return cont(endclosetag(err)); - } - return cont(); - } - function endtag(startOfLine) { - return function(type) { - if (type == "selfcloseTag" || - (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(curState.tagName.toLowerCase()))) { - maybePopContext(curState.tagName.toLowerCase()); - return cont(); - } - if (type == "endTag") { - maybePopContext(curState.tagName.toLowerCase()); - pushContext(curState.tagName, startOfLine); - return cont(); - } - return cont(); - }; - } - function endclosetag(err) { - return function(type) { - if (err) setStyle = "error"; - if (type == "endTag") { popContext(); return cont(); } - setStyle = "error"; - return cont(arguments.callee); - } - } - function maybePopContext(nextTagName) { - var parentTagName; - while (true) { - if (!curState.context) { - return; - } - parentTagName = curState.context.tagName.toLowerCase(); - if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) || - !Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) { - return; - } - popContext(); - } - } - - function attributes(type) { - if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);} - if (type == "endTag" || type == "selfcloseTag") return pass(); - setStyle = "error"; - return cont(attributes); - } - function attribute(type) { - if (type == "equals") return cont(attvalue, attributes); - if (!Kludges.allowMissing) setStyle = "error"; - return (type == "endTag" || type == "selfcloseTag") ? pass() : cont(); - } - function attvalue(type) { - if (type == "string") return cont(attvaluemaybe); - if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();} - setStyle = "error"; - return (type == "endTag" || type == "selfCloseTag") ? pass() : cont(); - } - function attvaluemaybe(type) { - if (type == "string") return cont(attvaluemaybe); - else return pass(); - } - - return { - startState: function() { - return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, context: null}; - }, - - token: function(stream, state) { - if (stream.sol()) { - state.startOfLine = true; - state.indented = stream.indentation(); - } - if (stream.eatSpace()) return null; - - setStyle = type = tagName = null; - var style = state.tokenize(stream, state); - state.type = type; - if ((style || type) && style != "comment") { - curState = state; - while (true) { - var comb = state.cc.pop() || element; - if (comb(type || style)) break; - } - } - state.startOfLine = false; - return setStyle || style; - }, - - indent: function(state, textAfter, fullLine) { - var context = state.context; - if ((state.tokenize != inTag && state.tokenize != inText) || - context && context.noIndent) - return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0; - if (alignCDATA && /")); + else return null; + } + else if (stream.match("--")) return chain(inBlock("comment", "-->")); + else if (stream.match("DOCTYPE", true, true)) { + stream.eatWhile(/[\w\._\-]/); + return chain(doctype(1)); + } + else return null; + } + else if (stream.eat("?")) { + stream.eatWhile(/[\w\._\-]/); + state.tokenize = inBlock("meta", "?>"); + return "meta"; + } + else { + type = stream.eat("/") ? "closeTag" : "openTag"; + stream.eatSpace(); + tagName = ""; + var c; + while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c; + state.tokenize = inTag; + return "tag"; + } + } + else if (ch == "&") { + var ok; + if (stream.eat("#")) { + if (stream.eat("x")) { + ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";"); + } else { + ok = stream.eatWhile(/[\d]/) && stream.eat(";"); + } + } else { + ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";"); + } + return ok ? "atom" : "error"; + } + else { + stream.eatWhile(/[^&<]/); + return null; + } + } + + function inTag(stream, state) { + var ch = stream.next(); + if (ch == ">" || (ch == "/" && stream.eat(">"))) { + state.tokenize = inText; + type = ch == ">" ? "endTag" : "selfcloseTag"; + return "tag"; + } + else if (ch == "=") { + type = "equals"; + return null; + } + else if (/[\'\"]/.test(ch)) { + state.tokenize = inAttribute(ch); + return state.tokenize(stream, state); + } + else { + stream.eatWhile(/[^\s\u00a0=<>\"\'\/?]/); + return "word"; + } + } + + function inAttribute(quote) { + return function(stream, state) { + while (!stream.eol()) { + if (stream.next() == quote) { + state.tokenize = inTag; + break; + } + } + return "string"; + }; + } + + function inBlock(style, terminator) { + return function(stream, state) { + while (!stream.eol()) { + if (stream.match(terminator)) { + state.tokenize = inText; + break; + } + stream.next(); + } + return style; + }; + } + function doctype(depth) { + return function(stream, state) { + var ch; + while ((ch = stream.next()) != null) { + if (ch == "<") { + state.tokenize = doctype(depth + 1); + return state.tokenize(stream, state); + } else if (ch == ">") { + if (depth == 1) { + state.tokenize = inText; + break; + } else { + state.tokenize = doctype(depth - 1); + return state.tokenize(stream, state); + } + } + } + return "meta"; + }; + } + + var curState, setStyle; + function pass() { + for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]); + } + function cont() { + pass.apply(null, arguments); + return true; + } + + function pushContext(tagName, startOfLine) { + var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent); + curState.context = { + prev: curState.context, + tagName: tagName, + indent: curState.indented, + startOfLine: startOfLine, + noIndent: noIndent + }; + } + function popContext() { + if (curState.context) curState.context = curState.context.prev; + } + + function element(type) { + if (type == "openTag") { + curState.tagName = tagName; + return cont(attributes, endtag(curState.startOfLine)); + } else if (type == "closeTag") { + var err = false; + if (curState.context) { + if (curState.context.tagName != tagName) { + if (Kludges.implicitlyClosed.hasOwnProperty(curState.context.tagName.toLowerCase())) { + popContext(); + } + err = !curState.context || curState.context.tagName != tagName; + } + } else { + err = true; + } + if (err) setStyle = "error"; + return cont(endclosetag(err)); + } + return cont(); + } + function endtag(startOfLine) { + return function(type) { + if (type == "selfcloseTag" || + (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(curState.tagName.toLowerCase()))) { + maybePopContext(curState.tagName.toLowerCase()); + return cont(); + } + if (type == "endTag") { + maybePopContext(curState.tagName.toLowerCase()); + pushContext(curState.tagName, startOfLine); + return cont(); + } + return cont(); + }; + } + function endclosetag(err) { + return function(type) { + if (err) setStyle = "error"; + if (type == "endTag") { popContext(); return cont(); } + setStyle = "error"; + return cont(arguments.callee); + }; + } + function maybePopContext(nextTagName) { + var parentTagName; + while (true) { + if (!curState.context) { + return; + } + parentTagName = curState.context.tagName.toLowerCase(); + if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) || + !Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) { + return; + } + popContext(); + } + } + + function attributes(type) { + if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);} + if (type == "endTag" || type == "selfcloseTag") return pass(); + setStyle = "error"; + return cont(attributes); + } + function attribute(type) { + if (type == "equals") return cont(attvalue, attributes); + if (!Kludges.allowMissing) setStyle = "error"; + return (type == "endTag" || type == "selfcloseTag") ? pass() : cont(); + } + function attvalue(type) { + if (type == "string") return cont(attvaluemaybe); + if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();} + setStyle = "error"; + return (type == "endTag" || type == "selfCloseTag") ? pass() : cont(); + } + function attvaluemaybe(type) { + if (type == "string") return cont(attvaluemaybe); + else return pass(); + } + + return { + startState: function() { + return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, context: null}; + }, + + token: function(stream, state) { + if (stream.sol()) { + state.startOfLine = true; + state.indented = stream.indentation(); + } + if (stream.eatSpace()) return null; + + setStyle = type = tagName = null; + var style = state.tokenize(stream, state); + state.type = type; + if ((style || type) && style != "comment") { + curState = state; + while (true) { + var comb = state.cc.pop() || element; + if (comb(type || style)) break; + } + } + state.startOfLine = false; + return setStyle || style; + }, + + indent: function(state, textAfter, fullLine) { + var context = state.context; + if ((state.tokenize != inTag && state.tokenize != inText) || + context && context.noIndent) + return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0; + if (alignCDATA && / - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -Please note that some subdirectories of the CodeMirror distribution -include their own LICENSE files, and are released under different -licences. diff --git a/lib/client/editor/codemirror_v2/README.md b/lib/client/editor/codemirror_v2/README.md deleted file mode 100644 index 8ed9871a..00000000 --- a/lib/client/editor/codemirror_v2/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# CodeMirror [![Build Status](https://secure.travis-ci.org/marijnh/CodeMirror.png?branch=master)](http://travis-ci.org/marijnh/CodeMirror) - -CodeMirror is a JavaScript component that provides a code editor in -the browser. When a mode is available for the language you are coding -in, it will color your code, and optionally help with indentation. - -The project page is http://codemirror.net -The manual is at http://codemirror.net/doc/manual.html diff --git a/lib/client/editor/codemirror_v2/codemirror.css b/lib/client/editor/codemirror_v2/codemirror.css deleted file mode 100644 index 41b8d09e..00000000 --- a/lib/client/editor/codemirror_v2/codemirror.css +++ /dev/null @@ -1,174 +0,0 @@ -.CodeMirror { - line-height: 1em; - font-family: monospace; - - /* Necessary so the scrollbar can be absolutely positioned within the wrapper on Lion. */ - position: relative; - /* This prevents unwanted scrollbars from showing up on the body and wrapper in IE. */ - overflow: hidden; -} - -.CodeMirror-scroll { - overflow: auto; - height: 300px; - /* This is needed to prevent an IE[67] bug where the scrolled content - is visible outside of the scrolling box. */ - position: relative; - outline: none; -} - -/* Vertical scrollbar */ -.CodeMirror-scrollbar { - position: absolute; - right: 0; top: 0; - overflow-x: hidden; - overflow-y: scroll; - z-index: 5; -} -.CodeMirror-scrollbar-inner { - /* This needs to have a nonzero width in order for the scrollbar to appear - in Firefox and IE9. */ - width: 1px; -} -.CodeMirror-scrollbar.cm-sb-overlap { - /* Ensure that the scrollbar appears in Lion, and that it overlaps the content - rather than sitting to the right of it. */ - position: absolute; - z-index: 1; - float: none; - right: 0; - min-width: 12px; -} -.CodeMirror-scrollbar.cm-sb-nonoverlap { - min-width: 12px; -} -.CodeMirror-scrollbar.cm-sb-ie7 { - min-width: 18px; -} - -.CodeMirror-gutter { - position: absolute; left: 0; top: 0; - z-index: 10; - background-color: #f7f7f7; - border-right: 1px solid #eee; - min-width: 2em; - height: 100%; -} -.CodeMirror-gutter-text { - color: #aaa; - text-align: right; - padding: .4em .2em .4em .4em; - white-space: pre !important; - cursor: default; -} -.CodeMirror-lines { - padding: .4em; - white-space: pre; - cursor: text; -} - -.CodeMirror pre { - -moz-border-radius: 0; - -webkit-border-radius: 0; - -o-border-radius: 0; - border-radius: 0; - border-width: 0; margin: 0; padding: 0; background: transparent; - font-family: inherit; - font-size: inherit; - padding: 0; margin: 0; - white-space: pre; - word-wrap: normal; - line-height: inherit; - color: inherit; - overflow: visible; -} - -.CodeMirror-wrap pre { - word-wrap: break-word; - white-space: pre-wrap; - word-break: normal; -} -.CodeMirror-wrap .CodeMirror-scroll { - overflow-x: hidden; -} - -.CodeMirror textarea { - outline: none !important; -} - -.CodeMirror pre.CodeMirror-cursor { - z-index: 10; - position: absolute; - visibility: hidden; - border-left: 1px solid black; - border-right: none; - width: 0; -} -.cm-keymap-fat-cursor pre.CodeMirror-cursor { - width: auto; - border: 0; - background: transparent; - background: rgba(0, 200, 0, .4); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#6600c800, endColorstr=#4c00c800); -} -/* Kludge to turn off filter in ie9+, which also accepts rgba */ -.cm-keymap-fat-cursor pre.CodeMirror-cursor:not(#nonsense_id) { - filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); -} -.CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {} -.CodeMirror-focused pre.CodeMirror-cursor { - visibility: visible; -} - -div.CodeMirror-selected { background: #d9d9d9; } -.CodeMirror-focused div.CodeMirror-selected { background: #d7d4f0; } - -.CodeMirror-searching { - background: #ffa; - background: rgba(255, 255, 0, .4); -} - -/* Default theme */ - -.cm-s-default span.cm-keyword {color: #708;} -.cm-s-default span.cm-atom {color: #219;} -.cm-s-default span.cm-number {color: #164;} -.cm-s-default span.cm-def {color: #00f;} -.cm-s-default span.cm-variable {color: black;} -.cm-s-default span.cm-variable-2 {color: #05a;} -.cm-s-default span.cm-variable-3 {color: #085;} -.cm-s-default span.cm-property {color: black;} -.cm-s-default span.cm-operator {color: black;} -.cm-s-default span.cm-comment {color: #a50;} -.cm-s-default span.cm-string {color: #a11;} -.cm-s-default span.cm-string-2 {color: #f50;} -.cm-s-default span.cm-meta {color: #555;} -.cm-s-default span.cm-error {color: #f00;} -.cm-s-default span.cm-qualifier {color: #555;} -.cm-s-default span.cm-builtin {color: #30a;} -.cm-s-default span.cm-bracket {color: #997;} -.cm-s-default span.cm-tag {color: #170;} -.cm-s-default span.cm-attribute {color: #00c;} -.cm-s-default span.cm-header {color: blue;} -.cm-s-default span.cm-quote {color: #090;} -.cm-s-default span.cm-hr {color: #999;} -.cm-s-default span.cm-link {color: #00c;} - -span.cm-header, span.cm-strong {font-weight: bold;} -span.cm-em {font-style: italic;} -span.cm-emstrong {font-style: italic; font-weight: bold;} -span.cm-link {text-decoration: underline;} - -span.cm-invalidchar {color: #f00;} - -div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} -div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} - -@media print { - - /* Hide the cursor when printing */ - .CodeMirror pre.CodeMirror-cursor { - visibility: hidden; - } - -} diff --git a/lib/client/editor/codemirror_v2/codemirror.js b/lib/client/editor/codemirror_v2/codemirror.js deleted file mode 100644 index e49481b5..00000000 --- a/lib/client/editor/codemirror_v2/codemirror.js +++ /dev/null @@ -1,3165 +0,0 @@ -// CodeMirror version 2.35 -// -// All functions that need access to the editor's state live inside -// the CodeMirror function. Below that, at the bottom of the file, -// some utilities are defined. - -// CodeMirror is the only global var we claim -window.CodeMirror = (function() { - "use strict"; - // This is the function that produces an editor instance. Its - // closure is used to store the editor state. - function CodeMirror(place, givenOptions) { - // Determine effective options based on given values and defaults. - var options = {}, defaults = CodeMirror.defaults; - for (var opt in defaults) - if (defaults.hasOwnProperty(opt)) - options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt]; - - var input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em"); - input.setAttribute("wrap", "off"); input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off"); - // Wraps and hides input textarea - var inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); - // The empty scrollbar content, used solely for managing the scrollbar thumb. - var scrollbarInner = elt("div", null, "CodeMirror-scrollbar-inner"); - // The vertical scrollbar. Horizontal scrolling is handled by the scroller itself. - var scrollbar = elt("div", [scrollbarInner], "CodeMirror-scrollbar"); - // DIVs containing the selection and the actual code - var lineDiv = elt("div"), selectionDiv = elt("div", null, null, "position: relative; z-index: -1"); - // Blinky cursor, and element used to ensure cursor fits at the end of a line - var cursor = elt("pre", "\u00a0", "CodeMirror-cursor"), widthForcer = elt("pre", "\u00a0", "CodeMirror-cursor", "visibility: hidden"); - // Used to measure text size - var measure = elt("div", null, null, "position: absolute; width: 100%; height: 0px; overflow: hidden; visibility: hidden;"); - var lineSpace = elt("div", [measure, cursor, widthForcer, selectionDiv, lineDiv], null, "position: relative; z-index: 0"); - var gutterText = elt("div", null, "CodeMirror-gutter-text"), gutter = elt("div", [gutterText], "CodeMirror-gutter"); - // Moved around its parent to cover visible view - var mover = elt("div", [gutter, elt("div", [lineSpace], "CodeMirror-lines")], null, "position: relative"); - // Set to the height of the text, causes scrolling - var sizer = elt("div", [mover], null, "position: relative"); - // Provides scrolling - var scroller = elt("div", [sizer], "CodeMirror-scroll"); - scroller.setAttribute("tabIndex", "-1"); - // The element in which the editor lives. - var wrapper = elt("div", [inputDiv, scrollbar, scroller], "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : "")); - if (place.appendChild) place.appendChild(wrapper); else place(wrapper); - - themeChanged(); keyMapChanged(); - // Needed to hide big blue blinking cursor on Mobile Safari - if (ios) input.style.width = "0px"; - if (!webkit) scroller.draggable = true; - lineSpace.style.outline = "none"; - if (options.tabindex != null) input.tabIndex = options.tabindex; - if (options.autofocus) focusInput(); - if (!options.gutter && !options.lineNumbers) gutter.style.display = "none"; - // Needed to handle Tab key in KHTML - if (khtml) inputDiv.style.height = "1px", inputDiv.style.position = "absolute"; - - // Check for OS X >= 10.7. This has transparent scrollbars, so the - // overlaying of one scrollbar with another won't work. This is a - // temporary hack to simply turn off the overlay scrollbar. See - // issue #727. - if (mac_geLion) { scrollbar.style.zIndex = -2; scrollbar.style.visibility = "hidden"; } - // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). - else if (ie_lt8) scrollbar.style.minWidth = "18px"; - - // Delayed object wrap timeouts, making sure only one is active. blinker holds an interval. - var poll = new Delayed(), highlight = new Delayed(), blinker; - - // mode holds a mode API object. doc is the tree of Line objects, - // frontier is the point up to which the content has been parsed, - // and history the undo history (instance of History constructor). - var mode, doc = new BranchChunk([new LeafChunk([new Line("")])]), frontier = 0, focused; - loadMode(); - // The selection. These are always maintained to point at valid - // positions. Inverted is used to remember that the user is - // selecting bottom-to-top. - var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false}; - // Selection-related flags. shiftSelecting obviously tracks - // whether the user is holding shift. - var shiftSelecting, lastClick, lastDoubleClick, lastScrollTop = 0, draggingText, - overwrite = false, suppressEdits = false, pasteIncoming = false; - // Variables used by startOperation/endOperation to track what - // happened during the operation. - var updateInput, userSelChange, changes, textChanged, selectionChanged, - gutterDirty, callbacks; - // Current visible range (may be bigger than the view window). - var displayOffset = 0, showingFrom = 0, showingTo = 0, lastSizeC = 0; - // bracketHighlighted is used to remember that a bracket has been - // marked. - var bracketHighlighted; - // Tracks the maximum line length so that the horizontal scrollbar - // can be kept static when scrolling. - var maxLine = getLine(0), updateMaxLine = false, maxLineChanged = true; - var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll - var goalColumn = null; - - // Initialize the content. - operation(function(){setValue(options.value || ""); updateInput = false;})(); - var history = new History(); - - // Register our event handlers. - connect(scroller, "mousedown", operation(onMouseDown)); - connect(scroller, "dblclick", operation(onDoubleClick)); - connect(lineSpace, "selectstart", e_preventDefault); - // Gecko browsers fire contextmenu *after* opening the menu, at - // which point we can't mess with it anymore. Context menu is - // handled in onMouseDown for Gecko. - if (!gecko) connect(scroller, "contextmenu", onContextMenu); - connect(scroller, "scroll", onScrollMain); - connect(scrollbar, "scroll", onScrollBar); - connect(scrollbar, "mousedown", function() {if (focused) setTimeout(focusInput, 0);}); - var resizeHandler = connect(window, "resize", function() { - if (wrapper.parentNode) updateDisplay(true); - else resizeHandler(); - }, true); - connect(input, "keyup", operation(onKeyUp)); - connect(input, "input", fastPoll); - connect(input, "keydown", operation(onKeyDown)); - connect(input, "keypress", operation(onKeyPress)); - connect(input, "focus", onFocus); - connect(input, "blur", onBlur); - - function drag_(e) { - if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return; - e_stop(e); - } - if (options.dragDrop) { - connect(scroller, "dragstart", onDragStart); - connect(scroller, "dragenter", drag_); - connect(scroller, "dragover", drag_); - connect(scroller, "drop", operation(onDrop)); - } - connect(scroller, "paste", function(){focusInput(); fastPoll();}); - connect(input, "paste", function(){pasteIncoming = true; fastPoll();}); - connect(input, "cut", operation(function(){ - if (!options.readOnly) replaceSelection(""); - })); - - // Needed to handle Tab key in KHTML - if (khtml) connect(sizer, "mouseup", function() { - if (document.activeElement == input) input.blur(); - focusInput(); - }); - - // IE throws unspecified error in certain cases, when - // trying to access activeElement before onload - var hasFocus; try { hasFocus = (document.activeElement == input); } catch(e) { } - if (hasFocus || options.autofocus) setTimeout(onFocus, 20); - else onBlur(); - - function isLine(l) {return l >= 0 && l < doc.size;} - // The instance object that we'll return. Mostly calls out to - // local functions in the CodeMirror function. Some do some extra - // range checking and/or clipping. operation is used to wrap the - // call so that changes it makes are tracked, and the display is - // updated afterwards. - var instance = wrapper.CodeMirror = { - getValue: getValue, - setValue: operation(setValue), - getSelection: getSelection, - replaceSelection: operation(replaceSelection), - focus: function(){window.focus(); focusInput(); onFocus(); fastPoll();}, - setOption: function(option, value) { - var oldVal = options[option]; - options[option] = value; - if (option == "mode" || option == "indentUnit") loadMode(); - else if (option == "readOnly" && value == "nocursor") {onBlur(); input.blur();} - else if (option == "readOnly" && !value) {resetInput(true);} - else if (option == "theme") themeChanged(); - else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)(); - else if (option == "tabSize") updateDisplay(true); - else if (option == "keyMap") keyMapChanged(); - else if (option == "tabindex") input.tabIndex = value; - if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" || - option == "theme" || option == "lineNumberFormatter") { - gutterChanged(); - updateDisplay(true); - } - }, - getOption: function(option) {return options[option];}, - getMode: function() {return mode;}, - undo: operation(undo), - redo: operation(redo), - indentLine: operation(function(n, dir) { - if (typeof dir != "string") { - if (dir == null) dir = options.smartIndent ? "smart" : "prev"; - else dir = dir ? "add" : "subtract"; - } - if (isLine(n)) indentLine(n, dir); - }), - indentSelection: operation(indentSelected), - historySize: function() {return {undo: history.done.length, redo: history.undone.length};}, - clearHistory: function() {history = new History();}, - setHistory: function(histData) { - history = new History(); - history.done = histData.done; - history.undone = histData.undone; - }, - getHistory: function() { - function cp(arr) { - for (var i = 0, nw = [], nwelt; i < arr.length; ++i) { - nw.push(nwelt = []); - for (var j = 0, elt = arr[i]; j < elt.length; ++j) { - var old = [], cur = elt[j]; - nwelt.push({start: cur.start, added: cur.added, old: old}); - for (var k = 0; k < cur.old.length; ++k) old.push(hlText(cur.old[k])); - } - } - return nw; - } - return {done: cp(history.done), undone: cp(history.undone)}; - }, - matchBrackets: operation(function(){matchBrackets(true);}), - getTokenAt: operation(function(pos) { - pos = clipPos(pos); - return getLine(pos.line).getTokenAt(mode, getStateBefore(pos.line), options.tabSize, pos.ch); - }), - getStateAfter: function(line) { - line = clipLine(line == null ? doc.size - 1: line); - return getStateBefore(line + 1); - }, - cursorCoords: function(start, mode) { - if (start == null) start = sel.inverted; - return this.charCoords(start ? sel.from : sel.to, mode); - }, - charCoords: function(pos, mode) { - pos = clipPos(pos); - if (mode == "local") return localCoords(pos, false); - if (mode == "div") return localCoords(pos, true); - return pageCoords(pos); - }, - coordsChar: function(coords) { - var off = eltOffset(lineSpace); - return coordsChar(coords.x - off.left, coords.y - off.top); - }, - markText: operation(markText), - setBookmark: setBookmark, - findMarksAt: findMarksAt, - setMarker: operation(addGutterMarker), - clearMarker: operation(removeGutterMarker), - setLineClass: operation(setLineClass), - hideLine: operation(function(h) {return setLineHidden(h, true);}), - showLine: operation(function(h) {return setLineHidden(h, false);}), - onDeleteLine: function(line, f) { - if (typeof line == "number") { - if (!isLine(line)) return null; - line = getLine(line); - } - (line.handlers || (line.handlers = [])).push(f); - return line; - }, - lineInfo: lineInfo, - getViewport: function() { return {from: showingFrom, to: showingTo};}, - addWidget: function(pos, node, scroll, vert, horiz) { - pos = localCoords(clipPos(pos)); - var top = pos.yBot, left = pos.x; - node.style.position = "absolute"; - sizer.appendChild(node); - if (vert == "over") top = pos.y; - else if (vert == "near") { - var vspace = Math.max(scroller.offsetHeight, doc.height * textHeight()), - hspace = Math.max(sizer.clientWidth, lineSpace.clientWidth) - paddingLeft(); - if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight) - top = pos.y - node.offsetHeight; - if (left + node.offsetWidth > hspace) - left = hspace - node.offsetWidth; - } - node.style.top = (top + paddingTop()) + "px"; - node.style.left = node.style.right = ""; - if (horiz == "right") { - left = sizer.clientWidth - node.offsetWidth; - node.style.right = "0px"; - } else { - if (horiz == "left") left = 0; - else if (horiz == "middle") left = (sizer.clientWidth - node.offsetWidth) / 2; - node.style.left = (left + paddingLeft()) + "px"; - } - if (scroll) - scrollIntoView(left, top, left + node.offsetWidth, top + node.offsetHeight); - }, - - lineCount: function() {return doc.size;}, - clipPos: clipPos, - getCursor: function(start) { - if (start == null) start = sel.inverted; - return copyPos(start ? sel.from : sel.to); - }, - somethingSelected: function() {return !posEq(sel.from, sel.to);}, - setCursor: operation(function(line, ch, user) { - if (ch == null && typeof line.line == "number") setCursor(line.line, line.ch, user); - else setCursor(line, ch, user); - }), - setSelection: operation(function(from, to, user) { - (user ? setSelectionUser : setSelection)(clipPos(from), clipPos(to || from)); - }), - getLine: function(line) {if (isLine(line)) return getLine(line).text;}, - getLineHandle: function(line) {if (isLine(line)) return getLine(line);}, - setLine: operation(function(line, text) { - if (isLine(line)) replaceRange(text, {line: line, ch: 0}, {line: line, ch: getLine(line).text.length}); - }), - removeLine: operation(function(line) { - if (isLine(line)) replaceRange("", {line: line, ch: 0}, clipPos({line: line+1, ch: 0})); - }), - replaceRange: operation(replaceRange), - getRange: function(from, to, lineSep) {return getRange(clipPos(from), clipPos(to), lineSep);}, - - triggerOnKeyDown: operation(onKeyDown), - execCommand: function(cmd) {return commands[cmd](instance);}, - // Stuff used by commands, probably not much use to outside code. - moveH: operation(moveH), - deleteH: operation(deleteH), - moveV: operation(moveV), - toggleOverwrite: function() { - if(overwrite){ - overwrite = false; - cursor.className = cursor.className.replace(" CodeMirror-overwrite", ""); - } else { - overwrite = true; - cursor.className += " CodeMirror-overwrite"; - } - }, - - posFromIndex: function(off) { - var lineNo = 0, ch; - doc.iter(0, doc.size, function(line) { - var sz = line.text.length + 1; - if (sz > off) { ch = off; return true; } - off -= sz; - ++lineNo; - }); - return clipPos({line: lineNo, ch: ch}); - }, - indexFromPos: function (coords) { - if (coords.line < 0 || coords.ch < 0) return 0; - var index = coords.ch; - doc.iter(0, coords.line, function (line) { - index += line.text.length + 1; - }); - return index; - }, - scrollTo: function(x, y) { - if (x != null) scroller.scrollLeft = x; - if (y != null) scrollbar.scrollTop = scroller.scrollTop = y; - updateDisplay([]); - }, - getScrollInfo: function() { - return {x: scroller.scrollLeft, y: scrollbar.scrollTop, - height: scrollbar.scrollHeight, width: scroller.scrollWidth}; - }, - setSize: function(width, height) { - function interpret(val) { - val = String(val); - return /^\d+$/.test(val) ? val + "px" : val; - } - if (width != null) wrapper.style.width = interpret(width); - if (height != null) scroller.style.height = interpret(height); - instance.refresh(); - }, - - operation: function(f){return operation(f)();}, - compoundChange: function(f){return compoundChange(f);}, - refresh: function(){ - updateDisplay(true, null, lastScrollTop); - if (scrollbar.scrollHeight > lastScrollTop) - scrollbar.scrollTop = lastScrollTop; - }, - getInputField: function(){return input;}, - getWrapperElement: function(){return wrapper;}, - getScrollerElement: function(){return scroller;}, - getGutterElement: function(){return gutter;} - }; - - function getLine(n) { return getLineAt(doc, n); } - function updateLineHeight(line, height) { - gutterDirty = true; - var diff = height - line.height; - for (var n = line; n; n = n.parent) n.height += diff; - } - - function lineContent(line, wrapAt) { - if (!line.styles) - line.highlight(mode, line.stateAfter = getStateBefore(lineNo(line)), options.tabSize); - return line.getContent(options.tabSize, wrapAt, options.lineWrapping); - } - - function setValue(code) { - var top = {line: 0, ch: 0}; - updateLines(top, {line: doc.size - 1, ch: getLine(doc.size-1).text.length}, - splitLines(code), top, top); - updateInput = true; - } - function getValue(lineSep) { - var text = []; - doc.iter(0, doc.size, function(line) { text.push(line.text); }); - return text.join(lineSep || "\n"); - } - - function onScrollBar(e) { - if (scrollbar.scrollTop != lastScrollTop) { - lastScrollTop = scroller.scrollTop = scrollbar.scrollTop; - updateDisplay([]); - } - } - - function onScrollMain(e) { - if (options.fixedGutter && gutter.style.left != scroller.scrollLeft + "px") - gutter.style.left = scroller.scrollLeft + "px"; - if (scroller.scrollTop != lastScrollTop) { - lastScrollTop = scroller.scrollTop; - if (scrollbar.scrollTop != lastScrollTop) - scrollbar.scrollTop = lastScrollTop; - updateDisplay([]); - } - if (options.onScroll) options.onScroll(instance); - } - - function onMouseDown(e) { - setShift(e_prop(e, "shiftKey")); - // Check whether this is a click in a widget - for (var n = e_target(e); n != wrapper; n = n.parentNode) - if (n.parentNode == sizer && n != mover) return; - - // See if this is a click in the gutter - for (var n = e_target(e); n != wrapper; n = n.parentNode) - if (n.parentNode == gutterText) { - if (options.onGutterClick) - options.onGutterClick(instance, indexOf(gutterText.childNodes, n) + showingFrom, e); - return e_preventDefault(e); - } - - var start = posFromMouse(e); - - switch (e_button(e)) { - case 3: - if (gecko) onContextMenu(e); - return; - case 2: - if (start) setCursor(start.line, start.ch, true); - setTimeout(focusInput, 20); - e_preventDefault(e); - return; - } - // For button 1, if it was clicked inside the editor - // (posFromMouse returning non-null), we have to adjust the - // selection. - if (!start) {if (e_target(e) == scroller) e_preventDefault(e); return;} - - if (!focused) onFocus(); - - var now = +new Date, type = "single"; - if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) { - type = "triple"; - e_preventDefault(e); - setTimeout(focusInput, 20); - selectLine(start.line); - } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) { - type = "double"; - lastDoubleClick = {time: now, pos: start}; - e_preventDefault(e); - var word = findWordAt(start); - setSelectionUser(word.from, word.to); - } else { lastClick = {time: now, pos: start}; } - - function dragEnd(e2) { - if (webkit) scroller.draggable = false; - draggingText = false; - up(); drop(); - if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) { - e_preventDefault(e2); - setCursor(start.line, start.ch, true); - focusInput(); - } - } - var last = start, going; - if (options.dragDrop && dragAndDrop && !options.readOnly && !posEq(sel.from, sel.to) && - !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") { - // Let the drag handler handle this. - if (webkit) scroller.draggable = true; - var up = connect(document, "mouseup", operation(dragEnd), true); - var drop = connect(scroller, "drop", operation(dragEnd), true); - draggingText = true; - // IE's approach to draggable - if (scroller.dragDrop) scroller.dragDrop(); - return; - } - e_preventDefault(e); - if (type == "single") setCursor(start.line, start.ch, true); - - var startstart = sel.from, startend = sel.to; - - function doSelect(cur) { - if (type == "single") { - setSelectionUser(start, cur); - } else if (type == "double") { - var word = findWordAt(cur); - if (posLess(cur, startstart)) setSelectionUser(word.from, startend); - else setSelectionUser(startstart, word.to); - } else if (type == "triple") { - if (posLess(cur, startstart)) setSelectionUser(startend, clipPos({line: cur.line, ch: 0})); - else setSelectionUser(startstart, clipPos({line: cur.line + 1, ch: 0})); - } - } - - function extend(e) { - var cur = posFromMouse(e, true); - if (cur && !posEq(cur, last)) { - if (!focused) onFocus(); - last = cur; - doSelect(cur); - updateInput = false; - var visible = visibleLines(); - if (cur.line >= visible.to || cur.line < visible.from) - going = setTimeout(operation(function(){extend(e);}), 150); - } - } - - function done(e) { - clearTimeout(going); - var cur = posFromMouse(e); - if (cur) doSelect(cur); - e_preventDefault(e); - focusInput(); - updateInput = true; - move(); up(); - } - var move = connect(document, "mousemove", operation(function(e) { - clearTimeout(going); - e_preventDefault(e); - if (!ie && !e_button(e)) done(e); - else extend(e); - }), true); - var up = connect(document, "mouseup", operation(done), true); - } - function onDoubleClick(e) { - for (var n = e_target(e); n != wrapper; n = n.parentNode) - if (n.parentNode == gutterText) return e_preventDefault(e); - e_preventDefault(e); - } - function onDrop(e) { - if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return; - e_preventDefault(e); - var pos = posFromMouse(e, true), files = e.dataTransfer.files; - if (!pos || options.readOnly) return; - if (files && files.length && window.FileReader && window.File) { - var n = files.length, text = Array(n), read = 0; - var loadFile = function(file, i) { - var reader = new FileReader; - reader.onload = function() { - text[i] = reader.result; - if (++read == n) { - pos = clipPos(pos); - operation(function() { - var end = replaceRange(text.join(""), pos, pos); - setSelectionUser(pos, end); - })(); - } - }; - reader.readAsText(file); - }; - for (var i = 0; i < n; ++i) loadFile(files[i], i); - } else { - // Don't do a replace if the drop happened inside of the selected text. - if (draggingText && !(posLess(pos, sel.from) || posLess(sel.to, pos))) return; - try { - var text = e.dataTransfer.getData("Text"); - if (text) { - compoundChange(function() { - var curFrom = sel.from, curTo = sel.to; - setSelectionUser(pos, pos); - if (draggingText) replaceRange("", curFrom, curTo); - replaceSelection(text); - focusInput(); - }); - } - } - catch(e){} - } - } - function onDragStart(e) { - var txt = getSelection(); - e.dataTransfer.setData("Text", txt); - - // Use dummy image instead of default browsers image. - if (e.dataTransfer.setDragImage) - e.dataTransfer.setDragImage(elt('img'), 0, 0); - } - - function doHandleBinding(bound, dropShift) { - if (typeof bound == "string") { - bound = commands[bound]; - if (!bound) return false; - } - var prevShift = shiftSelecting; - try { - if (options.readOnly) suppressEdits = true; - if (dropShift) shiftSelecting = null; - bound(instance); - } catch(e) { - if (e != Pass) throw e; - return false; - } finally { - shiftSelecting = prevShift; - suppressEdits = false; - } - return true; - } - var maybeTransition; - function handleKeyBinding(e) { - // Handle auto keymap transitions - var startMap = getKeyMap(options.keyMap), next = startMap.auto; - clearTimeout(maybeTransition); - if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() { - if (getKeyMap(options.keyMap) == startMap) { - options.keyMap = (next.call ? next.call(null, instance) : next); - } - }, 50); - - var name = keyNames[e_prop(e, "keyCode")], handled = false; - var flipCtrlCmd = opera && mac; - if (name == null || e.altGraphKey) return false; - if (e_prop(e, "altKey")) name = "Alt-" + name; - if (e_prop(e, flipCtrlCmd ? "metaKey" : "ctrlKey")) name = "Ctrl-" + name; - if (e_prop(e, flipCtrlCmd ? "ctrlKey" : "metaKey")) name = "Cmd-" + name; - - var stopped = false; - function stop() { stopped = true; } - - if (e_prop(e, "shiftKey")) { - handled = lookupKey("Shift-" + name, options.extraKeys, options.keyMap, - function(b) {return doHandleBinding(b, true);}, stop) - || lookupKey(name, options.extraKeys, options.keyMap, function(b) { - if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(b); - }, stop); - } else { - handled = lookupKey(name, options.extraKeys, options.keyMap, doHandleBinding, stop); - } - if (stopped) handled = false; - if (handled) { - e_preventDefault(e); - restartBlink(); - if (ie) { e.oldKeyCode = e.keyCode; e.keyCode = 0; } - } - return handled; - } - function handleCharBinding(e, ch) { - var handled = lookupKey("'" + ch + "'", options.extraKeys, - options.keyMap, function(b) { return doHandleBinding(b, true); }); - if (handled) { - e_preventDefault(e); - restartBlink(); - } - return handled; - } - - var lastStoppedKey = null; - function onKeyDown(e) { - if (!focused) onFocus(); - if (ie && e.keyCode == 27) { e.returnValue = false; } - if (pollingFast) { if (readInput()) pollingFast = false; } - if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return; - var code = e_prop(e, "keyCode"); - // IE does strange things with escape. - setShift(code == 16 || e_prop(e, "shiftKey")); - // First give onKeyEvent option a chance to handle this. - var handled = handleKeyBinding(e); - if (opera) { - lastStoppedKey = handled ? code : null; - // Opera has no cut event... we try to at least catch the key combo - if (!handled && code == 88 && e_prop(e, mac ? "metaKey" : "ctrlKey")) - replaceSelection(""); - } - } - function onKeyPress(e) { - if (pollingFast) readInput(); - if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return; - var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode"); - if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;} - if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(e)) return; - var ch = String.fromCharCode(charCode == null ? keyCode : charCode); - if (options.electricChars && mode.electricChars && options.smartIndent && !options.readOnly) { - if (mode.electricChars.indexOf(ch) > -1) - setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 75); - } - if (handleCharBinding(e, ch)) return; - fastPoll(); - } - function onKeyUp(e) { - if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return; - if (e_prop(e, "keyCode") == 16) shiftSelecting = null; - } - - function onFocus() { - if (options.readOnly == "nocursor") return; - if (!focused) { - if (options.onFocus) options.onFocus(instance); - focused = true; - if (scroller.className.search(/\bCodeMirror-focused\b/) == -1) - scroller.className += " CodeMirror-focused"; - } - slowPoll(); - restartBlink(); - } - function onBlur() { - if (focused) { - if (options.onBlur) options.onBlur(instance); - focused = false; - if (bracketHighlighted) - operation(function(){ - if (bracketHighlighted) { bracketHighlighted(); bracketHighlighted = null; } - })(); - scroller.className = scroller.className.replace(" CodeMirror-focused", ""); - } - clearInterval(blinker); - setTimeout(function() {if (!focused) shiftSelecting = null;}, 150); - } - - // Replace the range from from to to by the strings in newText. - // Afterwards, set the selection to selFrom, selTo. - function updateLines(from, to, newText, selFrom, selTo) { - if (suppressEdits) return; - var old = []; - doc.iter(from.line, to.line + 1, function(line) { - old.push(newHL(line.text, line.markedSpans)); - }); - if (history) { - history.addChange(from.line, newText.length, old); - while (history.done.length > options.undoDepth) history.done.shift(); - } - var lines = updateMarkedSpans(hlSpans(old[0]), hlSpans(lst(old)), from.ch, to.ch, newText); - updateLinesNoUndo(from, to, lines, selFrom, selTo); - } - function unredoHelper(from, to) { - if (!from.length) return; - var set = from.pop(), out = []; - for (var i = set.length - 1; i >= 0; i -= 1) { - var change = set[i]; - var replaced = [], end = change.start + change.added; - doc.iter(change.start, end, function(line) { replaced.push(newHL(line.text, line.markedSpans)); }); - out.push({start: change.start, added: change.old.length, old: replaced}); - var pos = {line: change.start + change.old.length - 1, - ch: editEnd(hlText(lst(replaced)), hlText(lst(change.old)))}; - updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length}, - change.old, pos, pos); - } - updateInput = true; - to.push(out); - } - function undo() {unredoHelper(history.done, history.undone);} - function redo() {unredoHelper(history.undone, history.done);} - - function updateLinesNoUndo(from, to, lines, selFrom, selTo) { - if (suppressEdits) return; - var recomputeMaxLength = false, maxLineLength = maxLine.text.length; - if (!options.lineWrapping) - doc.iter(from.line, to.line + 1, function(line) { - if (!line.hidden && line.text.length == maxLineLength) {recomputeMaxLength = true; return true;} - }); - if (from.line != to.line || lines.length > 1) gutterDirty = true; - - var nlines = to.line - from.line, firstLine = getLine(from.line), lastLine = getLine(to.line); - var lastHL = lst(lines); - - // First adjust the line structure - if (from.ch == 0 && to.ch == 0 && hlText(lastHL) == "") { - // This is a whole-line replace. Treated specially to make - // sure line objects move the way they are supposed to. - var added = [], prevLine = null; - for (var i = 0, e = lines.length - 1; i < e; ++i) - added.push(new Line(hlText(lines[i]), hlSpans(lines[i]))); - lastLine.update(lastLine.text, hlSpans(lastHL)); - if (nlines) doc.remove(from.line, nlines, callbacks); - if (added.length) doc.insert(from.line, added); - } else if (firstLine == lastLine) { - if (lines.length == 1) { - firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]) + firstLine.text.slice(to.ch), hlSpans(lines[0])); - } else { - for (var added = [], i = 1, e = lines.length - 1; i < e; ++i) - added.push(new Line(hlText(lines[i]), hlSpans(lines[i]))); - added.push(new Line(hlText(lastHL) + firstLine.text.slice(to.ch), hlSpans(lastHL))); - firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0])); - doc.insert(from.line + 1, added); - } - } else if (lines.length == 1) { - firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]) + lastLine.text.slice(to.ch), hlSpans(lines[0])); - doc.remove(from.line + 1, nlines, callbacks); - } else { - var added = []; - firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0])); - lastLine.update(hlText(lastHL) + lastLine.text.slice(to.ch), hlSpans(lastHL)); - for (var i = 1, e = lines.length - 1; i < e; ++i) - added.push(new Line(hlText(lines[i]), hlSpans(lines[i]))); - if (nlines > 1) doc.remove(from.line + 1, nlines - 1, callbacks); - doc.insert(from.line + 1, added); - } - if (options.lineWrapping) { - var perLine = Math.max(5, scroller.clientWidth / charWidth() - 3); - doc.iter(from.line, from.line + lines.length, function(line) { - if (line.hidden) return; - var guess = Math.ceil(line.text.length / perLine) || 1; - if (guess != line.height) updateLineHeight(line, guess); - }); - } else { - doc.iter(from.line, from.line + lines.length, function(line) { - var l = line.text; - if (!line.hidden && l.length > maxLineLength) { - maxLine = line; maxLineLength = l.length; maxLineChanged = true; - recomputeMaxLength = false; - } - }); - if (recomputeMaxLength) updateMaxLine = true; - } - - // Adjust frontier, schedule worker - frontier = Math.min(frontier, from.line); - startWorker(400); - - var lendiff = lines.length - nlines - 1; - // Remember that these lines changed, for updating the display - changes.push({from: from.line, to: to.line + 1, diff: lendiff}); - if (options.onChange) { - // Normalize lines to contain only strings, since that's what - // the change event handler expects - for (var i = 0; i < lines.length; ++i) - if (typeof lines[i] != "string") lines[i] = lines[i].text; - var changeObj = {from: from, to: to, text: lines}; - if (textChanged) { - for (var cur = textChanged; cur.next; cur = cur.next) {} - cur.next = changeObj; - } else textChanged = changeObj; - } - - // Update the selection - function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;} - setSelection(clipPos(selFrom), clipPos(selTo), - updateLine(sel.from.line), updateLine(sel.to.line)); - } - - function needsScrollbar() { - var realHeight = doc.height * textHeight() + 2 * paddingTop(); - return realHeight * .99 > scroller.offsetHeight ? realHeight : false; - } - - function updateVerticalScroll(scrollTop) { - var scrollHeight = needsScrollbar(); - scrollbar.style.display = scrollHeight ? "block" : "none"; - if (scrollHeight) { - scrollbarInner.style.height = sizer.style.minHeight = scrollHeight + "px"; - scrollbar.style.height = scroller.clientHeight + "px"; - if (scrollTop != null) { - scrollbar.scrollTop = scroller.scrollTop = scrollTop; - // 'Nudge' the scrollbar to work around a Webkit bug where, - // in some situations, we'd end up with a scrollbar that - // reported its scrollTop (and looked) as expected, but - // *behaved* as if it was still in a previous state (i.e. - // couldn't scroll up, even though it appeared to be at the - // bottom). - if (webkit) setTimeout(function() { - if (scrollbar.scrollTop != scrollTop) return; - scrollbar.scrollTop = scrollTop + (scrollTop ? -1 : 1); - scrollbar.scrollTop = scrollTop; - }, 0); - } - } else { - sizer.style.minHeight = ""; - } - // Position the mover div to align with the current virtual scroll position - mover.style.top = displayOffset * textHeight() + "px"; - } - - function computeMaxLength() { - maxLine = getLine(0); maxLineChanged = true; - var maxLineLength = maxLine.text.length; - doc.iter(1, doc.size, function(line) { - var l = line.text; - if (!line.hidden && l.length > maxLineLength) { - maxLineLength = l.length; maxLine = line; - } - }); - updateMaxLine = false; - } - - function replaceRange(code, from, to) { - from = clipPos(from); - if (!to) to = from; else to = clipPos(to); - code = splitLines(code); - function adjustPos(pos) { - if (posLess(pos, from)) return pos; - if (!posLess(to, pos)) return end; - var line = pos.line + code.length - (to.line - from.line) - 1; - var ch = pos.ch; - if (pos.line == to.line) - ch += lst(code).length - (to.ch - (to.line == from.line ? from.ch : 0)); - return {line: line, ch: ch}; - } - var end; - replaceRange1(code, from, to, function(end1) { - end = end1; - return {from: adjustPos(sel.from), to: adjustPos(sel.to)}; - }); - return end; - } - function replaceSelection(code, collapse) { - replaceRange1(splitLines(code), sel.from, sel.to, function(end) { - if (collapse == "end") return {from: end, to: end}; - else if (collapse == "start") return {from: sel.from, to: sel.from}; - else return {from: sel.from, to: end}; - }); - } - function replaceRange1(code, from, to, computeSel) { - var endch = code.length == 1 ? code[0].length + from.ch : lst(code).length; - var newSel = computeSel({line: from.line + code.length - 1, ch: endch}); - updateLines(from, to, code, newSel.from, newSel.to); - } - - function getRange(from, to, lineSep) { - var l1 = from.line, l2 = to.line; - if (l1 == l2) return getLine(l1).text.slice(from.ch, to.ch); - var code = [getLine(l1).text.slice(from.ch)]; - doc.iter(l1 + 1, l2, function(line) { code.push(line.text); }); - code.push(getLine(l2).text.slice(0, to.ch)); - return code.join(lineSep || "\n"); - } - function getSelection(lineSep) { - return getRange(sel.from, sel.to, lineSep); - } - - function slowPoll() { - if (pollingFast) return; - poll.set(options.pollInterval, function() { - readInput(); - if (focused) slowPoll(); - }); - } - function fastPoll() { - var missed = false; - pollingFast = true; - function p() { - var changed = readInput(); - if (!changed && !missed) {missed = true; poll.set(60, p);} - else {pollingFast = false; slowPoll();} - } - poll.set(20, p); - } - - // Previnput is a hack to work with IME. If we reset the textarea - // on every change, that breaks IME. So we look for changes - // compared to the previous content instead. (Modern browsers have - // events that indicate IME taking place, but these are not widely - // supported or compatible enough yet to rely on.) - var prevInput = ""; - function readInput() { - if (!focused || hasSelection(input) || options.readOnly) return false; - var text = input.value; - if (text == prevInput) return false; - if (!nestedOperation) startOperation(); - shiftSelecting = null; - var same = 0, l = Math.min(prevInput.length, text.length); - while (same < l && prevInput[same] == text[same]) ++same; - if (same < prevInput.length) - sel.from = {line: sel.from.line, ch: sel.from.ch - (prevInput.length - same)}; - else if (overwrite && posEq(sel.from, sel.to) && !pasteIncoming) - sel.to = {line: sel.to.line, ch: Math.min(getLine(sel.to.line).text.length, sel.to.ch + (text.length - same))}; - replaceSelection(text.slice(same), "end"); - if (text.length > 1000) { input.value = prevInput = ""; } - else prevInput = text; - if (!nestedOperation) endOperation(); - pasteIncoming = false; - return true; - } - function resetInput(user) { - if (!posEq(sel.from, sel.to)) { - prevInput = ""; - input.value = getSelection(); - if (focused) selectInput(input); - } else if (user) prevInput = input.value = ""; - } - - function focusInput() { - if (options.readOnly != "nocursor") input.focus(); - } - - function scrollCursorIntoView() { - var coords = calculateCursorCoords(); - scrollIntoView(coords.x, coords.y, coords.x, coords.yBot); - if (!focused) return; - var box = sizer.getBoundingClientRect(), doScroll = null; - if (coords.y + box.top < 0) doScroll = true; - else if (coords.y + box.top + textHeight() > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false; - if (doScroll != null) { - var hidden = cursor.style.display == "none"; - if (hidden) { - cursor.style.display = ""; - cursor.style.left = coords.x + "px"; - cursor.style.top = (coords.y - displayOffset) + "px"; - } - cursor.scrollIntoView(doScroll); - if (hidden) cursor.style.display = "none"; - } - } - function calculateCursorCoords() { - var cursor = localCoords(sel.inverted ? sel.from : sel.to); - var x = options.lineWrapping ? Math.min(cursor.x, lineSpace.offsetWidth) : cursor.x; - return {x: x, y: cursor.y, yBot: cursor.yBot}; - } - function scrollIntoView(x1, y1, x2, y2) { - var scrollPos = calculateScrollPos(x1, y1, x2, y2); - if (scrollPos.scrollLeft != null) {scroller.scrollLeft = scrollPos.scrollLeft;} - if (scrollPos.scrollTop != null) {scrollbar.scrollTop = scroller.scrollTop = scrollPos.scrollTop;} - } - function calculateScrollPos(x1, y1, x2, y2) { - var pl = paddingLeft(), pt = paddingTop(); - y1 += pt; y2 += pt; x1 += pl; x2 += pl; - var screen = scroller.clientHeight, screentop = scrollbar.scrollTop, result = {}; - var docBottom = needsScrollbar() || Infinity; - var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10; - if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1); - else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen; - - var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft; - var gutterw = options.fixedGutter ? gutter.clientWidth : 0; - var atLeft = x1 < gutterw + pl + 10; - if (x1 < screenleft + gutterw || atLeft) { - if (atLeft) x1 = 0; - result.scrollLeft = Math.max(0, x1 - 10 - gutterw); - } else if (x2 > screenw + screenleft - 3) { - result.scrollLeft = x2 + 10 - screenw; - } - return result; - } - - function visibleLines(scrollTop) { - var lh = textHeight(), top = (scrollTop != null ? scrollTop : scrollbar.scrollTop) - paddingTop(); - var fromHeight = Math.max(0, Math.floor(top / lh)); - var toHeight = Math.ceil((top + scroller.clientHeight) / lh); - return {from: lineAtHeight(doc, fromHeight), - to: lineAtHeight(doc, toHeight)}; - } - // Uses a set of changes plus the current scroll position to - // determine which DOM updates have to be made, and makes the - // updates. - function updateDisplay(changes, suppressCallback, scrollTop) { - if (!scroller.clientWidth) { - showingFrom = showingTo = displayOffset = 0; - return; - } - // Compute the new visible window - // If scrollTop is specified, use that to determine which lines - // to render instead of the current scrollbar position. - var visible = visibleLines(scrollTop); - // Bail out if the visible area is already rendered and nothing changed. - if (changes !== true && changes.length == 0 && visible.from > showingFrom && visible.to < showingTo) { - updateVerticalScroll(scrollTop); - return; - } - var from = Math.max(visible.from - 100, 0), to = Math.min(doc.size, visible.to + 100); - if (showingFrom < from && from - showingFrom < 20) from = showingFrom; - if (showingTo > to && showingTo - to < 20) to = Math.min(doc.size, showingTo); - - // Create a range of theoretically intact lines, and punch holes - // in that using the change info. - var intact = changes === true ? [] : - computeIntact([{from: showingFrom, to: showingTo, domStart: 0}], changes); - // Clip off the parts that won't be visible - var intactLines = 0; - for (var i = 0; i < intact.length; ++i) { - var range = intact[i]; - if (range.from < from) {range.domStart += (from - range.from); range.from = from;} - if (range.to > to) range.to = to; - if (range.from >= range.to) intact.splice(i--, 1); - else intactLines += range.to - range.from; - } - if (intactLines == to - from && from == showingFrom && to == showingTo) { - updateVerticalScroll(scrollTop); - return; - } - intact.sort(function(a, b) {return a.domStart - b.domStart;}); - - var th = textHeight(), gutterDisplay = gutter.style.display; - lineDiv.style.display = "none"; - patchDisplay(from, to, intact); - lineDiv.style.display = gutter.style.display = ""; - - var different = from != showingFrom || to != showingTo || lastSizeC != scroller.clientHeight + th; - // This is just a bogus formula that detects when the editor is - // resized or the font size changes. - if (different) lastSizeC = scroller.clientHeight + th; - if (from != showingFrom || to != showingTo && options.onViewportChange) - setTimeout(function(){ - if (options.onViewportChange) options.onViewportChange(instance, from, to); - }); - showingFrom = from; showingTo = to; - displayOffset = heightAtLine(doc, from); - startWorker(100); - - // Since this is all rather error prone, it is honoured with the - // only assertion in the whole file. - if (lineDiv.childNodes.length != showingTo - showingFrom) - throw new Error("BAD PATCH! " + JSON.stringify(intact) + " size=" + (showingTo - showingFrom) + - " nodes=" + lineDiv.childNodes.length); - - function checkHeights() { - var curNode = lineDiv.firstChild, heightChanged = false; - doc.iter(showingFrom, showingTo, function(line) { - // Work around bizarro IE7 bug where, sometimes, our curNode - // is magically replaced with a new node in the DOM, leaving - // us with a reference to an orphan (nextSibling-less) node. - if (!curNode) return; - if (!line.hidden) { - var height = Math.round(curNode.offsetHeight / th) || 1; - if (line.height != height) { - updateLineHeight(line, height); - gutterDirty = heightChanged = true; - } - } - curNode = curNode.nextSibling; - }); - return heightChanged; - } - - if (options.lineWrapping) checkHeights(); - - gutter.style.display = gutterDisplay; - if (different || gutterDirty) { - // If the gutter grew in size, re-check heights. If those changed, re-draw gutter. - updateGutter() && options.lineWrapping && checkHeights() && updateGutter(); - } - updateVerticalScroll(scrollTop); - updateSelection(); - if (!suppressCallback && options.onUpdate) options.onUpdate(instance); - return true; - } - - function computeIntact(intact, changes) { - for (var i = 0, l = changes.length || 0; i < l; ++i) { - var change = changes[i], intact2 = [], diff = change.diff || 0; - for (var j = 0, l2 = intact.length; j < l2; ++j) { - var range = intact[j]; - if (change.to <= range.from && change.diff) - intact2.push({from: range.from + diff, to: range.to + diff, - domStart: range.domStart}); - else if (change.to <= range.from || change.from >= range.to) - intact2.push(range); - else { - if (change.from > range.from) - intact2.push({from: range.from, to: change.from, domStart: range.domStart}); - if (change.to < range.to) - intact2.push({from: change.to + diff, to: range.to + diff, - domStart: range.domStart + (change.to - range.from)}); - } - } - intact = intact2; - } - return intact; - } - - function patchDisplay(from, to, intact) { - function killNode(node) { - var tmp = node.nextSibling; - node.parentNode.removeChild(node); - return tmp; - } - // The first pass removes the DOM nodes that aren't intact. - if (!intact.length) removeChildren(lineDiv); - else { - var domPos = 0, curNode = lineDiv.firstChild, n; - for (var i = 0; i < intact.length; ++i) { - var cur = intact[i]; - while (cur.domStart > domPos) {curNode = killNode(curNode); domPos++;} - for (var j = 0, e = cur.to - cur.from; j < e; ++j) {curNode = curNode.nextSibling; domPos++;} - } - while (curNode) curNode = killNode(curNode); - } - // This pass fills in the lines that actually changed. - var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from; - doc.iter(from, to, function(line) { - if (nextIntact && nextIntact.to == j) nextIntact = intact.shift(); - if (!nextIntact || nextIntact.from > j) { - if (line.hidden) var lineElement = elt("pre"); - else { - var lineElement = lineContent(line); - if (line.className) lineElement.className = line.className; - // Kludge to make sure the styled element lies behind the selection (by z-index) - if (line.bgClassName) { - var pre = elt("pre", "\u00a0", line.bgClassName, "position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: -2"); - lineElement = elt("div", [pre, lineElement], null, "position: relative"); - } - } - lineDiv.insertBefore(lineElement, curNode); - } else { - curNode = curNode.nextSibling; - } - ++j; - }); - } - - function updateGutter() { - if (!options.gutter && !options.lineNumbers) return; - var hText = mover.offsetHeight, hEditor = scroller.clientHeight; - gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px"; - var fragment = document.createDocumentFragment(), i = showingFrom, normalNode; - doc.iter(showingFrom, Math.max(showingTo, showingFrom + 1), function(line) { - if (line.hidden) { - fragment.appendChild(elt("pre")); - } else { - var marker = line.gutterMarker; - var text = options.lineNumbers ? options.lineNumberFormatter(i + options.firstLineNumber) : null; - if (marker && marker.text) - text = marker.text.replace("%N%", text != null ? text : ""); - else if (text == null) - text = "\u00a0"; - var markerElement = fragment.appendChild(elt("pre", null, marker && marker.style)); - markerElement.innerHTML = text; - for (var j = 1; j < line.height; ++j) { - markerElement.appendChild(elt("br")); - markerElement.appendChild(document.createTextNode("\u00a0")); - } - if (!marker) normalNode = i; - } - ++i; - }); - gutter.style.display = "none"; - removeChildrenAndAdd(gutterText, fragment); - // Make sure scrolling doesn't cause number gutter size to pop - if (normalNode != null && options.lineNumbers) { - var node = gutterText.childNodes[normalNode - showingFrom]; - var minwidth = String(doc.size).length, val = eltText(node.firstChild), pad = ""; - while (val.length + pad.length < minwidth) pad += "\u00a0"; - if (pad) node.insertBefore(document.createTextNode(pad), node.firstChild); - } - gutter.style.display = ""; - var resized = Math.abs((parseInt(lineSpace.style.marginLeft) || 0) - gutter.offsetWidth) > 2; - lineSpace.style.marginLeft = gutter.offsetWidth + "px"; - gutterDirty = false; - return resized; - } - function updateSelection() { - var collapsed = posEq(sel.from, sel.to); - var fromPos = localCoords(sel.from, true); - var toPos = collapsed ? fromPos : localCoords(sel.to, true); - var headPos = sel.inverted ? fromPos : toPos, th = textHeight(); - var wrapOff = eltOffset(wrapper), lineOff = eltOffset(lineDiv); - inputDiv.style.top = Math.max(0, Math.min(scroller.offsetHeight, headPos.y + lineOff.top - wrapOff.top)) + "px"; - inputDiv.style.left = Math.max(0, Math.min(scroller.offsetWidth, headPos.x + lineOff.left - wrapOff.left)) + "px"; - if (collapsed) { - cursor.style.top = headPos.y + "px"; - cursor.style.left = (options.lineWrapping ? Math.min(headPos.x, lineSpace.offsetWidth) : headPos.x) + "px"; - cursor.style.display = ""; - selectionDiv.style.display = "none"; - } else { - var sameLine = fromPos.y == toPos.y, fragment = document.createDocumentFragment(); - var clientWidth = lineSpace.clientWidth || lineSpace.offsetWidth; - var clientHeight = lineSpace.clientHeight || lineSpace.offsetHeight; - var add = function(left, top, right, height) { - var rstyle = quirksMode ? "width: " + (!right ? clientWidth : clientWidth - right - left) + "px" - : "right: " + right + "px"; - fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left + - "px; top: " + top + "px; " + rstyle + "; height: " + height + "px")); - }; - if (sel.from.ch && fromPos.y >= 0) { - var right = sameLine ? clientWidth - toPos.x : 0; - add(fromPos.x, fromPos.y, right, th); - } - var middleStart = Math.max(0, fromPos.y + (sel.from.ch ? th : 0)); - var middleHeight = Math.min(toPos.y, clientHeight) - middleStart; - if (middleHeight > 0.2 * th) - add(0, middleStart, 0, middleHeight); - if ((!sameLine || !sel.from.ch) && toPos.y < clientHeight - .5 * th) - add(0, toPos.y, clientWidth - toPos.x, th); - removeChildrenAndAdd(selectionDiv, fragment); - cursor.style.display = "none"; - selectionDiv.style.display = ""; - } - } - - function setShift(val) { - if (val) shiftSelecting = shiftSelecting || (sel.inverted ? sel.to : sel.from); - else shiftSelecting = null; - } - function setSelectionUser(from, to) { - var sh = shiftSelecting && clipPos(shiftSelecting); - if (sh) { - if (posLess(sh, from)) from = sh; - else if (posLess(to, sh)) to = sh; - } - setSelection(from, to); - userSelChange = true; - } - // Update the selection. Last two args are only used by - // updateLines, since they have to be expressed in the line - // numbers before the update. - function setSelection(from, to, oldFrom, oldTo) { - goalColumn = null; - if (oldFrom == null) {oldFrom = sel.from.line; oldTo = sel.to.line;} - if (posEq(sel.from, from) && posEq(sel.to, to)) return; - if (posLess(to, from)) {var tmp = to; to = from; from = tmp;} - - // Skip over hidden lines. - if (from.line != oldFrom) { - var from1 = skipHidden(from, oldFrom, sel.from.ch); - // If there is no non-hidden line left, force visibility on current line - if (!from1) setLineHidden(from.line, false); - else from = from1; - } - if (to.line != oldTo) to = skipHidden(to, oldTo, sel.to.ch); - - if (posEq(from, to)) sel.inverted = false; - else if (posEq(from, sel.to)) sel.inverted = false; - else if (posEq(to, sel.from)) sel.inverted = true; - - if (options.autoClearEmptyLines && posEq(sel.from, sel.to)) { - var head = sel.inverted ? from : to; - if (head.line != sel.from.line && sel.from.line < doc.size) { - var oldLine = getLine(sel.from.line); - if (/^\s+$/.test(oldLine.text)) - setTimeout(operation(function() { - if (oldLine.parent && /^\s+$/.test(oldLine.text)) { - var no = lineNo(oldLine); - replaceRange("", {line: no, ch: 0}, {line: no, ch: oldLine.text.length}); - } - }, 10)); - } - } - - sel.from = from; sel.to = to; - selectionChanged = true; - } - function skipHidden(pos, oldLine, oldCh) { - function getNonHidden(dir) { - var lNo = pos.line + dir, end = dir == 1 ? doc.size : -1; - while (lNo != end) { - var line = getLine(lNo); - if (!line.hidden) { - var ch = pos.ch; - if (toEnd || ch > oldCh || ch > line.text.length) ch = line.text.length; - return {line: lNo, ch: ch}; - } - lNo += dir; - } - } - var line = getLine(pos.line); - var toEnd = pos.ch == line.text.length && pos.ch != oldCh; - if (!line.hidden) return pos; - if (pos.line >= oldLine) return getNonHidden(1) || getNonHidden(-1); - else return getNonHidden(-1) || getNonHidden(1); - } - function setCursor(line, ch, user) { - var pos = clipPos({line: line, ch: ch || 0}); - (user ? setSelectionUser : setSelection)(pos, pos); - } - - function clipLine(n) {return Math.max(0, Math.min(n, doc.size-1));} - function clipPos(pos) { - if (pos.line < 0) return {line: 0, ch: 0}; - if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc.size-1).text.length}; - var ch = pos.ch, linelen = getLine(pos.line).text.length; - if (ch == null || ch > linelen) return {line: pos.line, ch: linelen}; - else if (ch < 0) return {line: pos.line, ch: 0}; - else return pos; - } - - function findPosH(dir, unit) { - var end = sel.inverted ? sel.from : sel.to, line = end.line, ch = end.ch; - var lineObj = getLine(line); - function findNextLine() { - for (var l = line + dir, e = dir < 0 ? -1 : doc.size; l != e; l += dir) { - var lo = getLine(l); - if (!lo.hidden) { line = l; lineObj = lo; return true; } - } - } - function moveOnce(boundToLine) { - if (ch == (dir < 0 ? 0 : lineObj.text.length)) { - if (!boundToLine && findNextLine()) ch = dir < 0 ? lineObj.text.length : 0; - else return false; - } else ch += dir; - return true; - } - if (unit == "char") moveOnce(); - else if (unit == "column") moveOnce(true); - else if (unit == "word") { - var sawWord = false; - for (;;) { - if (dir < 0) if (!moveOnce()) break; - if (isWordChar(lineObj.text.charAt(ch))) sawWord = true; - else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;} - if (dir > 0) if (!moveOnce()) break; - } - } - return {line: line, ch: ch}; - } - function moveH(dir, unit) { - var pos = dir < 0 ? sel.from : sel.to; - if (shiftSelecting || posEq(sel.from, sel.to)) pos = findPosH(dir, unit); - setCursor(pos.line, pos.ch, true); - } - function deleteH(dir, unit) { - if (!posEq(sel.from, sel.to)) replaceRange("", sel.from, sel.to); - else if (dir < 0) replaceRange("", findPosH(dir, unit), sel.to); - else replaceRange("", sel.from, findPosH(dir, unit)); - userSelChange = true; - } - function moveV(dir, unit) { - var dist = 0, pos = localCoords(sel.inverted ? sel.from : sel.to, true); - if (goalColumn != null) pos.x = goalColumn; - if (unit == "page") { - var screen = Math.min(scroller.clientHeight, window.innerHeight || document.documentElement.clientHeight); - var target = coordsChar(pos.x, pos.y + screen * dir); - } else if (unit == "line") { - var th = textHeight(); - var target = coordsChar(pos.x, pos.y + .5 * th + dir * th); - } - if (unit == "page") scrollbar.scrollTop += localCoords(target, true).y - pos.y; - setCursor(target.line, target.ch, true); - goalColumn = pos.x; - } - - function findWordAt(pos) { - var line = getLine(pos.line).text; - var start = pos.ch, end = pos.ch; - if (line) { - if (pos.after === false || end == line.length) --start; else ++end; - var startChar = line.charAt(start); - var check = isWordChar(startChar) ? isWordChar : - /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} : - function(ch) {return !/\s/.test(ch) && isWordChar(ch);}; - while (start > 0 && check(line.charAt(start - 1))) --start; - while (end < line.length && check(line.charAt(end))) ++end; - } - return {from: {line: pos.line, ch: start}, to: {line: pos.line, ch: end}}; - } - function selectLine(line) { - setSelectionUser({line: line, ch: 0}, clipPos({line: line + 1, ch: 0})); - } - function indentSelected(mode) { - if (posEq(sel.from, sel.to)) return indentLine(sel.from.line, mode); - var e = sel.to.line - (sel.to.ch ? 0 : 1); - for (var i = sel.from.line; i <= e; ++i) indentLine(i, mode); - } - - function indentLine(n, how) { - if (!how) how = "add"; - if (how == "smart") { - if (!mode.indent) how = "prev"; - else var state = getStateBefore(n); - } - - var line = getLine(n), curSpace = line.indentation(options.tabSize), - curSpaceString = line.text.match(/^\s*/)[0], indentation; - if (how == "smart") { - indentation = mode.indent(state, line.text.slice(curSpaceString.length), line.text); - if (indentation == Pass) how = "prev"; - } - if (how == "prev") { - if (n) indentation = getLine(n-1).indentation(options.tabSize); - else indentation = 0; - } - else if (how == "add") indentation = curSpace + options.indentUnit; - else if (how == "subtract") indentation = curSpace - options.indentUnit; - indentation = Math.max(0, indentation); - var diff = indentation - curSpace; - - var indentString = "", pos = 0; - if (options.indentWithTabs) - for (var i = Math.floor(indentation / options.tabSize); i; --i) {pos += options.tabSize; indentString += "\t";} - if (pos < indentation) indentString += spaceStr(indentation - pos); - - if (indentString != curSpaceString) - replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length}); - line.stateAfter = null; - } - - function loadMode() { - mode = CodeMirror.getMode(options, options.mode); - doc.iter(0, doc.size, function(line) { line.stateAfter = null; }); - frontier = 0; - startWorker(100); - } - function gutterChanged() { - var visible = options.gutter || options.lineNumbers; - gutter.style.display = visible ? "" : "none"; - if (visible) gutterDirty = true; - else lineDiv.parentNode.style.marginLeft = 0; - } - function wrappingChanged(from, to) { - if (options.lineWrapping) { - wrapper.className += " CodeMirror-wrap"; - var perLine = scroller.clientWidth / charWidth() - 3; - doc.iter(0, doc.size, function(line) { - if (line.hidden) return; - var guess = Math.ceil(line.text.length / perLine) || 1; - if (guess != 1) updateLineHeight(line, guess); - }); - lineSpace.style.minWidth = widthForcer.style.left = ""; - } else { - wrapper.className = wrapper.className.replace(" CodeMirror-wrap", ""); - computeMaxLength(); - doc.iter(0, doc.size, function(line) { - if (line.height != 1 && !line.hidden) updateLineHeight(line, 1); - }); - } - changes.push({from: 0, to: doc.size}); - } - function themeChanged() { - scroller.className = scroller.className.replace(/\s*cm-s-\S+/g, "") + - options.theme.replace(/(^|\s)\s*/g, " cm-s-"); - } - function keyMapChanged() { - var style = keyMap[options.keyMap].style; - wrapper.className = wrapper.className.replace(/\s*cm-keymap-\S+/g, "") + - (style ? " cm-keymap-" + style : ""); - } - - function TextMarker(type, style) { this.lines = []; this.type = type; if (style) this.style = style; } - TextMarker.prototype.clear = operation(function() { - var min, max; - for (var i = 0; i < this.lines.length; ++i) { - var line = this.lines[i]; - var span = getMarkedSpanFor(line.markedSpans, this); - if (span.from != null) min = lineNo(line); - if (span.to != null) max = lineNo(line); - line.markedSpans = removeMarkedSpan(line.markedSpans, span); - } - if (min != null) changes.push({from: min, to: max + 1}); - this.lines.length = 0; - this.explicitlyCleared = true; - }); - TextMarker.prototype.find = function() { - var from, to; - for (var i = 0; i < this.lines.length; ++i) { - var line = this.lines[i]; - var span = getMarkedSpanFor(line.markedSpans, this); - if (span.from != null || span.to != null) { - var found = lineNo(line); - if (span.from != null) from = {line: found, ch: span.from}; - if (span.to != null) to = {line: found, ch: span.to}; - } - } - if (this.type == "bookmark") return from; - return from && {from: from, to: to}; - }; - - function markText(from, to, className, options) { - from = clipPos(from); to = clipPos(to); - var marker = new TextMarker("range", className); - if (options) for (var opt in options) if (options.hasOwnProperty(opt)) - marker[opt] = options[opt]; - var curLine = from.line; - doc.iter(curLine, to.line + 1, function(line) { - var span = {from: curLine == from.line ? from.ch : null, - to: curLine == to.line ? to.ch : null, - marker: marker}; - line.markedSpans = (line.markedSpans || []).concat([span]); - marker.lines.push(line); - ++curLine; - }); - changes.push({from: from.line, to: to.line + 1}); - return marker; - } - - function setBookmark(pos) { - pos = clipPos(pos); - var marker = new TextMarker("bookmark"), line = getLine(pos.line); - history.addChange(pos.line, 1, [newHL(line.text, line.markedSpans)], true); - var span = {from: pos.ch, to: pos.ch, marker: marker}; - line.markedSpans = (line.markedSpans || []).concat([span]); - marker.lines.push(line); - return marker; - } - - function findMarksAt(pos) { - pos = clipPos(pos); - var markers = [], spans = getLine(pos.line).markedSpans; - if (spans) for (var i = 0; i < spans.length; ++i) { - var span = spans[i]; - if ((span.from == null || span.from <= pos.ch) && - (span.to == null || span.to >= pos.ch)) - markers.push(span.marker); - } - return markers; - } - - function addGutterMarker(line, text, className) { - if (typeof line == "number") line = getLine(clipLine(line)); - line.gutterMarker = {text: text, style: className}; - gutterDirty = true; - return line; - } - function removeGutterMarker(line) { - if (typeof line == "number") line = getLine(clipLine(line)); - line.gutterMarker = null; - gutterDirty = true; - } - - function changeLine(handle, op) { - var no = handle, line = handle; - if (typeof handle == "number") line = getLine(clipLine(handle)); - else no = lineNo(handle); - if (no == null) return null; - if (op(line, no)) changes.push({from: no, to: no + 1}); - else return null; - return line; - } - function setLineClass(handle, className, bgClassName) { - return changeLine(handle, function(line) { - if (line.className != className || line.bgClassName != bgClassName) { - line.className = className; - line.bgClassName = bgClassName; - return true; - } - }); - } - function setLineHidden(handle, hidden) { - return changeLine(handle, function(line, no) { - if (line.hidden != hidden) { - line.hidden = hidden; - if (!options.lineWrapping) { - if (hidden && line.text.length == maxLine.text.length) { - updateMaxLine = true; - } else if (!hidden && line.text.length > maxLine.text.length) { - maxLine = line; updateMaxLine = false; - } - } - updateLineHeight(line, hidden ? 0 : 1); - var fline = sel.from.line, tline = sel.to.line; - if (hidden && (fline == no || tline == no)) { - var from = fline == no ? skipHidden({line: fline, ch: 0}, fline, 0) : sel.from; - var to = tline == no ? skipHidden({line: tline, ch: 0}, tline, 0) : sel.to; - // Can't hide the last visible line, we'd have no place to put the cursor - if (!to) return; - setSelection(from, to); - } - return (gutterDirty = true); - } - }); - } - - function lineInfo(line) { - if (typeof line == "number") { - if (!isLine(line)) return null; - var n = line; - line = getLine(line); - if (!line) return null; - } else { - var n = lineNo(line); - if (n == null) return null; - } - var marker = line.gutterMarker; - return {line: n, handle: line, text: line.text, markerText: marker && marker.text, - markerClass: marker && marker.style, lineClass: line.className, bgClass: line.bgClassName}; - } - - function measureLine(line, ch) { - if (ch == 0) return {top: 0, left: 0}; - var pre = lineContent(line, ch); - removeChildrenAndAdd(measure, pre); - var anchor = pre.anchor; - var top = anchor.offsetTop, left = anchor.offsetLeft; - // Older IEs report zero offsets for spans directly after a wrap - if (ie && top == 0 && left == 0) { - var backup = elt("span", "x"); - anchor.parentNode.insertBefore(backup, anchor.nextSibling); - top = backup.offsetTop; - } - return {top: top, left: left}; - } - function localCoords(pos, inLineWrap) { - var x, lh = textHeight(), y = lh * (heightAtLine(doc, pos.line) - (inLineWrap ? displayOffset : 0)); - if (pos.ch == 0) x = 0; - else { - var sp = measureLine(getLine(pos.line), pos.ch); - x = sp.left; - if (options.lineWrapping) y += Math.max(0, sp.top); - } - return {x: x, y: y, yBot: y + lh}; - } - // Coords must be lineSpace-local - function coordsChar(x, y) { - var th = textHeight(), cw = charWidth(), heightPos = displayOffset + Math.floor(y / th); - if (heightPos < 0) return {line: 0, ch: 0}; - var lineNo = lineAtHeight(doc, heightPos); - if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc.size - 1).text.length}; - var lineObj = getLine(lineNo), text = lineObj.text; - var tw = options.lineWrapping, innerOff = tw ? heightPos - heightAtLine(doc, lineNo) : 0; - if (x <= 0 && innerOff == 0) return {line: lineNo, ch: 0}; - var wrongLine = false; - function getX(len) { - var sp = measureLine(lineObj, len); - if (tw) { - var off = Math.round(sp.top / th); - wrongLine = off != innerOff; - return Math.max(0, sp.left + (off - innerOff) * scroller.clientWidth); - } - return sp.left; - } - var from = 0, fromX = 0, to = text.length, toX; - // Guess a suitable upper bound for our search. - var estimated = Math.min(to, Math.ceil((x + innerOff * scroller.clientWidth * .9) / cw)); - for (;;) { - var estX = getX(estimated); - if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2)); - else {toX = estX; to = estimated; break;} - } - if (x > toX) return {line: lineNo, ch: to}; - // Try to guess a suitable lower bound as well. - estimated = Math.floor(to * 0.8); estX = getX(estimated); - if (estX < x) {from = estimated; fromX = estX;} - // Do a binary search between these bounds. - for (;;) { - if (to - from <= 1) { - var after = x - fromX < toX - x; - return {line: lineNo, ch: after ? from : to, after: after}; - } - var middle = Math.ceil((from + to) / 2), middleX = getX(middle); - if (middleX > x) {to = middle; toX = middleX; if (wrongLine) toX += 1000; } - else {from = middle; fromX = middleX;} - } - } - function pageCoords(pos) { - var local = localCoords(pos, true), off = eltOffset(lineSpace); - return {x: off.left + local.x, y: off.top + local.y, yBot: off.top + local.yBot}; - } - - var cachedHeight, cachedHeightFor, measurePre; - function textHeight() { - if (measurePre == null) { - measurePre = elt("pre"); - for (var i = 0; i < 49; ++i) { - measurePre.appendChild(document.createTextNode("x")); - measurePre.appendChild(elt("br")); - } - measurePre.appendChild(document.createTextNode("x")); - } - var offsetHeight = lineDiv.clientHeight; - if (offsetHeight == cachedHeightFor) return cachedHeight; - cachedHeightFor = offsetHeight; - removeChildrenAndAdd(measure, measurePre.cloneNode(true)); - cachedHeight = measure.firstChild.offsetHeight / 50 || 1; - removeChildren(measure); - return cachedHeight; - } - var cachedWidth, cachedWidthFor = 0; - function charWidth() { - if (scroller.clientWidth == cachedWidthFor) return cachedWidth; - cachedWidthFor = scroller.clientWidth; - var anchor = elt("span", "x"); - var pre = elt("pre", [anchor]); - removeChildrenAndAdd(measure, pre); - return (cachedWidth = anchor.offsetWidth || 10); - } - function paddingTop() {return lineSpace.offsetTop;} - function paddingLeft() {return lineSpace.offsetLeft;} - - function posFromMouse(e, liberal) { - var offW = eltOffset(scroller, true), x, y; - // Fails unpredictably on IE[67] when mouse is dragged around quickly. - try { x = e.clientX; y = e.clientY; } catch (e) { return null; } - // This is a mess of a heuristic to try and determine whether a - // scroll-bar was clicked or not, and to return null if one was - // (and !liberal). - if (!liberal && (x - offW.left > scroller.clientWidth || y - offW.top > scroller.clientHeight)) - return null; - var offL = eltOffset(lineSpace, true); - return coordsChar(x - offL.left, y - offL.top); - } - var detectingSelectAll; - function onContextMenu(e) { - var pos = posFromMouse(e), scrollPos = scrollbar.scrollTop; - if (!pos || opera) return; // Opera is difficult. - if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to)) - operation(setCursor)(pos.line, pos.ch); - - var oldCSS = input.style.cssText; - inputDiv.style.position = "absolute"; - input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) + - "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; " + - "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; - focusInput(); - resetInput(true); - // Adds "Select all" to context menu in FF - if (posEq(sel.from, sel.to)) input.value = prevInput = " "; - - function rehide() { - inputDiv.style.position = "relative"; - input.style.cssText = oldCSS; - if (ie_lt9) scrollbar.scrollTop = scrollPos; - slowPoll(); - - // Try to detect the user choosing select-all - if (input.selectionStart != null) { - clearTimeout(detectingSelectAll); - var extval = input.value = " " + (posEq(sel.from, sel.to) ? "" : input.value), i = 0; - prevInput = " "; - input.selectionStart = 1; input.selectionEnd = extval.length; - detectingSelectAll = setTimeout(function poll(){ - if (prevInput == " " && input.selectionStart == 0) - operation(commands.selectAll)(instance); - else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500); - else resetInput(); - }, 200); - } - } - - if (gecko) { - e_stop(e); - var mouseup = connect(window, "mouseup", function() { - mouseup(); - setTimeout(rehide, 20); - }, true); - } else { - setTimeout(rehide, 50); - } - } - - // Cursor-blinking - function restartBlink() { - clearInterval(blinker); - var on = true; - cursor.style.visibility = ""; - blinker = setInterval(function() { - cursor.style.visibility = (on = !on) ? "" : "hidden"; - }, options.cursorBlinkRate); - } - - var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"}; - function matchBrackets(autoclear) { - var head = sel.inverted ? sel.from : sel.to, line = getLine(head.line), pos = head.ch - 1; - var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)]; - if (!match) return; - var ch = match.charAt(0), forward = match.charAt(1) == ">", d = forward ? 1 : -1, st = line.styles; - for (var off = pos + 1, i = 0, e = st.length; i < e; i+=2) - if ((off -= st[i].length) <= 0) {var style = st[i+1]; break;} - - var stack = [line.text.charAt(pos)], re = /[(){}[\]]/; - function scan(line, from, to) { - if (!line.text) return; - var st = line.styles, pos = forward ? 0 : line.text.length - 1, cur; - for (var i = forward ? 0 : st.length - 2, e = forward ? st.length : -2; i != e; i += 2*d) { - var text = st[i]; - if (st[i+1] != style) {pos += d * text.length; continue;} - for (var j = forward ? 0 : text.length - 1, te = forward ? text.length : -1; j != te; j += d, pos+=d) { - if (pos >= from && pos < to && re.test(cur = text.charAt(j))) { - var match = matching[cur]; - if (match.charAt(1) == ">" == forward) stack.push(cur); - else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false}; - else if (!stack.length) return {pos: pos, match: true}; - } - } - } - } - for (var i = head.line, e = forward ? Math.min(i + 100, doc.size) : Math.max(-1, i - 100); i != e; i+=d) { - var line = getLine(i), first = i == head.line; - var found = scan(line, first && forward ? pos + 1 : 0, first && !forward ? pos : line.text.length); - if (found) break; - } - if (!found) found = {pos: null, match: false}; - var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; - var one = markText({line: head.line, ch: pos}, {line: head.line, ch: pos+1}, style), - two = found.pos != null && markText({line: i, ch: found.pos}, {line: i, ch: found.pos + 1}, style); - var clear = operation(function(){one.clear(); two && two.clear();}); - if (autoclear) setTimeout(clear, 800); - else bracketHighlighted = clear; - } - - // Finds the line to start with when starting a parse. Tries to - // find a line with a stateAfter, so that it can start with a - // valid state. If that fails, it returns the line with the - // smallest indentation, which tends to need the least context to - // parse correctly. - function findStartLine(n) { - var minindent, minline; - for (var search = n, lim = n - 40; search > lim; --search) { - if (search == 0) return 0; - var line = getLine(search-1); - if (line.stateAfter) return search; - var indented = line.indentation(options.tabSize); - if (minline == null || minindent > indented) { - minline = search - 1; - minindent = indented; - } - } - return minline; - } - function getStateBefore(n) { - var pos = findStartLine(n), state = pos && getLine(pos-1).stateAfter; - if (!state) state = startState(mode); - else state = copyState(mode, state); - doc.iter(pos, n, function(line) { - line.process(mode, state, options.tabSize); - line.stateAfter = (pos == n - 1 || pos % 5 == 0) ? copyState(mode, state) : null; - }); - return state; - } - function highlightWorker() { - if (frontier >= showingTo) return; - var end = +new Date + options.workTime, state = copyState(mode, getStateBefore(frontier)); - var startFrontier = frontier; - doc.iter(frontier, showingTo, function(line) { - if (frontier >= showingFrom) { // Visible - line.highlight(mode, state, options.tabSize); - line.stateAfter = copyState(mode, state); - } else { - line.process(mode, state, options.tabSize); - line.stateAfter = frontier % 5 == 0 ? copyState(mode, state) : null; - } - ++frontier; - if (+new Date > end) { - startWorker(options.workDelay); - return true; - } - }); - if (showingTo > startFrontier && frontier >= showingFrom) - operation(function() {changes.push({from: startFrontier, to: frontier});})(); - } - function startWorker(time) { - if (frontier < showingTo) - highlight.set(time, highlightWorker); - } - - // Operations are used to wrap changes in such a way that each - // change won't have to update the cursor and display (which would - // be awkward, slow, and error-prone), but instead updates are - // batched and then all combined and executed at once. - function startOperation() { - updateInput = userSelChange = textChanged = null; - changes = []; selectionChanged = false; callbacks = []; - } - function endOperation() { - if (updateMaxLine) computeMaxLength(); - if (maxLineChanged && !options.lineWrapping) { - var cursorWidth = widthForcer.offsetWidth, left = measureLine(maxLine, maxLine.text.length).left; - if (!ie_lt8) { - widthForcer.style.left = left + "px"; - lineSpace.style.minWidth = (left + cursorWidth) + "px"; - } - maxLineChanged = false; - } - var newScrollPos, updated; - if (selectionChanged) { - var coords = calculateCursorCoords(); - newScrollPos = calculateScrollPos(coords.x, coords.y, coords.x, coords.yBot); - } - if (changes.length || newScrollPos && newScrollPos.scrollTop != null) - updated = updateDisplay(changes, true, newScrollPos && newScrollPos.scrollTop); - if (!updated) { - if (selectionChanged) updateSelection(); - if (gutterDirty) updateGutter(); - } - if (newScrollPos) scrollCursorIntoView(); - if (selectionChanged) restartBlink(); - - if (focused && (updateInput === true || (updateInput !== false && selectionChanged))) - resetInput(userSelChange); - - if (selectionChanged && options.matchBrackets) - setTimeout(operation(function() { - if (bracketHighlighted) {bracketHighlighted(); bracketHighlighted = null;} - if (posEq(sel.from, sel.to)) matchBrackets(false); - }), 20); - var sc = selectionChanged, cbs = callbacks; // these can be reset by callbacks - if (textChanged && options.onChange && instance) - options.onChange(instance, textChanged); - if (sc && options.onCursorActivity) - options.onCursorActivity(instance); - for (var i = 0; i < cbs.length; ++i) cbs[i](instance); - if (updated && options.onUpdate) options.onUpdate(instance); - } - var nestedOperation = 0; - function operation(f) { - return function() { - if (!nestedOperation++) startOperation(); - try {var result = f.apply(this, arguments);} - finally {if (!--nestedOperation) endOperation();} - return result; - }; - } - - function compoundChange(f) { - history.startCompound(); - try { return f(); } finally { history.endCompound(); } - } - - for (var ext in extensions) - if (extensions.propertyIsEnumerable(ext) && - !instance.propertyIsEnumerable(ext)) - instance[ext] = extensions[ext]; - for (var i = 0; i < initHooks.length; ++i) initHooks[i](instance); - return instance; - } // (end of function CodeMirror) - - // The default configuration options. - CodeMirror.defaults = { - value: "", - mode: null, - theme: "default", - indentUnit: 2, - indentWithTabs: false, - smartIndent: true, - tabSize: 4, - keyMap: "default", - extraKeys: null, - electricChars: true, - autoClearEmptyLines: false, - onKeyEvent: null, - onDragEvent: null, - lineWrapping: false, - lineNumbers: false, - gutter: false, - fixedGutter: false, - firstLineNumber: 1, - readOnly: false, - dragDrop: true, - onChange: null, - onCursorActivity: null, - onViewportChange: null, - onGutterClick: null, - onUpdate: null, - onFocus: null, onBlur: null, onScroll: null, - matchBrackets: false, - cursorBlinkRate: 530, - workTime: 100, - workDelay: 200, - pollInterval: 100, - undoDepth: 40, - tabindex: null, - autofocus: null, - lineNumberFormatter: function(integer) { return integer; } - }; - - var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent); - var mac = ios || /Mac/.test(navigator.platform); - var win = /Win/.test(navigator.platform); - - // Known modes, by name and by MIME - var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {}; - CodeMirror.defineMode = function(name, mode) { - if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name; - if (arguments.length > 2) { - mode.dependencies = []; - for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]); - } - modes[name] = mode; - }; - CodeMirror.defineMIME = function(mime, spec) { - mimeModes[mime] = spec; - }; - CodeMirror.resolveMode = function(spec) { - if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) - spec = mimeModes[spec]; - else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) - return CodeMirror.resolveMode("application/xml"); - if (typeof spec == "string") return {name: spec}; - else return spec || {name: "null"}; - }; - CodeMirror.getMode = function(options, spec) { - var spec = CodeMirror.resolveMode(spec); - var mfactory = modes[spec.name]; - if (!mfactory) return CodeMirror.getMode(options, "text/plain"); - var modeObj = mfactory(options, spec); - if (modeExtensions.hasOwnProperty(spec.name)) { - var exts = modeExtensions[spec.name]; - for (var prop in exts) if (exts.hasOwnProperty(prop)) modeObj[prop] = exts[prop]; - } - modeObj.name = spec.name; - return modeObj; - }; - CodeMirror.listModes = function() { - var list = []; - for (var m in modes) - if (modes.propertyIsEnumerable(m)) list.push(m); - return list; - }; - CodeMirror.listMIMEs = function() { - var list = []; - for (var m in mimeModes) - if (mimeModes.propertyIsEnumerable(m)) list.push({mime: m, mode: mimeModes[m]}); - return list; - }; - - var extensions = CodeMirror.extensions = {}; - CodeMirror.defineExtension = function(name, func) { - extensions[name] = func; - }; - - var initHooks = []; - CodeMirror.defineInitHook = function(f) {initHooks.push(f);}; - - var modeExtensions = CodeMirror.modeExtensions = {}; - CodeMirror.extendMode = function(mode, properties) { - var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}); - for (var prop in properties) if (properties.hasOwnProperty(prop)) - exts[prop] = properties[prop]; - }; - - var commands = CodeMirror.commands = { - selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});}, - killLine: function(cm) { - var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to); - if (!sel && cm.getLine(from.line).length == from.ch) cm.replaceRange("", from, {line: from.line + 1, ch: 0}); - else cm.replaceRange("", from, sel ? to : {line: from.line}); - }, - deleteLine: function(cm) {var l = cm.getCursor().line; cm.replaceRange("", {line: l, ch: 0}, {line: l});}, - undo: function(cm) {cm.undo();}, - redo: function(cm) {cm.redo();}, - goDocStart: function(cm) {cm.setCursor(0, 0, true);}, - goDocEnd: function(cm) {cm.setSelection({line: cm.lineCount() - 1}, null, true);}, - goLineStart: function(cm) {cm.setCursor(cm.getCursor().line, 0, true);}, - goLineStartSmart: function(cm) { - var cur = cm.getCursor(); - var text = cm.getLine(cur.line), firstNonWS = Math.max(0, text.search(/\S/)); - cm.setCursor(cur.line, cur.ch <= firstNonWS && cur.ch ? 0 : firstNonWS, true); - }, - goLineEnd: function(cm) {cm.setSelection({line: cm.getCursor().line}, null, true);}, - goLineUp: function(cm) {cm.moveV(-1, "line");}, - goLineDown: function(cm) {cm.moveV(1, "line");}, - goPageUp: function(cm) {cm.moveV(-1, "page");}, - goPageDown: function(cm) {cm.moveV(1, "page");}, - goCharLeft: function(cm) {cm.moveH(-1, "char");}, - goCharRight: function(cm) {cm.moveH(1, "char");}, - goColumnLeft: function(cm) {cm.moveH(-1, "column");}, - goColumnRight: function(cm) {cm.moveH(1, "column");}, - goWordLeft: function(cm) {cm.moveH(-1, "word");}, - goWordRight: function(cm) {cm.moveH(1, "word");}, - delCharLeft: function(cm) {cm.deleteH(-1, "char");}, - delCharRight: function(cm) {cm.deleteH(1, "char");}, - delWordLeft: function(cm) {cm.deleteH(-1, "word");}, - delWordRight: function(cm) {cm.deleteH(1, "word");}, - indentAuto: function(cm) {cm.indentSelection("smart");}, - indentMore: function(cm) {cm.indentSelection("add");}, - indentLess: function(cm) {cm.indentSelection("subtract");}, - insertTab: function(cm) {cm.replaceSelection("\t", "end");}, - defaultTab: function(cm) { - if (cm.somethingSelected()) cm.indentSelection("add"); - else cm.replaceSelection("\t", "end"); - }, - transposeChars: function(cm) { - var cur = cm.getCursor(), line = cm.getLine(cur.line); - if (cur.ch > 0 && cur.ch < line.length - 1) - cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1), - {line: cur.line, ch: cur.ch - 1}, {line: cur.line, ch: cur.ch + 1}); - }, - newlineAndIndent: function(cm) { - cm.replaceSelection("\n", "end"); - cm.indentLine(cm.getCursor().line); - }, - toggleOverwrite: function(cm) {cm.toggleOverwrite();} - }; - - var keyMap = CodeMirror.keyMap = {}; - keyMap.basic = { - "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", - "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", - "Delete": "delCharRight", "Backspace": "delCharLeft", "Tab": "defaultTab", "Shift-Tab": "indentAuto", - "Enter": "newlineAndIndent", "Insert": "toggleOverwrite" - }; - // Note that the save and find-related commands aren't defined by - // default. Unknown commands are simply ignored. - keyMap.pcDefault = { - "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", - "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd", - "Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", - "Ctrl-Backspace": "delWordLeft", "Ctrl-Delete": "delWordRight", "Ctrl-S": "save", "Ctrl-F": "find", - "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", - "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", - fallthrough: "basic" - }; - keyMap.macDefault = { - "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", - "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goWordLeft", - "Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordLeft", - "Ctrl-Alt-Backspace": "delWordRight", "Alt-Delete": "delWordRight", "Cmd-S": "save", "Cmd-F": "find", - "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", - "Cmd-[": "indentLess", "Cmd-]": "indentMore", - fallthrough: ["basic", "emacsy"] - }; - keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault; - keyMap.emacsy = { - "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", - "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", - "Ctrl-V": "goPageUp", "Shift-Ctrl-V": "goPageDown", "Ctrl-D": "delCharRight", "Ctrl-H": "delCharLeft", - "Alt-D": "delWordRight", "Alt-Backspace": "delWordLeft", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars" - }; - - function getKeyMap(val) { - if (typeof val == "string") return keyMap[val]; - else return val; - } - function lookupKey(name, extraMap, map, handle, stop) { - function lookup(map) { - map = getKeyMap(map); - var found = map[name]; - if (found === false) { - if (stop) stop(); - return true; - } - if (found != null && handle(found)) return true; - if (map.nofallthrough) { - if (stop) stop(); - return true; - } - var fallthrough = map.fallthrough; - if (fallthrough == null) return false; - if (Object.prototype.toString.call(fallthrough) != "[object Array]") - return lookup(fallthrough); - for (var i = 0, e = fallthrough.length; i < e; ++i) { - if (lookup(fallthrough[i])) return true; - } - return false; - } - if (extraMap && lookup(extraMap)) return true; - return lookup(map); - } - function isModifierKey(event) { - var name = keyNames[e_prop(event, "keyCode")]; - return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"; - } - CodeMirror.isModifierKey = isModifierKey; - - CodeMirror.fromTextArea = function(textarea, options) { - if (!options) options = {}; - options.value = textarea.value; - if (!options.tabindex && textarea.tabindex) - options.tabindex = textarea.tabindex; - // Set autofocus to true if this textarea is focused, or if it has - // autofocus and no other element is focused. - if (options.autofocus == null) { - var hasFocus = document.body; - // doc.activeElement occasionally throws on IE - try { hasFocus = document.activeElement; } catch(e) {} - options.autofocus = hasFocus == textarea || - textarea.getAttribute("autofocus") != null && hasFocus == document.body; - } - - function save() {textarea.value = instance.getValue();} - if (textarea.form) { - // Deplorable hack to make the submit method do the right thing. - var rmSubmit = connect(textarea.form, "submit", save, true); - if (typeof textarea.form.submit == "function") { - var realSubmit = textarea.form.submit; - textarea.form.submit = function wrappedSubmit() { - save(); - textarea.form.submit = realSubmit; - textarea.form.submit(); - textarea.form.submit = wrappedSubmit; - }; - } - } - - textarea.style.display = "none"; - var instance = CodeMirror(function(node) { - textarea.parentNode.insertBefore(node, textarea.nextSibling); - }, options); - instance.save = save; - instance.getTextArea = function() { return textarea; }; - instance.toTextArea = function() { - save(); - textarea.parentNode.removeChild(instance.getWrapperElement()); - textarea.style.display = ""; - if (textarea.form) { - rmSubmit(); - if (typeof textarea.form.submit == "function") - textarea.form.submit = realSubmit; - } - }; - return instance; - }; - - var gecko = /gecko\/\d{7}/i.test(navigator.userAgent); - var ie = /MSIE \d/.test(navigator.userAgent); - var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent); - var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent); - var quirksMode = ie && document.documentMode == 5; - var webkit = /WebKit\//.test(navigator.userAgent); - var chrome = /Chrome\//.test(navigator.userAgent); - var opera = /Opera\//.test(navigator.userAgent); - var safari = /Apple Computer/.test(navigator.vendor); - var khtml = /KHTML\//.test(navigator.userAgent); - var mac_geLion = /Mac OS X 10\D([7-9]|\d\d)\D/.test(navigator.userAgent); - - // Utility functions for working with state. Exported because modes - // sometimes need to do this. - function copyState(mode, state) { - if (state === true) return state; - if (mode.copyState) return mode.copyState(state); - var nstate = {}; - for (var n in state) { - var val = state[n]; - if (val instanceof Array) val = val.concat([]); - nstate[n] = val; - } - return nstate; - } - CodeMirror.copyState = copyState; - function startState(mode, a1, a2) { - return mode.startState ? mode.startState(a1, a2) : true; - } - CodeMirror.startState = startState; - CodeMirror.innerMode = function(mode, state) { - while (mode.innerMode) { - var info = mode.innerMode(state); - state = info.state; - mode = info.mode; - } - return info || {mode: mode, state: state}; - }; - - // The character stream used by a mode's parser. - function StringStream(string, tabSize) { - this.pos = this.start = 0; - this.string = string; - this.tabSize = tabSize || 8; - } - StringStream.prototype = { - eol: function() {return this.pos >= this.string.length;}, - sol: function() {return this.pos == 0;}, - peek: function() {return this.string.charAt(this.pos) || undefined;}, - next: function() { - if (this.pos < this.string.length) - return this.string.charAt(this.pos++); - }, - eat: function(match) { - var ch = this.string.charAt(this.pos); - if (typeof match == "string") var ok = ch == match; - else var ok = ch && (match.test ? match.test(ch) : match(ch)); - if (ok) {++this.pos; return ch;} - }, - eatWhile: function(match) { - var start = this.pos; - while (this.eat(match)){} - return this.pos > start; - }, - eatSpace: function() { - var start = this.pos; - while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos; - return this.pos > start; - }, - skipToEnd: function() {this.pos = this.string.length;}, - skipTo: function(ch) { - var found = this.string.indexOf(ch, this.pos); - if (found > -1) {this.pos = found; return true;} - }, - backUp: function(n) {this.pos -= n;}, - column: function() {return countColumn(this.string, this.start, this.tabSize);}, - indentation: function() {return countColumn(this.string, null, this.tabSize);}, - match: function(pattern, consume, caseInsensitive) { - if (typeof pattern == "string") { - var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;}; - if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) { - if (consume !== false) this.pos += pattern.length; - return true; - } - } else { - var match = this.string.slice(this.pos).match(pattern); - if (match && match.index > 0) return null; - if (match && consume !== false) this.pos += match[0].length; - return match; - } - }, - current: function(){return this.string.slice(this.start, this.pos);} - }; - CodeMirror.StringStream = StringStream; - - function MarkedSpan(from, to, marker) { - this.from = from; this.to = to; this.marker = marker; - } - - function getMarkedSpanFor(spans, marker) { - if (spans) for (var i = 0; i < spans.length; ++i) { - var span = spans[i]; - if (span.marker == marker) return span; - } - } - - function removeMarkedSpan(spans, span) { - var r; - for (var i = 0; i < spans.length; ++i) - if (spans[i] != span) (r || (r = [])).push(spans[i]); - return r; - } - - function markedSpansBefore(old, startCh, endCh) { - if (old) for (var i = 0, nw; i < old.length; ++i) { - var span = old[i], marker = span.marker; - var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh); - if (startsBefore || marker.type == "bookmark" && span.from == startCh && span.from != endCh) { - var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh); - (nw || (nw = [])).push({from: span.from, - to: endsAfter ? null : span.to, - marker: marker}); - } - } - return nw; - } - - function markedSpansAfter(old, endCh) { - if (old) for (var i = 0, nw; i < old.length; ++i) { - var span = old[i], marker = span.marker; - var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh); - if (endsAfter || marker.type == "bookmark" && span.from == endCh) { - var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh); - (nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh, - to: span.to == null ? null : span.to - endCh, - marker: marker}); - } - } - return nw; - } - - function updateMarkedSpans(oldFirst, oldLast, startCh, endCh, newText) { - if (!oldFirst && !oldLast) return newText; - // Get the spans that 'stick out' on both sides - var first = markedSpansBefore(oldFirst, startCh); - var last = markedSpansAfter(oldLast, endCh); - - // Next, merge those two ends - var sameLine = newText.length == 1, offset = lst(newText).length + (sameLine ? startCh : 0); - if (first) { - // Fix up .to properties of first - for (var i = 0; i < first.length; ++i) { - var span = first[i]; - if (span.to == null) { - var found = getMarkedSpanFor(last, span.marker); - if (!found) span.to = startCh; - else if (sameLine) span.to = found.to == null ? null : found.to + offset; - } - } - } - if (last) { - // Fix up .from in last (or move them into first in case of sameLine) - for (var i = 0; i < last.length; ++i) { - var span = last[i]; - if (span.to != null) span.to += offset; - if (span.from == null) { - var found = getMarkedSpanFor(first, span.marker); - if (!found) { - span.from = offset; - if (sameLine) (first || (first = [])).push(span); - } - } else { - span.from += offset; - if (sameLine) (first || (first = [])).push(span); - } - } - } - - var newMarkers = [newHL(newText[0], first)]; - if (!sameLine) { - // Fill gap with whole-line-spans - var gap = newText.length - 2, gapMarkers; - if (gap > 0 && first) - for (var i = 0; i < first.length; ++i) - if (first[i].to == null) - (gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker}); - for (var i = 0; i < gap; ++i) - newMarkers.push(newHL(newText[i+1], gapMarkers)); - newMarkers.push(newHL(lst(newText), last)); - } - return newMarkers; - } - - // hl stands for history-line, a data structure that can be either a - // string (line without markers) or a {text, markedSpans} object. - function hlText(val) { return typeof val == "string" ? val : val.text; } - function hlSpans(val) { - if (typeof val == "string") return null; - var spans = val.markedSpans, out = null; - for (var i = 0; i < spans.length; ++i) { - if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); } - else if (out) out.push(spans[i]); - } - return !out ? spans : out.length ? out : null; - } - function newHL(text, spans) { return spans ? {text: text, markedSpans: spans} : text; } - - function detachMarkedSpans(line) { - var spans = line.markedSpans; - if (!spans) return; - for (var i = 0; i < spans.length; ++i) { - var lines = spans[i].marker.lines; - var ix = indexOf(lines, line); - lines.splice(ix, 1); - } - line.markedSpans = null; - } - - function attachMarkedSpans(line, spans) { - if (!spans) return; - for (var i = 0; i < spans.length; ++i) - var marker = spans[i].marker.lines.push(line); - line.markedSpans = spans; - } - - // When measuring the position of the end of a line, different - // browsers require different approaches. If an empty span is added, - // many browsers report bogus offsets. Of those, some (Webkit, - // recent IE) will accept a space without moving the whole span to - // the next line when wrapping it, others work with a zero-width - // space. - var eolSpanContent = " "; - if (gecko || (ie && !ie_lt8)) eolSpanContent = "\u200b"; - else if (opera) eolSpanContent = ""; - - // Line objects. These hold state related to a line, including - // highlighting info (the styles array). - function Line(text, markedSpans) { - this.text = text; - this.height = 1; - attachMarkedSpans(this, markedSpans); - } - Line.prototype = { - update: function(text, markedSpans) { - this.text = text; - this.stateAfter = this.styles = null; - detachMarkedSpans(this); - attachMarkedSpans(this, markedSpans); - }, - // Run the given mode's parser over a line, update the styles - // array, which contains alternating fragments of text and CSS - // classes. - highlight: function(mode, state, tabSize) { - var stream = new StringStream(this.text, tabSize), st = this.styles || (this.styles = []); - var pos = st.length = 0; - if (this.text == "" && mode.blankLine) mode.blankLine(state); - while (!stream.eol()) { - var style = mode.token(stream, state), substr = stream.current(); - stream.start = stream.pos; - if (pos && st[pos-1] == style) { - st[pos-2] += substr; - } else if (substr) { - st[pos++] = substr; st[pos++] = style; - } - // Give up when line is ridiculously long - if (stream.pos > 5000) { - st[pos++] = this.text.slice(stream.pos); st[pos++] = null; - break; - } - } - }, - process: function(mode, state, tabSize) { - var stream = new StringStream(this.text, tabSize); - if (this.text == "" && mode.blankLine) mode.blankLine(state); - while (!stream.eol() && stream.pos <= 5000) { - mode.token(stream, state); - stream.start = stream.pos; - } - }, - // Fetch the parser token for a given character. Useful for hacks - // that want to inspect the mode state (say, for completion). - getTokenAt: function(mode, state, tabSize, ch) { - var txt = this.text, stream = new StringStream(txt, tabSize); - while (stream.pos < ch && !stream.eol()) { - stream.start = stream.pos; - var style = mode.token(stream, state); - } - return {start: stream.start, - end: stream.pos, - string: stream.current(), - className: style || null, - state: state}; - }, - indentation: function(tabSize) {return countColumn(this.text, null, tabSize);}, - // Produces an HTML fragment for the line, taking selection, - // marking, and highlighting into account. - getContent: function(tabSize, wrapAt, compensateForWrapping) { - var first = true, col = 0, specials = /[\t\u0000-\u0019\u200b\u2028\u2029\uFEFF]/g; - var pre = elt("pre"); - function span_(html, text, style) { - if (!text) return; - // Work around a bug where, in some compat modes, IE ignores leading spaces - if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1); - first = false; - if (!specials.test(text)) { - col += text.length; - var content = document.createTextNode(text); - } else { - var content = document.createDocumentFragment(), pos = 0; - while (true) { - specials.lastIndex = pos; - var m = specials.exec(text); - var skipped = m ? m.index - pos : text.length - pos; - if (skipped) { - content.appendChild(document.createTextNode(text.slice(pos, pos + skipped))); - col += skipped; - } - if (!m) break; - pos += skipped + 1; - if (m[0] == "\t") { - var tabWidth = tabSize - col % tabSize; - content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")); - col += tabWidth; - } else { - var token = elt("span", "\u2022", "cm-invalidchar"); - token.title = "\\u" + m[0].charCodeAt(0).toString(16); - content.appendChild(token); - col += 1; - } - } - } - if (style) html.appendChild(elt("span", [content], style)); - else html.appendChild(content); - } - var span = span_; - if (wrapAt != null) { - var outPos = 0, anchor = pre.anchor = elt("span"); - span = function(html, text, style) { - var l = text.length; - if (wrapAt >= outPos && wrapAt < outPos + l) { - var cut = wrapAt - outPos; - if (cut) { - span_(html, text.slice(0, cut), style); - // See comment at the definition of spanAffectsWrapping - if (compensateForWrapping) { - var view = text.slice(cut - 1, cut + 1); - if (spanAffectsWrapping.test(view)) html.appendChild(elt("wbr")); - else if (!ie_lt8 && /\w\w/.test(view)) html.appendChild(document.createTextNode("\u200d")); - } - } - html.appendChild(anchor); - span_(anchor, opera ? text.slice(cut, cut + 1) : text.slice(cut), style); - if (opera) span_(html, text.slice(cut + 1), style); - wrapAt--; - outPos += l; - } else { - outPos += l; - span_(html, text, style); - if (outPos == wrapAt && outPos == len) { - setTextContent(anchor, eolSpanContent); - html.appendChild(anchor); - } - // Stop outputting HTML when gone sufficiently far beyond measure - else if (outPos > wrapAt + 10 && /\s/.test(text)) span = function(){}; - } - }; - } - - var st = this.styles, allText = this.text, marked = this.markedSpans; - var len = allText.length; - function styleToClass(style) { - if (!style) return null; - return "cm-" + style.replace(/ +/g, " cm-"); - } - if (!allText && wrapAt == null) { - span(pre, " "); - } else if (!marked || !marked.length) { - for (var i = 0, ch = 0; ch < len; i+=2) { - var str = st[i], style = st[i+1], l = str.length; - if (ch + l > len) str = str.slice(0, len - ch); - ch += l; - span(pre, str, styleToClass(style)); - } - } else { - marked.sort(function(a, b) { return a.from - b.from; }); - var pos = 0, i = 0, text = "", style, sg = 0; - var nextChange = marked[0].from || 0, marks = [], markpos = 0; - var advanceMarks = function() { - var m; - while (markpos < marked.length && - ((m = marked[markpos]).from == pos || m.from == null)) { - if (m.marker.type == "range") marks.push(m); - ++markpos; - } - nextChange = markpos < marked.length ? marked[markpos].from : Infinity; - for (var i = 0; i < marks.length; ++i) { - var to = marks[i].to; - if (to == null) to = Infinity; - if (to == pos) marks.splice(i--, 1); - else nextChange = Math.min(to, nextChange); - } - }; - var m = 0; - while (pos < len) { - if (nextChange == pos) advanceMarks(); - var upto = Math.min(len, nextChange); - while (true) { - if (text) { - var end = pos + text.length; - var appliedStyle = style; - for (var j = 0; j < marks.length; ++j) { - var mark = marks[j]; - appliedStyle = (appliedStyle ? appliedStyle + " " : "") + mark.marker.style; - if (mark.marker.endStyle && mark.to === Math.min(end, upto)) appliedStyle += " " + mark.marker.endStyle; - if (mark.marker.startStyle && mark.from === pos) appliedStyle += " " + mark.marker.startStyle; - } - span(pre, end > upto ? text.slice(0, upto - pos) : text, appliedStyle); - if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;} - pos = end; - } - text = st[i++]; style = styleToClass(st[i++]); - } - } - } - return pre; - }, - cleanUp: function() { - this.parent = null; - detachMarkedSpans(this); - } - }; - - // Data structure that holds the sequence of lines. - function LeafChunk(lines) { - this.lines = lines; - this.parent = null; - for (var i = 0, e = lines.length, height = 0; i < e; ++i) { - lines[i].parent = this; - height += lines[i].height; - } - this.height = height; - } - LeafChunk.prototype = { - chunkSize: function() { return this.lines.length; }, - remove: function(at, n, callbacks) { - for (var i = at, e = at + n; i < e; ++i) { - var line = this.lines[i]; - this.height -= line.height; - line.cleanUp(); - if (line.handlers) - for (var j = 0; j < line.handlers.length; ++j) callbacks.push(line.handlers[j]); - } - this.lines.splice(at, n); - }, - collapse: function(lines) { - lines.splice.apply(lines, [lines.length, 0].concat(this.lines)); - }, - insertHeight: function(at, lines, height) { - this.height += height; - this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at)); - for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this; - }, - iterN: function(at, n, op) { - for (var e = at + n; at < e; ++at) - if (op(this.lines[at])) return true; - } - }; - function BranchChunk(children) { - this.children = children; - var size = 0, height = 0; - for (var i = 0, e = children.length; i < e; ++i) { - var ch = children[i]; - size += ch.chunkSize(); height += ch.height; - ch.parent = this; - } - this.size = size; - this.height = height; - this.parent = null; - } - BranchChunk.prototype = { - chunkSize: function() { return this.size; }, - remove: function(at, n, callbacks) { - this.size -= n; - for (var i = 0; i < this.children.length; ++i) { - var child = this.children[i], sz = child.chunkSize(); - if (at < sz) { - var rm = Math.min(n, sz - at), oldHeight = child.height; - child.remove(at, rm, callbacks); - this.height -= oldHeight - child.height; - if (sz == rm) { this.children.splice(i--, 1); child.parent = null; } - if ((n -= rm) == 0) break; - at = 0; - } else at -= sz; - } - if (this.size - n < 25) { - var lines = []; - this.collapse(lines); - this.children = [new LeafChunk(lines)]; - this.children[0].parent = this; - } - }, - collapse: function(lines) { - for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines); - }, - insert: function(at, lines) { - var height = 0; - for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height; - this.insertHeight(at, lines, height); - }, - insertHeight: function(at, lines, height) { - this.size += lines.length; - this.height += height; - for (var i = 0, e = this.children.length; i < e; ++i) { - var child = this.children[i], sz = child.chunkSize(); - if (at <= sz) { - child.insertHeight(at, lines, height); - if (child.lines && child.lines.length > 50) { - while (child.lines.length > 50) { - var spilled = child.lines.splice(child.lines.length - 25, 25); - var newleaf = new LeafChunk(spilled); - child.height -= newleaf.height; - this.children.splice(i + 1, 0, newleaf); - newleaf.parent = this; - } - this.maybeSpill(); - } - break; - } - at -= sz; - } - }, - maybeSpill: function() { - if (this.children.length <= 10) return; - var me = this; - do { - var spilled = me.children.splice(me.children.length - 5, 5); - var sibling = new BranchChunk(spilled); - if (!me.parent) { // Become the parent node - var copy = new BranchChunk(me.children); - copy.parent = me; - me.children = [copy, sibling]; - me = copy; - } else { - me.size -= sibling.size; - me.height -= sibling.height; - var myIndex = indexOf(me.parent.children, me); - me.parent.children.splice(myIndex + 1, 0, sibling); - } - sibling.parent = me.parent; - } while (me.children.length > 10); - me.parent.maybeSpill(); - }, - iter: function(from, to, op) { this.iterN(from, to - from, op); }, - iterN: function(at, n, op) { - for (var i = 0, e = this.children.length; i < e; ++i) { - var child = this.children[i], sz = child.chunkSize(); - if (at < sz) { - var used = Math.min(n, sz - at); - if (child.iterN(at, used, op)) return true; - if ((n -= used) == 0) break; - at = 0; - } else at -= sz; - } - } - }; - - function getLineAt(chunk, n) { - while (!chunk.lines) { - for (var i = 0;; ++i) { - var child = chunk.children[i], sz = child.chunkSize(); - if (n < sz) { chunk = child; break; } - n -= sz; - } - } - return chunk.lines[n]; - } - function lineNo(line) { - if (line.parent == null) return null; - var cur = line.parent, no = indexOf(cur.lines, line); - for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { - for (var i = 0, e = chunk.children.length; ; ++i) { - if (chunk.children[i] == cur) break; - no += chunk.children[i].chunkSize(); - } - } - return no; - } - function lineAtHeight(chunk, h) { - var n = 0; - outer: do { - for (var i = 0, e = chunk.children.length; i < e; ++i) { - var child = chunk.children[i], ch = child.height; - if (h < ch) { chunk = child; continue outer; } - h -= ch; - n += child.chunkSize(); - } - return n; - } while (!chunk.lines); - for (var i = 0, e = chunk.lines.length; i < e; ++i) { - var line = chunk.lines[i], lh = line.height; - if (h < lh) break; - h -= lh; - } - return n + i; - } - function heightAtLine(chunk, n) { - var h = 0; - outer: do { - for (var i = 0, e = chunk.children.length; i < e; ++i) { - var child = chunk.children[i], sz = child.chunkSize(); - if (n < sz) { chunk = child; continue outer; } - n -= sz; - h += child.height; - } - return h; - } while (!chunk.lines); - for (var i = 0; i < n; ++i) h += chunk.lines[i].height; - return h; - } - - // The history object 'chunks' changes that are made close together - // and at almost the same time into bigger undoable units. - function History() { - this.time = 0; - this.done = []; this.undone = []; - this.compound = 0; - this.closed = false; - } - History.prototype = { - addChange: function(start, added, old) { - this.undone.length = 0; - var time = +new Date, cur = lst(this.done), last = cur && lst(cur); - var dtime = time - this.time; - - if (cur && !this.closed && this.compound) { - cur.push({start: start, added: added, old: old}); - } else if (dtime > 400 || !last || this.closed || - last.start > start + old.length || last.start + last.added < start) { - this.done.push([{start: start, added: added, old: old}]); - this.closed = false; - } else { - var startBefore = Math.max(0, last.start - start), - endAfter = Math.max(0, (start + old.length) - (last.start + last.added)); - for (var i = startBefore; i > 0; --i) last.old.unshift(old[i - 1]); - for (var i = endAfter; i > 0; --i) last.old.push(old[old.length - i]); - if (startBefore) last.start = start; - last.added += added - (old.length - startBefore - endAfter); - } - this.time = time; - }, - startCompound: function() { - if (!this.compound++) this.closed = true; - }, - endCompound: function() { - if (!--this.compound) this.closed = true; - } - }; - - function stopMethod() {e_stop(this);} - // Ensure an event has a stop method. - function addStop(event) { - if (!event.stop) event.stop = stopMethod; - return event; - } - - function e_preventDefault(e) { - if (e.preventDefault) e.preventDefault(); - else e.returnValue = false; - } - function e_stopPropagation(e) { - if (e.stopPropagation) e.stopPropagation(); - else e.cancelBubble = true; - } - function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);} - CodeMirror.e_stop = e_stop; - CodeMirror.e_preventDefault = e_preventDefault; - CodeMirror.e_stopPropagation = e_stopPropagation; - - function e_target(e) {return e.target || e.srcElement;} - function e_button(e) { - var b = e.which; - if (b == null) { - if (e.button & 1) b = 1; - else if (e.button & 2) b = 3; - else if (e.button & 4) b = 2; - } - if (mac && e.ctrlKey && b == 1) b = 3; - return b; - } - - // Allow 3rd-party code to override event properties by adding an override - // object to an event object. - function e_prop(e, prop) { - var overridden = e.override && e.override.hasOwnProperty(prop); - return overridden ? e.override[prop] : e[prop]; - } - - // Event handler registration. If disconnect is true, it'll return a - // function that unregisters the handler. - function connect(node, type, handler, disconnect) { - if (typeof node.addEventListener == "function") { - node.addEventListener(type, handler, false); - if (disconnect) return function() {node.removeEventListener(type, handler, false);}; - } else { - var wrapHandler = function(event) {handler(event || window.event);}; - node.attachEvent("on" + type, wrapHandler); - if (disconnect) return function() {node.detachEvent("on" + type, wrapHandler);}; - } - } - CodeMirror.connect = connect; - - function Delayed() {this.id = null;} - Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}}; - - var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}}; - - // Detect drag-and-drop - var dragAndDrop = function() { - // There is *some* kind of drag-and-drop support in IE6-8, but I - // couldn't get it to work yet. - if (ie_lt9) return false; - var div = elt('div'); - return "draggable" in div || "dragDrop" in div; - }(); - - // Feature-detect whether newlines in textareas are converted to \r\n - var lineSep = function () { - var te = elt("textarea"); - te.value = "foo\nbar"; - if (te.value.indexOf("\r") > -1) return "\r\n"; - return "\n"; - }(); - - // For a reason I have yet to figure out, some browsers disallow - // word wrapping between certain characters *only* if a new inline - // element is started between them. This makes it hard to reliably - // measure the position of things, since that requires inserting an - // extra span. This terribly fragile set of regexps matches the - // character combinations that suffer from this phenomenon on the - // various browsers. - var spanAffectsWrapping = /^$/; // Won't match any two-character string - if (gecko) spanAffectsWrapping = /$'/; - else if (safari) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/; - else if (chrome) spanAffectsWrapping = /\-[^ \-\.?]|\?[^ \-\.?\]\}:;!'\"\),\/]|[\.!\"#&%\)*+,:;=>\]|\}~][\(\{\[<]|\$'/; - - // Counts the column offset in a string, taking tabs into account. - // Used mostly to find indentation. - function countColumn(string, end, tabSize) { - if (end == null) { - end = string.search(/[^\s\u00a0]/); - if (end == -1) end = string.length; - } - for (var i = 0, n = 0; i < end; ++i) { - if (string.charAt(i) == "\t") n += tabSize - (n % tabSize); - else ++n; - } - return n; - } - - function eltOffset(node, screen) { - // Take the parts of bounding client rect that we are interested in so we are able to edit if need be, - // since the returned value cannot be changed externally (they are kept in sync as the element moves within the page) - try { var box = node.getBoundingClientRect(); box = { top: box.top, left: box.left }; } - catch(e) { box = {top: 0, left: 0}; } - if (!screen) { - // Get the toplevel scroll, working around browser differences. - if (window.pageYOffset == null) { - var t = document.documentElement || document.body.parentNode; - if (t.scrollTop == null) t = document.body; - box.top += t.scrollTop; box.left += t.scrollLeft; - } else { - box.top += window.pageYOffset; box.left += window.pageXOffset; - } - } - return box; - } - - function eltText(node) { - return node.textContent || node.innerText || node.nodeValue || ""; - } - - var spaceStrs = [""]; - function spaceStr(n) { - while (spaceStrs.length <= n) - spaceStrs.push(lst(spaceStrs) + " "); - return spaceStrs[n]; - } - - function lst(arr) { return arr[arr.length-1]; } - - function selectInput(node) { - if (ios) { // Mobile Safari apparently has a bug where select() is broken. - node.selectionStart = 0; - node.selectionEnd = node.value.length; - } else node.select(); - } - - // Operations on {line, ch} objects. - function posEq(a, b) {return a.line == b.line && a.ch == b.ch;} - function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);} - function copyPos(x) {return {line: x.line, ch: x.ch};} - - function elt(tag, content, className, style) { - var e = document.createElement(tag); - if (className) e.className = className; - if (style) e.style.cssText = style; - if (typeof content == "string") setTextContent(e, content); - else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]); - return e; - } - function removeChildren(e) { - e.innerHTML = ""; - return e; - } - function removeChildrenAndAdd(parent, e) { - removeChildren(parent).appendChild(e); - } - function setTextContent(e, str) { - if (ie_lt9) { - e.innerHTML = ""; - e.appendChild(document.createTextNode(str)); - } else e.textContent = str; - } - - // Used to position the cursor after an undo/redo by finding the - // last edited character. - function editEnd(from, to) { - if (!to) return 0; - if (!from) return to.length; - for (var i = from.length, j = to.length; i >= 0 && j >= 0; --i, --j) - if (from.charAt(i) != to.charAt(j)) break; - return j + 1; - } - - function indexOf(collection, elt) { - if (collection.indexOf) return collection.indexOf(elt); - for (var i = 0, e = collection.length; i < e; ++i) - if (collection[i] == elt) return i; - return -1; - } - function isWordChar(ch) { - return /\w/.test(ch) || ch.toUpperCase() != ch.toLowerCase() || /[\u4E00-\u9FA5]/.test(ch); - } - - // See if "".split is the broken IE version, if so, provide an - // alternative way to split lines. - var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) { - var pos = 0, result = [], l = string.length; - while (pos <= l) { - var nl = string.indexOf("\n", pos); - if (nl == -1) nl = string.length; - var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl); - var rt = line.indexOf("\r"); - if (rt != -1) { - result.push(line.slice(0, rt)); - pos += rt + 1; - } else { - result.push(line); - pos = nl + 1; - } - } - return result; - } : function(string){return string.split(/\r\n?|\n/);}; - CodeMirror.splitLines = splitLines; - - var hasSelection = window.getSelection ? function(te) { - try { return te.selectionStart != te.selectionEnd; } - catch(e) { return false; } - } : function(te) { - try {var range = te.ownerDocument.selection.createRange();} - catch(e) {} - if (!range || range.parentElement() != te) return false; - return range.compareEndPoints("StartToEnd", range) != 0; - }; - - CodeMirror.defineMode("null", function() { - return {token: function(stream) {stream.skipToEnd();}}; - }); - CodeMirror.defineMIME("text/plain", "null"); - - var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", - 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", - 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", - 46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 109: "-", 107: "=", 127: "Delete", - 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", - 221: "]", 222: "'", 63276: "PageUp", 63277: "PageDown", 63275: "End", 63273: "Home", - 63234: "Left", 63232: "Up", 63235: "Right", 63233: "Down", 63302: "Insert", 63272: "Delete"}; - CodeMirror.keyNames = keyNames; - (function() { - // Number keys - for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i); - // Alphabetic keys - for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i); - // Function keys - for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i; - })(); - - CodeMirror.version = "2.35"; - - return CodeMirror; -})(); diff --git a/lib/client/editor/codemirror_v2/mode/javascript.js b/lib/client/editor/codemirror_v2/mode/javascript.js deleted file mode 100644 index 37f6f873..00000000 --- a/lib/client/editor/codemirror_v2/mode/javascript.js +++ /dev/null @@ -1,409 +0,0 @@ -// TODO actually recognize syntax of TypeScript constructs - -CodeMirror.defineMode("javascript", function(config, parserConfig) { - var indentUnit = config.indentUnit; - var jsonMode = parserConfig.json; - var isTS = parserConfig.typescript; - - // Tokenizer - - var keywords = function(){ - function kw(type) {return {type: type, style: "keyword"};} - var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"); - var operator = kw("operator"), atom = {type: "atom", style: "atom"}; - - var jsKeywords = { - "if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, - "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C, - "var": kw("var"), "const": kw("var"), "let": kw("var"), - "function": kw("function"), "catch": kw("catch"), - "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"), - "in": operator, "typeof": operator, "instanceof": operator, - "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom - }; - - // Extend the 'normal' keywords with the TypeScript language extensions - if (isTS) { - var type = {type: "variable", style: "variable-3"}; - var tsKeywords = { - // object-like things - "interface": kw("interface"), - "class": kw("class"), - "extends": kw("extends"), - "constructor": kw("constructor"), - - // scope modifiers - "public": kw("public"), - "private": kw("private"), - "protected": kw("protected"), - "static": kw("static"), - - "super": kw("super"), - - // types - "string": type, "number": type, "bool": type, "any": type - }; - - for (var attr in tsKeywords) { - jsKeywords[attr] = tsKeywords[attr]; - } - } - - return jsKeywords; - }(); - - var isOperatorChar = /[+\-*&%=<>!?|]/; - - function chain(stream, state, f) { - state.tokenize = f; - return f(stream, state); - } - - function nextUntilUnescaped(stream, end) { - var escaped = false, next; - while ((next = stream.next()) != null) { - if (next == end && !escaped) - return false; - escaped = !escaped && next == "\\"; - } - return escaped; - } - - // Used as scratch variables to communicate multiple values without - // consing up tons of objects. - var type, content; - function ret(tp, style, cont) { - type = tp; content = cont; - return style; - } - - function jsTokenBase(stream, state) { - var ch = stream.next(); - if (ch == '"' || ch == "'") - return chain(stream, state, jsTokenString(ch)); - else if (/[\[\]{}\(\),;\:\.]/.test(ch)) - return ret(ch); - else if (ch == "0" && stream.eat(/x/i)) { - stream.eatWhile(/[\da-f]/i); - return ret("number", "number"); - } - else if (/\d/.test(ch) || ch == "-" && stream.eat(/\d/)) { - stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/); - return ret("number", "number"); - } - else if (ch == "/") { - if (stream.eat("*")) { - return chain(stream, state, jsTokenComment); - } - else if (stream.eat("/")) { - stream.skipToEnd(); - return ret("comment", "comment"); - } - else if (state.lastType == "operator" || state.lastType == "keyword c" || - /^[\[{}\(,;:]$/.test(state.lastType)) { - nextUntilUnescaped(stream, "/"); - stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla - return ret("regexp", "string-2"); - } - else { - stream.eatWhile(isOperatorChar); - return ret("operator", null, stream.current()); - } - } - else if (ch == "#") { - stream.skipToEnd(); - return ret("error", "error"); - } - else if (isOperatorChar.test(ch)) { - stream.eatWhile(isOperatorChar); - return ret("operator", null, stream.current()); - } - else { - stream.eatWhile(/[\w\$_]/); - var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word]; - return (known && state.lastType != ".") ? ret(known.type, known.style, word) : - ret("variable", "variable", word); - } - } - - function jsTokenString(quote) { - return function(stream, state) { - if (!nextUntilUnescaped(stream, quote)) - state.tokenize = jsTokenBase; - return ret("string", "string"); - }; - } - - function jsTokenComment(stream, state) { - var maybeEnd = false, ch; - while (ch = stream.next()) { - if (ch == "/" && maybeEnd) { - state.tokenize = jsTokenBase; - break; - } - maybeEnd = (ch == "*"); - } - return ret("comment", "comment"); - } - - // Parser - - var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true}; - - function JSLexical(indented, column, type, align, prev, info) { - this.indented = indented; - this.column = column; - this.type = type; - this.prev = prev; - this.info = info; - if (align != null) this.align = align; - } - - function inScope(state, varname) { - for (var v = state.localVars; v; v = v.next) - if (v.name == varname) return true; - } - - function parseJS(state, style, type, content, stream) { - var cc = state.cc; - // Communicate our context to the combinators. - // (Less wasteful than consing up a hundred closures on every call.) - cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; - - if (!state.lexical.hasOwnProperty("align")) - state.lexical.align = true; - - while(true) { - var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement; - if (combinator(type, content)) { - while(cc.length && cc[cc.length - 1].lex) - cc.pop()(); - if (cx.marked) return cx.marked; - if (type == "variable" && inScope(state, content)) return "variable-2"; - return style; - } - } - } - - // Combinator utils - - var cx = {state: null, column: null, marked: null, cc: null}; - function pass() { - for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]); - } - function cont() { - pass.apply(null, arguments); - return true; - } - function register(varname) { - var state = cx.state; - if (state.context) { - cx.marked = "def"; - for (var v = state.localVars; v; v = v.next) - if (v.name == varname) return; - state.localVars = {name: varname, next: state.localVars}; - } - } - - // Combinators - - var defaultVars = {name: "this", next: {name: "arguments"}}; - function pushcontext() { - cx.state.context = {prev: cx.state.context, vars: cx.state.localVars}; - cx.state.localVars = defaultVars; - } - function popcontext() { - cx.state.localVars = cx.state.context.vars; - cx.state.context = cx.state.context.prev; - } - function pushlex(type, info) { - var result = function() { - var state = cx.state; - state.lexical = new JSLexical(state.indented, cx.stream.column(), type, null, state.lexical, info); - }; - result.lex = true; - return result; - } - function poplex() { - var state = cx.state; - if (state.lexical.prev) { - if (state.lexical.type == ")") - state.indented = state.lexical.indented; - state.lexical = state.lexical.prev; - } - } - poplex.lex = true; - - function expect(wanted) { - return function expecting(type) { - if (type == wanted) return cont(); - else if (wanted == ";") return pass(); - else return cont(arguments.callee); - }; - } - - function statement(type) { - if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex); - if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex); - if (type == "keyword b") return cont(pushlex("form"), statement, poplex); - if (type == "{") return cont(pushlex("}"), block, poplex); - if (type == ";") return cont(); - if (type == "function") return cont(functiondef); - if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"), - poplex, statement, poplex); - if (type == "variable") return cont(pushlex("stat"), maybelabel); - if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"), - block, poplex, poplex); - if (type == "case") return cont(expression, expect(":")); - if (type == "default") return cont(expect(":")); - if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"), - statement, poplex, popcontext); - return pass(pushlex("stat"), expression, expect(";"), poplex); - } - function expression(type) { - if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator); - if (type == "function") return cont(functiondef); - if (type == "keyword c") return cont(maybeexpression); - if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeoperator); - if (type == "operator") return cont(expression); - if (type == "[") return cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator); - if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator); - return cont(); - } - function maybeexpression(type) { - if (type.match(/[;\}\)\],]/)) return pass(); - return pass(expression); - } - - function maybeoperator(type, value) { - if (type == "operator" && /\+\+|--/.test(value)) return cont(maybeoperator); - if (type == "operator" && value == "?") return cont(expression, expect(":"), expression); - if (type == ";") return; - if (type == "(") return cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator); - if (type == ".") return cont(property, maybeoperator); - if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator); - } - function maybelabel(type) { - if (type == ":") return cont(poplex, statement); - return pass(maybeoperator, expect(";"), poplex); - } - function property(type) { - if (type == "variable") {cx.marked = "property"; return cont();} - } - function objprop(type) { - if (type == "variable") cx.marked = "property"; - if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expression); - } - function commasep(what, end) { - function proceed(type) { - if (type == ",") return cont(what, proceed); - if (type == end) return cont(); - return cont(expect(end)); - } - return function commaSeparated(type) { - if (type == end) return cont(); - else return pass(what, proceed); - }; - } - function block(type) { - if (type == "}") return cont(); - return pass(statement, block); - } - function maybetype(type) { - if (type == ":") return cont(typedef); - return pass(); - } - function typedef(type) { - if (type == "variable"){cx.marked = "variable-3"; return cont();} - return pass(); - } - function vardef1(type, value) { - if (type == "variable") { - register(value); - return isTS ? cont(maybetype, vardef2) : cont(vardef2); - } - return pass(); - } - function vardef2(type, value) { - if (value == "=") return cont(expression, vardef2); - if (type == ",") return cont(vardef1); - } - function forspec1(type) { - if (type == "var") return cont(vardef1, expect(";"), forspec2); - if (type == ";") return cont(forspec2); - if (type == "variable") return cont(formaybein); - return cont(forspec2); - } - function formaybein(type, value) { - if (value == "in") return cont(expression); - return cont(maybeoperator, forspec2); - } - function forspec2(type, value) { - if (type == ";") return cont(forspec3); - if (value == "in") return cont(expression); - return cont(expression, expect(";"), forspec3); - } - function forspec3(type) { - if (type != ")") cont(expression); - } - function functiondef(type, value) { - if (type == "variable") {register(value); return cont(functiondef);} - if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext); - } - function funarg(type, value) { - if (type == "variable") {register(value); return isTS ? cont(maybetype) : cont();} - } - - // Interface - - return { - startState: function(basecolumn) { - return { - tokenize: jsTokenBase, - lastType: null, - cc: [], - lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false), - localVars: parserConfig.localVars, - context: parserConfig.localVars && {vars: parserConfig.localVars}, - indented: 0 - }; - }, - - token: function(stream, state) { - if (stream.sol()) { - if (!state.lexical.hasOwnProperty("align")) - state.lexical.align = false; - state.indented = stream.indentation(); - } - if (stream.eatSpace()) return null; - var style = state.tokenize(stream, state); - if (type == "comment") return style; - state.lastType = type; - return parseJS(state, style, type, content, stream); - }, - - indent: function(state, textAfter) { - if (state.tokenize == jsTokenComment) return CodeMirror.Pass; - if (state.tokenize != jsTokenBase) return 0; - var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical; - if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev; - var type = lexical.type, closing = firstChar == type; - if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? 4 : 0); - else if (type == "form" && firstChar == "{") return lexical.indented; - else if (type == "form") return lexical.indented + indentUnit; - else if (type == "stat") - return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? indentUnit : 0); - else if (lexical.info == "switch" && !closing) - return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit); - else if (lexical.align) return lexical.column + (closing ? 0 : 1); - else return lexical.indented + (closing ? 0 : indentUnit); - }, - - electricChars: ":{}" - }; -}); - -CodeMirror.defineMIME("text/javascript", "javascript"); -CodeMirror.defineMIME("application/json", {name: "javascript", json: true}); -CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true }); -CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true }); diff --git a/lib/client/editor/codemirror_v2/mode/xml.js b/lib/client/editor/codemirror_v2/mode/xml.js deleted file mode 100644 index 860e368f..00000000 --- a/lib/client/editor/codemirror_v2/mode/xml.js +++ /dev/null @@ -1,318 +0,0 @@ -CodeMirror.defineMode("xml", function(config, parserConfig) { - var indentUnit = config.indentUnit; - var Kludges = parserConfig.htmlMode ? { - autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true, - 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true, - 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true, - 'track': true, 'wbr': true}, - implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true, - 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true, - 'th': true, 'tr': true}, - contextGrabbers: { - 'dd': {'dd': true, 'dt': true}, - 'dt': {'dd': true, 'dt': true}, - 'li': {'li': true}, - 'option': {'option': true, 'optgroup': true}, - 'optgroup': {'optgroup': true}, - 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true, - 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true, - 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true, - 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true, - 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true}, - 'rp': {'rp': true, 'rt': true}, - 'rt': {'rp': true, 'rt': true}, - 'tbody': {'tbody': true, 'tfoot': true}, - 'td': {'td': true, 'th': true}, - 'tfoot': {'tbody': true}, - 'th': {'td': true, 'th': true}, - 'thead': {'tbody': true, 'tfoot': true}, - 'tr': {'tr': true} - }, - doNotIndent: {"pre": true}, - allowUnquoted: true, - allowMissing: true - } : { - autoSelfClosers: {}, - implicitlyClosed: {}, - contextGrabbers: {}, - doNotIndent: {}, - allowUnquoted: false, - allowMissing: false - }; - var alignCDATA = parserConfig.alignCDATA; - - // Return variables for tokenizers - var tagName, type; - - function inText(stream, state) { - function chain(parser) { - state.tokenize = parser; - return parser(stream, state); - } - - var ch = stream.next(); - if (ch == "<") { - if (stream.eat("!")) { - if (stream.eat("[")) { - if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>")); - else return null; - } - else if (stream.match("--")) return chain(inBlock("comment", "-->")); - else if (stream.match("DOCTYPE", true, true)) { - stream.eatWhile(/[\w\._\-]/); - return chain(doctype(1)); - } - else return null; - } - else if (stream.eat("?")) { - stream.eatWhile(/[\w\._\-]/); - state.tokenize = inBlock("meta", "?>"); - return "meta"; - } - else { - type = stream.eat("/") ? "closeTag" : "openTag"; - stream.eatSpace(); - tagName = ""; - var c; - while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c; - state.tokenize = inTag; - return "tag"; - } - } - else if (ch == "&") { - var ok; - if (stream.eat("#")) { - if (stream.eat("x")) { - ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";"); - } else { - ok = stream.eatWhile(/[\d]/) && stream.eat(";"); - } - } else { - ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";"); - } - return ok ? "atom" : "error"; - } - else { - stream.eatWhile(/[^&<]/); - return null; - } - } - - function inTag(stream, state) { - var ch = stream.next(); - if (ch == ">" || (ch == "/" && stream.eat(">"))) { - state.tokenize = inText; - type = ch == ">" ? "endTag" : "selfcloseTag"; - return "tag"; - } - else if (ch == "=") { - type = "equals"; - return null; - } - else if (/[\'\"]/.test(ch)) { - state.tokenize = inAttribute(ch); - return state.tokenize(stream, state); - } - else { - stream.eatWhile(/[^\s\u00a0=<>\"\'\/?]/); - return "word"; - } - } - - function inAttribute(quote) { - return function(stream, state) { - while (!stream.eol()) { - if (stream.next() == quote) { - state.tokenize = inTag; - break; - } - } - return "string"; - }; - } - - function inBlock(style, terminator) { - return function(stream, state) { - while (!stream.eol()) { - if (stream.match(terminator)) { - state.tokenize = inText; - break; - } - stream.next(); - } - return style; - }; - } - function doctype(depth) { - return function(stream, state) { - var ch; - while ((ch = stream.next()) != null) { - if (ch == "<") { - state.tokenize = doctype(depth + 1); - return state.tokenize(stream, state); - } else if (ch == ">") { - if (depth == 1) { - state.tokenize = inText; - break; - } else { - state.tokenize = doctype(depth - 1); - return state.tokenize(stream, state); - } - } - } - return "meta"; - }; - } - - var curState, setStyle; - function pass() { - for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]); - } - function cont() { - pass.apply(null, arguments); - return true; - } - - function pushContext(tagName, startOfLine) { - var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent); - curState.context = { - prev: curState.context, - tagName: tagName, - indent: curState.indented, - startOfLine: startOfLine, - noIndent: noIndent - }; - } - function popContext() { - if (curState.context) curState.context = curState.context.prev; - } - - function element(type) { - if (type == "openTag") { - curState.tagName = tagName; - return cont(attributes, endtag(curState.startOfLine)); - } else if (type == "closeTag") { - var err = false; - if (curState.context) { - if (curState.context.tagName != tagName) { - if (Kludges.implicitlyClosed.hasOwnProperty(curState.context.tagName.toLowerCase())) { - popContext(); - } - err = !curState.context || curState.context.tagName != tagName; - } - } else { - err = true; - } - if (err) setStyle = "error"; - return cont(endclosetag(err)); - } - return cont(); - } - function endtag(startOfLine) { - return function(type) { - if (type == "selfcloseTag" || - (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(curState.tagName.toLowerCase()))) { - maybePopContext(curState.tagName.toLowerCase()); - return cont(); - } - if (type == "endTag") { - maybePopContext(curState.tagName.toLowerCase()); - pushContext(curState.tagName, startOfLine); - return cont(); - } - return cont(); - }; - } - function endclosetag(err) { - return function(type) { - if (err) setStyle = "error"; - if (type == "endTag") { popContext(); return cont(); } - setStyle = "error"; - return cont(arguments.callee); - }; - } - function maybePopContext(nextTagName) { - var parentTagName; - while (true) { - if (!curState.context) { - return; - } - parentTagName = curState.context.tagName.toLowerCase(); - if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) || - !Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) { - return; - } - popContext(); - } - } - - function attributes(type) { - if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);} - if (type == "endTag" || type == "selfcloseTag") return pass(); - setStyle = "error"; - return cont(attributes); - } - function attribute(type) { - if (type == "equals") return cont(attvalue, attributes); - if (!Kludges.allowMissing) setStyle = "error"; - return (type == "endTag" || type == "selfcloseTag") ? pass() : cont(); - } - function attvalue(type) { - if (type == "string") return cont(attvaluemaybe); - if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();} - setStyle = "error"; - return (type == "endTag" || type == "selfCloseTag") ? pass() : cont(); - } - function attvaluemaybe(type) { - if (type == "string") return cont(attvaluemaybe); - else return pass(); - } - - return { - startState: function() { - return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, context: null}; - }, - - token: function(stream, state) { - if (stream.sol()) { - state.startOfLine = true; - state.indented = stream.indentation(); - } - if (stream.eatSpace()) return null; - - setStyle = type = tagName = null; - var style = state.tokenize(stream, state); - state.type = type; - if ((style || type) && style != "comment") { - curState = state; - while (true) { - var comb = state.cc.pop() || element; - if (comb(type || style)) break; - } - } - state.startOfLine = false; - return setStyle || style; - }, - - indent: function(state, textAfter, fullLine) { - var context = state.context; - if ((state.tokenize != inTag && state.tokenize != inText) || - context && context.noIndent) - return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0; - if (alignCDATA && /' + pData + '
', lConfig); @@ -180,8 +172,9 @@ var CloudCommander, Util, DOM, CloudFunc, $; FancyBox.show = function(pCallBack){ set(); - var lConfig = FancyBox.getConfig(), + var lConfig = getConfig(), lResult = Util.exec(pCallBack); + if(!lResult){ var lCurrentFile = DOM.getCurrentFile(), lA = DOM.getByClass('fancybox', lCurrentFile)[0]; @@ -190,7 +183,8 @@ var CloudCommander, Util, DOM, CloudFunc, $; if(lA.rel) $.fancybox.open({ href : lA.href }, lConfig); - else FancyBox.loadData(lA, FancyBox.onDataLoaded); + else + FancyBox.loadData(lA, FancyBox.onDataLoaded); } } }; @@ -210,7 +204,7 @@ var CloudCommander, Util, DOM, CloudFunc, $; var lF3 = cloudcmd.KEY.F3, lKeyBinded = KeyBinding.get(), lKey = event.keyCode, - lShift = event.shiftKey; + lShift = event.shiftKey; /* если клавиши можно обрабатывать */ if( lKeyBinded && lKey === lF3 && lShift ){ From d91c9953ee5c8c0b9cb4e431045c6b83931a55c6 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 15 Nov 2012 05:10:10 -0500 Subject: [PATCH 158/281] fixed bug with keys panel and fm bottom margin --- ChangeLog | 1 + css/style.css | 2 +- lib/client/editor/codemirror3/codemirror.css | 10 +- lib/client/editor/codemirror3/codemirror.js | 1030 +++++++++-------- .../editor/codemirror3/mode/javascript.js | 78 +- lib/client/editor/codemirror3/package.json | 2 +- 6 files changed, 650 insertions(+), 473 deletions(-) diff --git a/ChangeLog b/ChangeLog index fc8f5d87..bbcd31f4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -87,6 +87,7 @@ dom.js and util.js. * CodeMirror upgraded to version 2.35.0. +* Fixed bug with keys panel and fm bottom margin. 2012.10.01, Version 0.1.7 diff --git a/css/style.css b/css/style.css index 9ab14010..d64dc79c 100644 --- a/css/style.css +++ b/css/style.css @@ -175,7 +175,7 @@ body{ } #fm{ height: 90%; - margin: 26px; + margin: 26px 26px 0 26px; } .fm_header{ font-weight: bold; diff --git a/lib/client/editor/codemirror3/codemirror.css b/lib/client/editor/codemirror3/codemirror.css index 9de98196..9be4b005 100644 --- a/lib/client/editor/codemirror3/codemirror.css +++ b/lib/client/editor/codemirror3/codemirror.css @@ -175,6 +175,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} color: inherit; z-index: 2; position: relative; + overflow: visible; } .CodeMirror-wrap pre { word-wrap: break-word; @@ -187,6 +188,11 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} z-index: 0; } +.CodeMirror-linewidget { + position: relative; + z-index: 2; +} + .CodeMirror-wrap .CodeMirror-scroll { overflow-x: hidden; } @@ -209,8 +215,8 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} visibility: visible; } -div.CodeMirror-selected { background: #d9d9d9; } -.CodeMirror-focused div.CodeMirror-selected { background: #d7d4f0; } +.CodeMirror-selected { background: #d9d9d9; } +.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } .CodeMirror-searching { background: #ffa; diff --git a/lib/client/editor/codemirror3/codemirror.js b/lib/client/editor/codemirror3/codemirror.js index 3ebd1807..a9c7412e 100644 --- a/lib/client/editor/codemirror3/codemirror.js +++ b/lib/client/editor/codemirror3/codemirror.js @@ -1,3 +1,5 @@ +// CodeMirror version 3.0beta2 +// // CodeMirror is the only global var we claim window.CodeMirror = (function() { "use strict"; @@ -10,12 +12,14 @@ window.CodeMirror = (function() { var ie = /MSIE \d/.test(navigator.userAgent); var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent); var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent); + var ie_lt10 = /MSIE [1-9]\b/.test(navigator.userAgent); var webkit = /WebKit\//.test(navigator.userAgent); var chrome = /Chrome\//.test(navigator.userAgent); var opera = /Opera\//.test(navigator.userAgent); var safari = /Apple Computer/.test(navigator.vendor); var khtml = /KHTML\//.test(navigator.userAgent); - var mac_geLion = /Mac OS X 10\D([7-9]|\d\d)\D/.test(navigator.userAgent); + var mac_geLion = /Mac OS X 1\d\D([7-9]|\d\d)\D/.test(navigator.userAgent); + var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(navigator.userAgent); var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent); var mac = ios || /Mac/.test(navigator.platform); @@ -41,16 +45,14 @@ window.CodeMirror = (function() { if (options.autofocus) focusInput(this); if (options.lineWrapping) display.wrapper.className += " CodeMirror-wrap"; - var doc = new BranchChunk([new LeafChunk([new Line("", null, textHeight(display))])]); - // frontier is the point up to which the content has been parsed, - doc.frontier = 0; - doc.highlight = new Delayed(); - doc.tabSize = options.tabSize; + var doc = new BranchChunk([new LeafChunk([makeLine("", null, textHeight(display))])]); // The selection. These are always maintained to point at valid // positions. Inverted is used to remember that the user is // selecting bottom-to-top. this.view = { doc: doc, + // frontier is the point up to which the content has been parsed, + frontier: 0, highlight: new Delayed(), sel: {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false, shift: false}, scrollTop: 0, scrollLeft: 0, overwrite: false, focused: false, @@ -66,7 +68,10 @@ window.CodeMirror = (function() { // Initialize the content. this.setValue(options.value || ""); - doc.history = new History(); + // Override magic textarea content restore that IE sometimes does + // on our hidden textarea on reload + if (ie) setTimeout(bind(resetInput, this, true), 20); + this.view.history = makeHistory(); registerEventHandlers(this); // IE throws unspecified error in certain cases, when @@ -78,6 +83,7 @@ window.CodeMirror = (function() { for (var opt in optionHandlers) if (optionHandlers.propertyIsEnumerable(opt)) optionHandlers[opt](this, options[opt]); + for (var i = 0; i < initHooks.length; ++i) initHooks[i](this); } // DISPLAY CONSTRUCTOR @@ -102,7 +108,7 @@ window.CodeMirror = (function() { // Used to measure text size d.measure = elt("div", null, "CodeMirror-measure"); // Wraps everything that needs to exist inside the vertically-padded coordinate system - d.lineSpace = elt("div", [d.measure, d.cursor, d.otherCursor, d.selectionDiv, d.lineDiv], + d.lineSpace = elt("div", [d.measure, d.selectionDiv, d.lineDiv, d.cursor, d.otherCursor], null, "position: relative; outline: none"); // Moved around its parent to cover visible view d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative"); @@ -158,6 +164,10 @@ window.CodeMirror = (function() { // string instead of the (large) selection. d.inaccurateSelection = false; + // Used to adjust overwrite behaviour when a paste has been + // detected + d.pasteIncoming = false; + return d; } @@ -167,9 +177,9 @@ window.CodeMirror = (function() { function loadMode(cm) { var doc = cm.view.doc; - doc.mode = CodeMirror.getMode(cm.options, cm.options.mode); + cm.view.mode = CodeMirror.getMode(cm.options, cm.options.mode); doc.iter(0, doc.size, function(line) { line.stateAfter = null; }); - doc.frontier = 0; + cm.view.frontier = 0; startWorker(cm, 100); } @@ -289,7 +299,7 @@ window.CodeMirror = (function() { } else d.scrollbarFiller.style.display = ""; if (mac_geLion && scrollbarWidth(d.measure) === 0) - d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = "12px"; + d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px"; } function visibleLines(display, doc, scrollTop) { @@ -342,7 +352,7 @@ window.CodeMirror = (function() { if (updated) { signalLater(cm, cm, "update", cm); if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo) - signalLater(cm, cm, "update", cm, cm.display.showingFrom, cm.display.showingTo); + signalLater(cm, cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo); } updateSelection(cm); updateScrollbars(cm.display, cm.view.doc.height, scrollTop); @@ -377,7 +387,8 @@ window.CodeMirror = (function() { for (var i = 0; i < changes.length; ++i) if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; } - var from = Math.max(visible.from - 100, 0), to = Math.min(doc.size, visible.to + 100); + var from = Math.max(visible.from - cm.options.viewportMargin, 0); + var to = Math.min(doc.size, visible.to + cm.options.viewportMargin); if (display.showingFrom < from && from - display.showingFrom < 20) from = display.showingFrom; if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(doc.size, display.showingTo); @@ -398,7 +409,7 @@ window.CodeMirror = (function() { return; intact.sort(function(a, b) {return a.domStart - b.domStart;}); - display.lineDiv.style.display = "none"; + if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none"; patchDisplay(cm, from, to, intact, positionsChangedFrom); display.lineDiv.style.display = ""; @@ -419,22 +430,19 @@ window.CodeMirror = (function() { // Update line heights for visible lines based on actual DOM // sizes - var curNode = display.lineDiv.firstChild, heightChanged = false; - var relativeTo = curNode.offsetTop; + var curNode = display.lineDiv.firstChild, relativeTo = curNode.offsetTop; doc.iter(display.showingFrom, display.showingTo, function(line) { // Work around bizarro IE7 bug where, sometimes, our curNode // is magically replaced with a new node in the DOM, leaving // us with a reference to an orphan (nextSibling-less) node. if (!curNode) return; - if (!line.hidden) { + if (!line.hidden || (line.hidden.handle.showWidgets && line.widgets)) { var end = curNode.offsetHeight + curNode.offsetTop; var height = end - relativeTo, diff = line.height - height; if (height < 2) height = textHeight(display); relativeTo = end; - if (diff > .001 || diff < -.001) { + if (diff > .001 || diff < -.001) updateLineHeight(line, height); - heightChanged = true; - } } curNode = curNode.nextSibling; }); @@ -467,16 +475,34 @@ window.CodeMirror = (function() { return intact; } + function getDimensions(cm) { + var d = cm.display, left = {}, width = {}; + for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) { + left[cm.options.gutters[i]] = n.offsetLeft; + width[cm.options.gutters[i]] = n.offsetWidth; + } + return {fixedPos: compensateForHScroll(d), + gutterTotalWidth: d.gutters.offsetWidth, + gutterLeft: left, + gutterWidth: width, + wrapperWidth: d.wrapper.clientWidth}; + } + function patchDisplay(cm, from, to, intact, updateNumbersFrom) { function killNode(node) { var tmp = node.nextSibling; node.parentNode.removeChild(node); return tmp; } + var dims = getDimensions(cm); var display = cm.display, lineNumbers = cm.options.lineNumbers; // The first pass removes the DOM nodes that aren't intact. - if (!intact.length) removeChildren(display.lineDiv); - else { + if (!intact.length) { + // old IE does bad things to nodes when .innerHTML = "" is used on a parent + // we still need widgets and markers intact to add back to the new content later + if (ie_lt10) for (var ld = display.lineDiv, tmp = ld.firstChild; tmp; tmp = ld.firstChild) ld.removeChild(tmp); + else removeChildren(display.lineDiv); + } else { var domPos = 0, curNode = display.lineDiv.firstChild, n; for (var i = 0; i < intact.length; ++i) { var cur = intact[i]; @@ -491,79 +517,84 @@ window.CodeMirror = (function() { } // This pass fills in the lines that actually changed. var nextIntact = intact.shift(), curNode = display.lineDiv.firstChild; - var j = from, gutterSpecs = cm.options.gutters; - var fixedPos = compensateForHScroll(display); + var j = from; cm.view.doc.iter(from, to, function(line) { if (nextIntact && nextIntact.to == j) nextIntact = intact.shift(); - if (!nextIntact || nextIntact.from > j) { - if (line.hidden) var lineElement = elt("div"); - else { - var lineElement = lineContent(cm, line), markers = line.gutterMarkers; - if (line.className) lineElement.className = line.className; - // Lines with gutter elements or a background class need - // to be wrapped again, and have the extra elements added - - // to the wrapper div - if (lineNumbers || markers || line.bgClassName || (line.widgets && line.widgets.length)) { - var wrap = elt("div", null, null, "position: relative"); - if (lineNumbers || markers) { - var gutterWrap = wrap.appendChild(elt("div", null, null, - "position: absolute; left: " + fixedPos + "px")); - wrap.alignable = [gutterWrap]; - if (lineNumbers) - gutterWrap.appendChild(elt("div", lineNumberFor(cm.options, j), - "CodeMirror-linenumber CodeMirror-gutter-elt", - "left: " + display.lineGutter.offsetLeft + "px; width: " - + display.lineNumInnerWidth + "px")); - if (markers) - for (var k = 0; k < gutterSpecs.length; ++k) { - var id = gutterSpecs[k], found = markers.hasOwnProperty(id) && markers[id]; - if (found) { - var gutterElt = display.gutters.childNodes[k]; - gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " + - gutterElt.offsetLeft + "px; width: " + gutterElt.clientWidth + "px")); - } - } - } - // Kludge to make sure the styled element lies behind the selection (by z-index) - if (line.bgClassName) - wrap.appendChild(elt("div", "\u00a0", line.bgClassName + " CodeMirror-linebackground")); - wrap.appendChild(lineElement); - if (line.widgets) - for (var i = 0, ws = line.widgets; i < ws.length; ++i) { - var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget"); - node.widget = widget; - node.style.position = "relative"; - if (widget.noHScroll) { - (wrap.alignable || (wrap.alignable = [])).push(node); - var width = display.wrapper.clientWidth; - node.style.left = fixedPos + "px"; - if (!widget.coverGutter) { - width -= display.gutters.offsetWidth; - node.style.paddingLeft = display.gutters.offsetWidth + "px"; - } - node.style.width = width + "px"; - } - if (widget.coverGutter) { - node.style.zIndex = 5; - node.style.position = "relative"; - if (!widget.noHScroll) node.style.marginLeft = -display.gutters.offsetWidth + "px"; - } - wrap.appendChild(node); - } - lineElement = wrap; - if (ie_lt8) lineElement.style.zIndex = 2; - } - } - display.lineDiv.insertBefore(lineElement, curNode); - } else { + if (!nextIntact || nextIntact.from > j) + display.lineDiv.insertBefore(buildLineElement(cm, line, j, dims), curNode); + else curNode = curNode.nextSibling; - } ++j; }); } + function buildLineElement(cm, line, lineNo, dims) { + if (line.hidden && (!line.hidden.handle.showWidgets || !line.widgets)) + return elt("div"); + + var lineElement = line.hidden ? elt("div") : lineContent(cm, line); + var markers = line.gutterMarkers, display = cm.display; + + if (!cm.options.lineNumbers && !markers && !line.bgClassName && + (!line.widgets || !line.widgets.length)) return lineElement; + + // Lines with gutter elements or a background class need + // to be wrapped again, and have the extra elements added + // to the wrapper div + + var wrap = elt("div", null, null, "position: relative"); + if (cm.options.lineNumbers || markers) { + var gutterWrap = wrap.appendChild(elt("div", null, null, "position: absolute; left: " + + dims.fixedPos + "px")); + wrap.alignable = [gutterWrap]; + if (cm.options.lineNumbers) + gutterWrap.appendChild(elt("div", lineNumberFor(cm.options, lineNo), + "CodeMirror-linenumber CodeMirror-gutter-elt", + "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: " + + display.lineNumInnerWidth + "px")); + if (markers) + for (var k = 0; k < cm.options.gutters.length; ++k) { + var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id]; + if (found) { + gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " + + dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px")); + } + } + } + // Kludge to make sure the styled element lies behind the selection (by z-index) + if (line.bgClassName) + wrap.appendChild(elt("div", "\u00a0", line.bgClassName + " CodeMirror-linebackground")); + wrap.appendChild(lineElement); + if (line.widgets) + for (var i = 0, ws = line.widgets; i < ws.length; ++i) { + var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget"); + node.widget = widget; + if (widget.noHScroll) { + (wrap.alignable || (wrap.alignable = [])).push(node); + var width = dims.wrapperWidth; + node.style.left = dims.fixedPos + "px"; + if (!widget.coverGutter) { + width -= dims.gutterTotalWidth; + node.style.paddingLeft = dims.gutterTotalWidth + "px"; + } + node.style.width = width + "px"; + } + if (widget.coverGutter) { + node.style.zIndex = 5; + node.style.position = "relative"; + if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px"; + } + if (widget.above) + wrap.insertBefore(node, cm.options.lineNumbers && !line.hidden ? gutterWrap : lineElement); + else + wrap.appendChild(node); + } + + if (ie_lt8) wrap.style.zIndex = 2; + return wrap; + } + // SELECTION / CURSOR function selHead(view) { @@ -576,7 +607,7 @@ window.CodeMirror = (function() { var pos = headPos = cursorCoords(cm, sel.from, "div"); display.cursor.style.left = pos.left + "px"; display.cursor.style.top = pos.top + "px"; - display.cursor.style.height = (pos.bottom - pos.top) * cm.options.cursorHeight + "px"; + display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"; display.cursor.style.display = ""; display.selectionDiv.style.display = "none"; @@ -589,7 +620,7 @@ window.CodeMirror = (function() { } else { headPos = cursorCoords(cm, selHead(cm.view), "div"); var fragment = document.createDocumentFragment(); - var clientWidth = display.lineSpace.clientWidth; + var clientWidth = display.lineSpace.offsetWidth; var add = function(left, top, width, bottom) { if (top < 0) top = 0; fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left + @@ -669,32 +700,38 @@ window.CodeMirror = (function() { // HIGHLIGHT WORKER function startWorker(cm, time) { - if (cm.view.doc.frontier < cm.display.showingTo) - cm.view.doc.highlight.set(time, bind(highlightWorker, cm)); + if (cm.view.frontier < cm.display.showingTo) + cm.view.highlight.set(time, bind(highlightWorker, cm)); } function highlightWorker(cm) { - var doc = cm.view.doc; - if (doc.frontier >= cm.display.showingTo) return; + var view = cm.view, doc = view.doc; + if (view.frontier >= cm.display.showingTo) return; var end = +new Date + cm.options.workTime; - var state = copyState(doc.mode, getStateBefore(doc, doc.frontier)); - var startFrontier = doc.frontier; - doc.iter(doc.frontier, cm.display.showingTo, function(line) { - if (doc.frontier >= cm.display.showingFrom) { // Visible - line.highlight(doc.mode, state, cm.options.tabSize); - line.stateAfter = copyState(doc.mode, state); + var state = copyState(view.mode, getStateBefore(cm, view.frontier)); + var changed = [], prevChange; + doc.iter(view.frontier, Math.min(doc.size, cm.display.showingTo + 500), function(line) { + if (view.frontier >= cm.display.showingFrom) { // Visible + if (highlightLine(cm, line, state) && view.frontier >= cm.display.showingFrom) { + if (prevChange && prevChange.end == view.frontier) prevChange.end++; + else changed.push(prevChange = {start: view.frontier, end: view.frontier + 1}); + } + line.stateAfter = copyState(view.mode, state); } else { - line.process(doc.mode, state, cm.options.tabSize); - line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null; + processLine(cm, line, state); + line.stateAfter = view.frontier % 5 == 0 ? copyState(view.mode, state) : null; } - ++doc.frontier; + ++view.frontier; if (+new Date > end) { startWorker(cm, cm.options.workDelay); return true; } }); - if (cm.display.showingTo > startFrontier && doc.frontier >= cm.display.showingFrom) - operation(cm, function() {regChange(this, startFrontier, doc.frontier);})(); + if (changed.length) + operation(cm, function() { + for (var i = 0; i < changed.length; ++i) + regChange(this, changed[i].start, changed[i].end); + })(); } // Finds the line to start with when starting a parse. Tries to @@ -702,13 +739,13 @@ window.CodeMirror = (function() { // valid state. If that fails, it returns the line with the // smallest indentation, which tends to need the least context to // parse correctly. - function findStartLine(doc, n) { - var minindent, minline; + function findStartLine(cm, n) { + var minindent, minline, doc = cm.view.doc; for (var search = n, lim = n - 100; search > lim; --search) { if (search == 0) return 0; var line = getLine(doc, search-1); if (line.stateAfter) return search; - var indented = line.indentation(doc.tabSize); + var indented = countColumn(line.text, null, cm.options.tabSize); if (minline == null || minindent > indented) { minline = search - 1; minindent = indented; @@ -717,13 +754,14 @@ window.CodeMirror = (function() { return minline; } - function getStateBefore(doc, n) { - var pos = findStartLine(doc, n), state = pos && getLine(doc, pos-1).stateAfter; - if (!state) state = startState(doc.mode); - else state = copyState(doc.mode, state); - doc.iter(pos, n, function(line) { - line.process(doc.mode, state, doc.tabSize); - line.stateAfter = (pos == n - 1 || pos % 5 == 0) ? copyState(doc.mode, state) : null; + function getStateBefore(cm, n) { + var view = cm.view; + var pos = findStartLine(cm, n), state = pos && getLine(view.doc, pos-1).stateAfter; + if (!state) state = startState(view.mode); + else state = copyState(view.mode, state); + view.doc.iter(pos, n, function(line) { + processLine(cm, line, state); + line.stateAfter = (pos == n - 1 || pos % 5 == 0) ? copyState(view.mode, state) : null; }); return state; } @@ -750,12 +788,13 @@ window.CodeMirror = (function() { var atEnd = ch && ch == line.text.length; var pre = lineContent(cm, line, atEnd ? ch - 1 : ch); removeChildrenAndAdd(display.measure, pre); - var anchor = pre.anchor, outer = display.lineDiv.getBoundingClientRect(); + var anchor = pre.anchor; // We'll sample once at the top, once at the bottom of the line, // to get the real line height (in case there tokens on the line // with bigger fonts) anchor.style.verticalAlign = "top"; - var box1 = anchor.getBoundingClientRect(), left = box1.left - outer.left, right = box1.right - outer.left; + var outer = display.lineDiv.getBoundingClientRect(), box1 = anchor.getBoundingClientRect(); + var left = box1.left - outer.left, right = box1.right - outer.left; if (ie) { var left1 = anchor.offsetLeft; // In IE, verticalAlign does not influence offsetTop, unless @@ -787,7 +826,11 @@ window.CodeMirror = (function() { } // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page" - function intoCoordSystem(cm, pos, rect, context) { + function intoCoordSystem(cm, lineObj, pos, rect, context) { + if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) { + var size = lineObj.widgets[i].node.offsetHeight; + rect.top += size; rect.bottom += size; + } if (context == "line") return rect; if (!context) context = "local"; var yOff = heightAtLine(cm.view.doc, pos.line); @@ -804,7 +847,7 @@ window.CodeMirror = (function() { function charCoords(cm, pos, context, lineObj) { if (!lineObj) lineObj = getLine(cm.view.doc, pos.line); - return intoCoordSystem(cm, pos, measureLine(cm, lineObj, pos.ch), context); + return intoCoordSystem(cm, lineObj, pos, measureLine(cm, lineObj, pos.ch), context); } function cursorCoords(cm, pos, context, lineObj) { @@ -812,7 +855,7 @@ window.CodeMirror = (function() { function get(ch, right) { var m = measureLine(cm, lineObj, ch); if (right) m.left = m.right; else m.right = m.left; - return intoCoordSystem(cm, pos, m, context); + return intoCoordSystem(cm, lineObj, pos, m, context); } var order = getOrder(lineObj), ch = pos.ch; if (!order) return get(ch); @@ -847,22 +890,20 @@ window.CodeMirror = (function() { function coordsChar(cm, x, y) { var display = cm.display, doc = cm.view.doc; var cw = charWidth(display), heightPos = display.viewOffset + y; - if (heightPos < 0) return {line: 0, ch: 0}; + if (heightPos < 0) return {line: 0, ch: 0, outside: true}; var lineNo = lineAtHeight(doc, heightPos); if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc, doc.size - 1).text.length}; var lineObj = getLine(doc, lineNo); if (!lineObj.text.length) return {line: lineNo, ch: 0}; - var tw = cm.options.lineWrapping, innerOff = tw ? heightPos - heightAtLine(doc, lineNo) : 0; + var innerOff = heightPos - heightAtLine(doc, lineNo); if (x < 0) x = 0; - var wrongLine = false; + var wrongLine = false, cWidth = display.wrapper.clientWidth; function getX(ch) { var sp = cursorCoords(cm, {line: lineNo, ch: ch}, "line", lineObj); - if (tw) { - wrongLine = true; - if (innerOff > sp.bottom) return Math.max(0, sp.left - display.wrapper.clientWidth); - else if (innerOff < sp.top) return sp.left + display.wrapper.clientWidth; - else wrongLine = false; - } + wrongLine = true; + if (innerOff > sp.bottom) return Math.max(0, sp.left - cWidth); + else if (innerOff < sp.top) return sp.left + cWidth; + else wrongLine = false; return sp.left; } var bidi = getOrder(lineObj), dist = lineObj.text.length; @@ -881,13 +922,13 @@ window.CodeMirror = (function() { if (estX < x) {from = estimated; fromX = estX;} dist = to - from; } else toX = getX(to); - if (x > toX) return {line: lineNo, ch: to}; + if (x > toX) return {line: lineNo, ch: to, outside: wrongLine}; // Do a binary search between these bounds. for (;;) { if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) { var after = x - fromX < toX - x, ch = after ? from : to; while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch; - return {line: lineNo, ch: ch, after: after}; + return {line: lineNo, ch: ch, after: after, outside: wrongLine}; } var step = Math.ceil(dist / 2), middle = from + step; if (bidi) { @@ -1001,12 +1042,6 @@ window.CodeMirror = (function() { cm.curOp.changes.push({from: from, to: to, diff: lendiff}); } - function compoundChange(cm, f) { - var hist = cm.view.doc.history; - hist.startCompound(); - try { return f(); } finally { hist.endCompound(); } - } - // INPUT HANDLING function slowPoll(cm) { @@ -1044,7 +1079,7 @@ window.CodeMirror = (function() { while (same < l && prevInput[same] == text[same]) ++same; if (same < prevInput.length) sel.from = {line: sel.from.line, ch: sel.from.ch - (prevInput.length - same)}; - else if (view.overwrite && posEq(sel.from, sel.to)) + else if (view.overwrite && posEq(sel.from, sel.to) && !cm.display.pasteIncoming) sel.to = {line: sel.to.line, ch: Math.min(getLine(cm.view.doc, sel.to.line).text.length, sel.to.ch + (text.length - same))}; var updateInput = cm.curOp.updateInput; cm.replaceSelection(text.slice(same), "end"); @@ -1052,6 +1087,7 @@ window.CodeMirror = (function() { if (text.length > 1000) { input.value = cm.display.prevInput = ""; } else cm.display.prevInput = text; endOperation(cm); + cm.display.pasteIncoming = false; return true; } @@ -1088,29 +1124,20 @@ window.CodeMirror = (function() { if (!gecko) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);}); on(d.scroller, "scroll", function() { - if (d.scroller.scrollTop != cm.view.scrollTop) { - d.scrollbarV.scrollTop = cm.view.scrollTop = d.scroller.scrollTop; - updateDisplay(cm, []); - } - if (d.scroller.scrollLeft != cm.view.scrollLeft) { - d.scrollbarH.scrollLeft = cm.view.scrollLeft = d.scroller.scrollLeft; - alignVertically(cm.display); - } + setScrollTop(cm, d.scroller.scrollTop); + setScrollLeft(cm, d.scroller.scrollLeft); signal(cm, "scroll", cm); }); on(d.scrollbarV, "scroll", function() { - if (d.scrollbarV.scrollTop != cm.view.scrollTop) { - d.scroller.scrollTop = cm.view.scrollTop = d.scrollbarV.scrollTop; - updateDisplay(cm, []); - } + setScrollTop(cm, d.scrollbarV.scrollTop); }); on(d.scrollbarH, "scroll", function() { - if (d.scrollbarH.scrollLeft != cm.view.scrollLeft) { - d.scroller.scrollLeft = cm.view.scrollLeft = d.scrollbarH.scrollLeft; - alignVertically(cm.display); - } + setScrollLeft(cm, d.scrollbarH.scrollLeft); }); + on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);}); + on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);}); + function reFocus() { if (cm.view.focused) setTimeout(bind(focusInput, cm), 0); } on(d.scrollbarH, "mousedown", reFocus); on(d.scrollbarV, "mousedown", reFocus); @@ -1145,7 +1172,11 @@ window.CodeMirror = (function() { on(d.scroller, "drop", operation(cm, onDrop)); } on(d.scroller, "paste", function(){focusInput(cm); fastPoll(cm);}); - on(d.input, "paste", bind(fastPoll, cm)); + on(d.input, "paste", function() { + d.pasteIncoming = true; + fastPoll(cm); + }); + function prepareCopy() { if (d.inaccurateSelection) { d.prevInput = ""; @@ -1246,7 +1277,7 @@ window.CodeMirror = (function() { }); // Let the drag handler handle this. if (webkit) display.scroller.draggable = true; - view.draggingText = true; + view.draggingText = dragEnd; // IE's approach to draggable if (display.scroller.dragDrop) display.scroller.dragDrop(); on(document, "mouseup", dragEnd); @@ -1344,7 +1375,11 @@ window.CodeMirror = (function() { for (var i = 0; i < n; ++i) loadFile(files[i], i); } else { // Don't do a replace if the drop happened inside of the selected text. - if (cm.view.draggingText && !(posLess(pos, cm.view.sel.from) || posLess(cm.view.sel.to, pos))) return; + if (cm.view.draggingText && !(posLess(pos, cm.view.sel.from) || posLess(cm.view.sel.to, pos))) { + cm.view.draggingText(e); + if (ie) setTimeout(bind(focusInput, cm), 50); + return; + } try { var text = e.dataTransfer.getData("Text"); if (text) { @@ -1354,6 +1389,7 @@ window.CodeMirror = (function() { if (cm.view.draggingText) replaceRange(cm, "", curFrom, curTo); cm.replaceSelection(text); focusInput(cm); + onFocus(cm); }); } } @@ -1395,6 +1431,68 @@ window.CodeMirror = (function() { e.dataTransfer.setDragImage(elt('img'), 0, 0); } + function setScrollTop(cm, val) { + if (cm.view.scrollTop == val) return; + cm.view.scrollTop = val; + if (!gecko) updateDisplay(cm, [], val); + if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val; + if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val; + if (gecko) updateDisplay(cm, []); + } + function setScrollLeft(cm, val) { + if (cm.view.scrollLeft == val) return; + cm.view.scrollLeft = val; + if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val; + if (cm.display.scrollbarH.scrollLeft != val) cm.display.scrollbarH.scrollLeft = val; + alignVertically(cm.display); + } + + // Since the delta values reported on mouse wheel events are + // unstandardized between browsers and even browser versions, and + // generally horribly unpredictable, this code starts by measuring + // the scroll effect that the first few mouse wheel events have, + // and, from that, detects the way it can convert deltas to pixel + // offsets afterwards. + // + // The reason we want to directly handle the wheel event is that it + // gives us a chance to update the display before the actual + // scrolling happens, reducing flickering. + + var wheelSamples = [], wheelDX, wheelDY, wheelStartX, wheelStartY, wheelPixelsPerUnit; + function onScrollWheel(cm, e) { + var dx = e.wheelDeltaX, dy = e.wheelDeltaY; + if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail; + if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail; + else if (dy == null) dy = e.wheelDelta; + + var scroll = cm.display.scroller; + if (wheelPixelsPerUnit != null) { + if (dx) setScrollLeft(cm, Math.max(0, Math.round(scroll.scrollLeft += dx * wheelPixelsPerUnit))); + if (dy) setScrollTop(cm, Math.max(0, Math.round(scroll.scrollTop + dy * wheelPixelsPerUnit))); + e_stop(e); + } else { + if (wheelStartX == null) { + wheelStartX = scroll.scrollLeft; wheelStartY = scroll.scrollTop; + wheelDX = dx; wheelDY = dy; + setTimeout(function() { + var movedX = scroll.scrollLeft - wheelStartX; + var movedY = scroll.scrollTop - wheelStartY; + var sample = (movedY && wheelDY && movedY / wheelDY) || + (movedX && wheelDX && movedX / wheelDX); + wheelStartX = wheelStartY = null; + if (!sample) return; + wheelSamples.push(sample); + if (wheelSamples.length >= 15) { + for (var total = 0, i = 0; i < wheelSamples.length; ++i) total += wheelSamples[i]; + wheelPixelsPerUnit = total / wheelSamples.length; + } + }, 200); + } else { + wheelDX += dx; wheelDY += dy; + } + } + } + function doHandleBinding(cm, bound, dropShift) { if (typeof bound == "string") { bound = commands[bound]; @@ -1492,9 +1590,9 @@ window.CodeMirror = (function() { if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;} if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return; var ch = String.fromCharCode(charCode == null ? keyCode : charCode); - if (this.options.electricChars && this.view.doc.mode.electricChars && + if (this.options.electricChars && this.view.mode.electricChars && this.options.smartIndent && !this.options.readOnly && - this.view.doc.mode.electricChars.indexOf(ch) > -1) + this.view.mode.electricChars.indexOf(ch) > -1) setTimeout(operation(cm, function() {indentLine(cm, cm.view.sel.to.line, "smart");}), 75); if (handleCharBinding(cm, e, ch)) return; fastPoll(cm); @@ -1582,9 +1680,9 @@ window.CodeMirror = (function() { doc.iter(from.line, to.line + 1, function(line) { old.push(newHL(line.text, line.markedSpans)); }); - if (doc.history) { - doc.history.addChange(from.line, newText.length, old); - while (doc.history.done.length > cm.options.undoDepth) doc.history.done.shift(); + if (view.history) { + addChange(view.history, from.line, newText.length, old); + while (view.history.done.length > cm.options.undoDepth) view.history.done.shift(); } var lines = updateMarkedSpans(hlSpans(old[0]), hlSpans(lst(old)), from.ch, to.ch, newText); updateDocNoUndo(cm, from, to, lines, selFrom, selTo); @@ -1606,11 +1704,11 @@ window.CodeMirror = (function() { to.push(out); } function undo(cm) { - var hist = cm.view.doc.history; + var hist = cm.view.history; unredoHelper(cm, hist.done, hist.undone); } function redo(cm) { - var hist = cm.view.doc.history; + var hist = cm.view.history; unredoHelper(cm, hist.undone, hist.done); } @@ -1632,31 +1730,31 @@ window.CodeMirror = (function() { // sure line objects move the way they are supposed to. var added = [], prevLine = null; for (var i = 0, e = lines.length - 1; i < e; ++i) - added.push(new Line(hlText(lines[i]), hlSpans(lines[i]), th)); - lastLine.update(lastLine.text, hlSpans(lastHL), cm); + added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th)); + updateLine(cm, lastLine, lastLine.text, hlSpans(lastHL)); if (nlines) doc.remove(from.line, nlines, cm); if (added.length) doc.insert(from.line, added); } else if (firstLine == lastLine) { if (lines.length == 1) { - firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]) + firstLine.text.slice(to.ch), - hlSpans(lines[0]), cm); + updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]) + + firstLine.text.slice(to.ch), hlSpans(lines[0])); } else { for (var added = [], i = 1, e = lines.length - 1; i < e; ++i) - added.push(new Line(hlText(lines[i]), hlSpans(lines[i]), th)); - added.push(new Line(hlText(lastHL) + firstLine.text.slice(to.ch), hlSpans(lastHL), th)); - firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]), cm); + added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th)); + added.push(makeLine(hlText(lastHL) + firstLine.text.slice(to.ch), hlSpans(lastHL), th)); + updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0])); doc.insert(from.line + 1, added); } } else if (lines.length == 1) { - firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]) + lastLine.text.slice(to.ch), - hlSpans(lines[0]), cm); + updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]) + + lastLine.text.slice(to.ch), hlSpans(lines[0])); doc.remove(from.line + 1, nlines, cm); } else { var added = []; - firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]), cm); - lastLine.update(hlText(lastHL) + lastLine.text.slice(to.ch), hlSpans(lastHL), cm); + updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0])); + updateLine(cm, lastLine, hlText(lastHL) + lastLine.text.slice(to.ch), hlSpans(lastHL)); for (var i = 1, e = lines.length - 1; i < e; ++i) - added.push(new Line(hlText(lines[i]), hlSpans(lines[i]), th)); + added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th)); if (nlines > 1) doc.remove(from.line + 1, nlines - 1, cm); doc.insert(from.line + 1, added); } @@ -1680,7 +1778,7 @@ window.CodeMirror = (function() { } // Adjust frontier, schedule worker - doc.frontier = Math.min(doc.frontier, from.line); + view.frontier = Math.min(view.frontier, from.line); startWorker(cm, 400); var lendiff = lines.length - nlines - 1; @@ -1878,18 +1976,19 @@ window.CodeMirror = (function() { var doc = cm.view.doc; if (!how) how = "add"; if (how == "smart") { - if (!doc.mode.indent) how = "prev"; - else var state = getStateBefore(doc, n); + if (!cm.view.mode.indent) how = "prev"; + else var state = getStateBefore(cm, n); } - var line = getLine(doc, n), curSpace = line.indentation(doc.tabSize), - curSpaceString = line.text.match(/^\s*/)[0], indentation; + var tabSize = cm.options.tabSize; + var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize); + var curSpaceString = line.text.match(/^\s*/)[0], indentation; if (how == "smart") { - indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text); + indentation = cm.view.mode.indent(state, line.text.slice(curSpaceString.length), line.text); if (indentation == Pass) how = "prev"; } if (how == "prev") { - if (n) indentation = getLine(doc, n-1).indentation(doc.tabSize); + if (n) indentation = countColumn(getLine(doc, n-1).text, null, tabSize); else indentation = 0; } else if (how == "add") indentation = curSpace + cm.options.indentUnit; @@ -1899,11 +1998,12 @@ window.CodeMirror = (function() { var indentString = "", pos = 0; if (cm.options.indentWithTabs) - for (var i = Math.floor(indentation / doc.tabSize); i; --i) {pos += doc.tabSize; indentString += "\t";} + for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} if (pos < indentation) indentString += spaceStr(indentation - pos); if (indentString != curSpaceString) replaceRange(cm, indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length}); + line.stateAfter = null; } function changeLine(cm, handle, op) { @@ -1922,7 +2022,7 @@ window.CodeMirror = (function() { function findNextLine() { for (var l = line + dir, e = dir < 0 ? -1 : doc.size; l != e; l += dir) { var lo = getLine(doc, l); - if (!lo.hidden || !lo.hidden.handle.unfoldOnEneter) { line = l; lineObj = lo; return true; } + if (!lo.hidden || lo.hidden.handle.unfoldOnEnter) { line = l; lineObj = lo; return true; } } } function moveOnce(boundToLine) { @@ -2006,9 +2106,11 @@ window.CodeMirror = (function() { else if (option == "readOnly" && !value) {resetInput(this, true);} else if (option == "theme") themeChanged(this); else if (option == "lineWrapping") operation(this, wrappingChanged)(this); - else if (option == "tabSize") {this.view.doc.tabSize = value; updateDisplay(this, true);} + else if (option == "tabSize") updateDisplay(this, true); else if (option == "keyMap") keyMapChanged(this); else if (option == "gutters" || option == "lineNumbers") setGuttersForLineNumbers(this.options); + else if (option == "tabindex") this.display.input.tabIndex = value; + else if (option == "viewportMargin") this.refresh(); if (option == "lineNumbers" || option == "gutters" || option == "firstLineNumber" || option == "theme" || option == "lineNumberFormatter") guttersChanged(this); @@ -2018,14 +2120,14 @@ window.CodeMirror = (function() { getOption: function(option) {return this.options[option];}, - getMode: function() {return this.view.doc.mode;}, + getMode: function() {return this.view.mode;}, undo: operation(null, function() { - var hist = this.view.doc.history; + var hist = this.view.history; unredoHelper(this, hist.done, hist.undone); }), redo: operation(null, function() { - var hist = this.view.doc.history; + var hist = this.view.history; unredoHelper(this, hist.undone, hist.done); }), @@ -2045,14 +2147,14 @@ window.CodeMirror = (function() { }), historySize: function() { - var hist = this.view.doc.history; + var hist = this.view.history; return {undo: hist.done.length, redo: hist.undone.length}; }, - clearHistory: function() {this.view.doc.history = new History();}, + clearHistory: function() {this.view.history = makeHistory();}, getHistory: function() { - var hist = this.view.doc.history; + var hist = this.view.history; function cp(arr) { for (var i = 0, nw = [], nwelt; i < arr.length; ++i) { nw.push(nwelt = []); @@ -2068,7 +2170,7 @@ window.CodeMirror = (function() { }, setHistory: function(histData) { - var hist = this.view.doc.history = new History(); + var hist = this.view.history = makeHistory(); hist.done = histData.done; hist.undone = histData.undone; }, @@ -2076,14 +2178,13 @@ window.CodeMirror = (function() { getTokenAt: function(pos) { var doc = this.view.doc; pos = clipPos(doc, pos); - return getLine(doc, pos.line).getTokenAt(doc.mode, getStateBefore(doc, pos.line), - this.options.tabSize, pos.ch); + return getTokenAt(this, getLine(doc, pos.line), getStateBefore(this, pos.line), pos.ch); }, getStateAfter: function(line) { var doc = this.view.doc; line = clipLine(doc, line == null ? doc.size - 1: line); - return getStateBefore(doc, line + 1); + return getStateBefore(this, line + 1); }, cursorCoords: function(start, mode) { @@ -2114,7 +2215,7 @@ window.CodeMirror = (function() { var span = {from: curLine == from.line ? from.ch : null, to: curLine == to.line ? to.ch : null, marker: marker}; - (line.markedSpans || (line.markedSpans = [])).push(span); + line.markedSpans = (line.markedSpans || []).concat([span]); marker.lines.push(line); ++curLine; }); @@ -2127,7 +2228,7 @@ window.CodeMirror = (function() { pos = clipPos(doc, pos); var marker = new TextMarker(this, "bookmark"), line = getLine(doc, pos.line); var span = {from: pos.ch, to: pos.ch, marker: marker}; - (line.markedSpans || (line.markedSpans = [])).push(span); + line.markedSpans = (line.markedSpans || []).concat([span]); marker.lines.push(line); return marker; }, @@ -2195,11 +2296,12 @@ window.CodeMirror = (function() { regChange(this, no, no + 1); }), - foldLines: operation(null, function(from, to, unfoldOnEnter) { + foldLines: operation(null, function(from, to, options) { if (typeof from != "number") from = lineNo(from); if (typeof to != "number") to = lineNo(to); if (from > to) return; - var lines = [], handle = {lines: lines, unfoldOnEnter: unfoldOnEnter}, cm = this, view = cm.view, doc = view.doc; + var handle = options || {}, lines = handle.lines = []; + var cm = this, view = cm.view, doc = view.doc; doc.iter(from, to, function(line) { lines.push(line); if (!line.hidden && line.text.length == cm.view.maxLine.text.length) @@ -2373,14 +2475,11 @@ window.CodeMirror = (function() { } else if (unit == "line") { y = dir > 0 ? pos.bottom + 3 : pos.top - 3; } - var target = coordsChar(this, x, y), line; - // Work around problem with moving 'through' line widgets - if (dir > 0 && target.line == cur.line && cur.line < doc.size - 1 && getLine(doc, cur.line).widgets && - Math.abs(cursorCoords(this, target, "div").top - pos.top) < 2) - target = coordsChar(this, x, cursorCoords(this, {line: cur.line + 1, ch: 0}, "div").top + 3); - else if (dir < 0 && cur.line > 0 && (line = getLine(doc, target.line)).widgets && target.ch == line.text.length) - target = coordsChar(this, x, cursorCoords(this, {line: target.line, ch: line.text.length}, "div").bottom - 3); - + do { + var target = coordsChar(this, x, y); + y += dir * 5; + } while (target.outside && (dir < 0 ? y > 0 : y < doc.height)); + if (unit == "page") display.scrollbarV.scrollTop += charCoords(this, target, "div").top - pos.top; setSelectionUser(this, target, target); view.goalColumn = x; @@ -2480,8 +2579,10 @@ window.CodeMirror = (function() { cursorHeight: 1, workTime: 100, workDelay: 200, + flattenSpans: true, pollInterval: 100, undoDepth: 40, + viewportMargin: 10, tabindex: null, autofocus: null, lineNumberFormatter: function(integer) { return integer; } @@ -2550,6 +2651,9 @@ window.CodeMirror = (function() { optionHandlers[name] = handler; }; + var initHooks = []; + CodeMirror.defineInitHook = function(f) {initHooks.push(f);}; + // MODE STATE HANDLING // Utility functions for working with state. Exported because modes @@ -2719,6 +2823,7 @@ window.CodeMirror = (function() { var name = keyNames[e_prop(event, "keyCode")]; return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"; } + CodeMirror.isModifierKey = isModifierKey; // FROMTEXTAREA @@ -2844,17 +2949,17 @@ window.CodeMirror = (function() { TextMarker.prototype.clear = function() { startOperation(this.cm); - var min = Infinity, max = -Infinity; + var min = null, max = null; for (var i = 0; i < this.lines.length; ++i) { var line = this.lines[i]; - var span = getMarkedSpanFor(line.markedSpans, this, true); - if (span.from != null || span.to != null) { - var lineN = lineNo(line); - min = Math.min(min, lineN); max = Math.max(max, lineN); - } + var span = getMarkedSpanFor(line.markedSpans, this); + if (span.from != null) min = lineNo(line); + if (span.to != null) max = lineNo(line); + line.markedSpans = removeMarkedSpan(line.markedSpans, span); } - if (min != Infinity) regChange(this.cm, min, max + 1); + if (min != null) regChange(this.cm, min, max + 1); this.lines.length = 0; + this.explicitlyCleared = true; endOperation(this.cm); }; @@ -2875,15 +2980,17 @@ window.CodeMirror = (function() { // TEXTMARKER SPANS - function getMarkedSpanFor(spans, marker, del) { + function getMarkedSpanFor(spans, marker) { if (spans) for (var i = 0; i < spans.length; ++i) { var span = spans[i]; - if (span.marker == marker) { - if (del) spans.splice(i, 1); - return span; - } + if (span.marker == marker) return span; } } + function removeMarkedSpan(spans, span) { + for (var r, i = 0; i < spans.length; ++i) + if (spans[i] != span) (r || (r = [])).push(spans[i]); + return r; + } function markedSpansBefore(old, startCh, endCh) { if (old) for (var i = 0, nw; i < old.length; ++i) { @@ -2968,7 +3075,15 @@ window.CodeMirror = (function() { // hl stands for history-line, a data structure that can be either a // string (line without markers) or a {text, markedSpans} object. function hlText(val) { return typeof val == "string" ? val : val.text; } - function hlSpans(val) { return typeof val == "string" ? null : val.markedSpans; } + function hlSpans(val) { + if (typeof val == "string") return null; + var spans = val.markedSpans, out = null; + for (var i = 0; i < spans.length; ++i) { + if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); } + else if (out) out.push(spans[i]); + } + return !out ? spans : out.length ? out : null; + } function newHL(text, spans) { return spans ? {text: text, markedSpans: spans} : text; } function detachMarkedSpans(line) { @@ -2993,208 +3108,218 @@ window.CodeMirror = (function() { // Line objects. These hold state related to a line, including // highlighting info (the styles array). - function Line(text, markedSpans, height) { - this.text = text; - this.height = height; - attachMarkedSpans(this, markedSpans); + function makeLine(text, markedSpans, height) { + var line = {text: text, height: height}; + attachMarkedSpans(line, markedSpans); + return line; } - Line.prototype = { - update: function(text, markedSpans, cm) { - this.text = text; - this.stateAfter = this.styles = null; - detachMarkedSpans(this); - attachMarkedSpans(this, markedSpans); - signalLater(cm, this, "change"); - }, + function updateLine(cm, line, text, markedSpans) { + line.text = text; + line.stateAfter = line.styles = null; + if (line.order != null) line.order = null; + detachMarkedSpans(line); + attachMarkedSpans(line, markedSpans); + signalLater(cm, line, "change"); + } - // Run the given mode's parser over a line, update the styles - // array, which contains alternating fragments of text and CSS - // classes. - highlight: function(mode, state, tabSize) { - var stream = new StringStream(this.text, tabSize), st = this.styles || (this.styles = []); - var pos = st.length = 0; - if (this.text == "" && mode.blankLine) mode.blankLine(state); - while (!stream.eol()) { - var style = mode.token(stream, state), substr = stream.current(); - stream.start = stream.pos; - if (pos && st[pos-1] == style) { - st[pos-2] += substr; - } else if (substr) { - st[pos++] = substr; st[pos++] = style; + function cleanUpLine(line) { + line.parent = null; + detachMarkedSpans(line); + } + + // Run the given mode's parser over a line, update the styles + // array, which contains alternating fragments of text and CSS + // classes. + function highlightLine(cm, line, state) { + var mode = cm.view.mode, flattenSpans = cm.options.flattenSpans; + var changed = !line.styles, pos = 0, curText = "", curStyle = null; + var stream = new StringStream(line.text, cm.options.tabSize), st = line.styles || (line.styles = []); + if (line.text == "" && mode.blankLine) mode.blankLine(state); + while (!stream.eol()) { + var style = mode.token(stream, state), substr = stream.current(); + stream.start = stream.pos; + if (!flattenSpans || curStyle != style) { + if (curText) { + changed = changed || pos >= st.length || curText != st[pos] || curStyle != st[pos+1]; + st[pos++] = curText; st[pos++] = curStyle; } - // Give up when line is ridiculously long - if (stream.pos > 5000) { - st[pos++] = this.text.slice(stream.pos); st[pos++] = null; - break; - } - } - }, - - // Lightweight form of highlight -- proceed over this line and - // update state, but don't save a style array. - process: function(mode, state, tabSize) { - var stream = new StringStream(this.text, tabSize); - if (this.text == "" && mode.blankLine) mode.blankLine(state); - while (!stream.eol() && stream.pos <= 5000) { - mode.token(stream, state); - stream.start = stream.pos; - } - }, - - // Fetch the parser token for a given character. Useful for hacks - // that want to inspect the mode state (say, for completion). - getTokenAt: function(mode, state, tabSize, ch) { - var txt = this.text, stream = new StringStream(txt, tabSize); - while (stream.pos < ch && !stream.eol()) { - stream.start = stream.pos; - var style = mode.token(stream, state); - } - return {start: stream.start, - end: stream.pos, - string: stream.current(), - className: style || null, - state: state}; - }, - - indentation: function(tabSize) {return countColumn(this.text, null, tabSize);}, - - // Produces an HTML fragment for the line, taking selection, - // marking, and highlighting into account. - getContent: function(tabSize, wrapAt, compensateForWrapping) { - var first = true, col = 0, specials = /[\t\u0000-\u0019\u200b\u2028\u2029\uFEFF]/g; - var pre = elt("pre"); - function span_(text, style) { - if (!text) return; - // Work around a bug where, in some compat modes, IE ignores leading spaces - if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1); - first = false; - if (!specials.test(text)) { - col += text.length; - var content = document.createTextNode(text); - } else { - var content = document.createDocumentFragment(), pos = 0; - while (true) { - specials.lastIndex = pos; - var m = specials.exec(text); - var skipped = m ? m.index - pos : text.length - pos; - if (skipped) { - content.appendChild(document.createTextNode(text.slice(pos, pos + skipped))); - col += skipped; - } - if (!m) break; - pos += skipped + 1; - if (m[0] == "\t") { - var tabWidth = tabSize - col % tabSize; - content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")); - col += tabWidth; - } else { - var token = elt("span", "\u2022", "cm-invalidchar"); - token.title = "\\u" + m[0].charCodeAt(0).toString(16); - content.appendChild(token); - col += 1; - } - } - } - if (style != null) return pre.appendChild(elt("span", [content], style)); - else return pre.appendChild(content); - } - var span = span_; - if (wrapAt != null) { - var outPos = 0; - span = function(text, style) { - var l = text.length; - if (wrapAt >= outPos && wrapAt < outPos + l) { - var cut = wrapAt - outPos; - if (wrapAt > outPos) { - span_(text.slice(0, cut), style); - // See comment at the definition of spanAffectsWrapping - if (compensateForWrapping && spanAffectsWrapping.test(text.slice(cut - 1, cut + 1))) - pre.appendChild(elt("wbr")); - } - if (cut + 1 == l) { - pre.anchor = span_(text.slice(cut), style || ""); - wrapAt--; - } else { - var end = cut + 1; - while (isExtendingChar.test(text.charAt(end))) ++end; - pre.anchor = span_(text.slice(cut, end), style || ""); - if (compensateForWrapping && spanAffectsWrapping.test(text.slice(cut, end + 1))) - pre.appendChild(elt("wbr")); - span_(text.slice(end), style); - } - outPos += l; - } else { - outPos += l; - span_(text, style); - } - }; - } - - var st = this.styles, allText = this.text, marked = this.markedSpans; - var len = allText.length; - function styleToClass(style) { - if (!style) return null; - return "cm-" + style.replace(/ +/g, " cm-"); - } - if (!allText) { - span("\u00a0"); - } else if (!marked || !marked.length) { - for (var i = 0, ch = 0; ch < len; i+=2) { - var str = st[i], style = st[i+1], l = str.length; - if (ch + l > len) str = str.slice(0, len - ch); - ch += l; - span(str, styleToClass(style)); - } - } else { - marked.sort(function(a, b) { return a.from - b.from; }); - var pos = 0, i = 0, text = "", style, sg = 0; - var nextChange = marked[0].from || 0, marks = [], markpos = 0; - var advanceMarks = function() { - var m; - while (markpos < marked.length && - ((m = marked[markpos]).from == pos || m.from == null)) { - if (m.marker.type == "range") marks.push(m); - ++markpos; - } - nextChange = markpos < marked.length ? marked[markpos].from : Infinity; - for (var i = 0; i < marks.length; ++i) { - var to = marks[i].to; - if (to == null) to = Infinity; - if (to == pos) marks.splice(i--, 1); - else nextChange = Math.min(to, nextChange); - } - }; - var m = 0; - while (pos < len) { - if (nextChange == pos) advanceMarks(); - var upto = Math.min(len, nextChange); - while (true) { - if (text) { - var end = pos + text.length; - var appliedStyle = style; - for (var j = 0; j < marks.length; ++j) { - var mark = marks[j]; - appliedStyle = (appliedStyle ? appliedStyle + " " : "") + mark.marker.style; - if (mark.marker.endStyle && mark.to === Math.min(end, upto)) appliedStyle += " " + mark.marker.endStyle; - if (mark.marker.startStyle && mark.from === pos) appliedStyle += " " + mark.marker.startStyle; - } - span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle); - if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;} - pos = end; - } - text = st[i++]; style = styleToClass(st[i++]); - } - } - } - return pre; - }, - - cleanUp: function() { - this.parent = null; - detachMarkedSpans(this); + curText = substr; curStyle = style; + } else curText = curText + substr; + // Give up when line is ridiculously long + if (stream.pos > 5000) break; } - }; + if (curText) { + changed = changed || pos >= st.length || curText != st[pos] || curStyle != st[pos+1]; + st[pos++] = curText; st[pos++] = curStyle; + } + if (stream.pos > 5000) st[pos++] = line.text.slice(stream.pos); st[pos++] = null; + if (pos != st.length) { st.length = pos; changed = true; } + return changed; + } + + // Lightweight form of highlight -- proceed over this line and + // update state, but don't save a style array. + function processLine(cm, line, state) { + var mode = cm.view.mode; + var stream = new StringStream(line.text, cm.options.tabSize); + if (line.text == "" && mode.blankLine) mode.blankLine(state); + while (!stream.eol() && stream.pos <= 5000) { + mode.token(stream, state); + stream.start = stream.pos; + } + } + + // Fetch the parser token for a given character. Useful for hacks + // that want to inspect the mode state (say, for completion). + function getTokenAt(cm, line, state, ch) { + var mode = cm.view.mode; + var txt = line.text, stream = new StringStream(txt, cm.options.tabSize); + while (stream.pos < ch && !stream.eol()) { + stream.start = stream.pos; + var style = mode.token(stream, state); + } + return {start: stream.start, + end: stream.pos, + string: stream.current(), + className: style || null, + state: state}; + } + + var styleToClassCache = {}; + function styleToClass(style) { + if (!style) return null; + return styleToClassCache[style] || + (styleToClassCache[style] = "cm-" + style.replace(/ +/g, " cm-")); + } + + // Produces an HTML fragment for the line, taking selection, + // marking, and highlighting into account. + function buildLineContent(line, tabSize, wrapAt, compensateForWrapping) { + var first = true, col = 0, specials = /[\t\u0000-\u0019\u200b\u2028\u2029\uFEFF]/g; + var pre = elt("pre"); + if (line.className) pre.className = line.className; + function span_(text, style) { + if (!text) return; + // Work around a bug where, in some compat modes, IE ignores leading spaces + if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1); + first = false; + if (!specials.test(text)) { + col += text.length; + var content = document.createTextNode(text); + } else { + var content = document.createDocumentFragment(), pos = 0; + while (true) { + specials.lastIndex = pos; + var m = specials.exec(text); + var skipped = m ? m.index - pos : text.length - pos; + if (skipped) { + content.appendChild(document.createTextNode(text.slice(pos, pos + skipped))); + col += skipped; + } + if (!m) break; + pos += skipped + 1; + if (m[0] == "\t") { + var tabWidth = tabSize - col % tabSize; + content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")); + col += tabWidth; + } else { + var token = elt("span", "\u2022", "cm-invalidchar"); + token.title = "\\u" + m[0].charCodeAt(0).toString(16); + content.appendChild(token); + col += 1; + } + } + } + if (style != null) return pre.appendChild(elt("span", [content], style)); + else return pre.appendChild(content); + } + var span = span_; + if (wrapAt != null) { + var outPos = 0; + span = function(text, style) { + var l = text.length; + if (wrapAt >= outPos && wrapAt < outPos + l) { + var cut = wrapAt - outPos; + if (wrapAt > outPos) { + span_(text.slice(0, cut), style); + // See comment at the definition of spanAffectsWrapping + if (compensateForWrapping && spanAffectsWrapping.test(text.slice(cut - 1, cut + 1))) + pre.appendChild(elt("wbr")); + } + if (cut + 1 == l) { + pre.anchor = span_(text.slice(cut), style || ""); + wrapAt--; + } else { + var end = cut + 1; + while (isExtendingChar.test(text.charAt(end))) ++end; + pre.anchor = span_(text.slice(cut, end), style || ""); + if (compensateForWrapping && spanAffectsWrapping.test(text.slice(cut, end + 1))) + pre.appendChild(elt("wbr")); + span_(text.slice(end), style); + } + outPos += l; + } else { + outPos += l; + span_(text, style); + } + }; + } + + var st = line.styles, allText = line.text, marked = line.markedSpans; + var len = allText.length; + if (!allText) { + span("\u00a0"); + } else if (!marked || !marked.length) { + for (var i = 0, ch = 0; ch < len; i+=2) { + var str = st[i], style = st[i+1], l = str.length; + if (ch + l > len) str = str.slice(0, len - ch); + ch += l; + span(str, styleToClass(style)); + } + } else { + marked.sort(function(a, b) { return a.from - b.from; }); + var pos = 0, i = 0, text = "", style, sg = 0; + var nextChange = marked[0].from || 0, marks = [], markpos = 0; + var advanceMarks = function() { + var m; + while (markpos < marked.length && + ((m = marked[markpos]).from == pos || m.from == null)) { + if (m.marker.type == "range") marks.push(m); + ++markpos; + } + nextChange = markpos < marked.length ? marked[markpos].from : Infinity; + for (var i = 0; i < marks.length; ++i) { + var to = marks[i].to; + if (to == null) to = Infinity; + if (to == pos) marks.splice(i--, 1); + else nextChange = Math.min(to, nextChange); + } + }; + var m = 0; + while (pos < len) { + if (nextChange == pos) advanceMarks(); + var upto = Math.min(len, nextChange); + while (true) { + if (text) { + var end = pos + text.length; + var appliedStyle = style; + for (var j = 0; j < marks.length; ++j) { + var mark = marks[j]; + appliedStyle = (appliedStyle ? appliedStyle + " " : "") + mark.marker.style; + if (mark.marker.endStyle && mark.to === Math.min(end, upto)) appliedStyle += " " + mark.marker.endStyle; + if (mark.marker.startStyle && mark.from === pos) appliedStyle += " " + mark.marker.startStyle; + } + span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle); + if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;} + pos = end; + } + text = st[i++]; style = styleToClass(st[i++]); + } + } + } + return pre; + } // DOCUMENT DATA STRUCTURE @@ -3214,7 +3339,7 @@ window.CodeMirror = (function() { for (var i = at, e = at + n; i < e; ++i) { var line = this.lines[i]; this.height -= line.height; - line.cleanUp(); + cleanUpLine(line); signalLater(cm, line, "delete"); } this.lines.splice(at, n); @@ -3410,51 +3535,48 @@ window.CodeMirror = (function() { function lineContent(cm, line, anchorAt) { if (!line.styles) { - var doc = lineDoc(line); - line.highlight(doc.mode, line.stateAfter = getStateBefore(doc, lineNo(line)), cm.options.tabSize); + highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line))); } - return line.getContent(cm.options.tabSize, anchorAt, cm.options.lineWrapping); + return buildLineContent(line, cm.options.tabSize, anchorAt, cm.options.lineWrapping); } // HISTORY // The history object 'chunks' changes that are made close together // and at almost the same time into bigger undoable units. - function History() { - this.time = 0; - this.done = []; this.undone = []; - this.compound = 0; - this.closed = false; + function makeHistory() { + return {time: 0, done: [], undone: [], + compound: 0, closed: false}; } - History.prototype = { - addChange: function(start, added, old) { - this.undone.length = 0; - var time = +new Date, cur = lst(this.done), last = cur && lst(cur); - var dtime = time - this.time; - if (this.compound && cur && !this.closed) { - cur.push({start: start, added: added, old: old}); - } else if (dtime > 400 || !last || this.closed || - last.start > start + old.length || last.start + last.added < start) { - this.done.push([{start: start, added: added, old: old}]); - this.closed = false; - } else { - var startBefore = Math.max(0, last.start - start), - endAfter = Math.max(0, (start + old.length) - (last.start + last.added)); - for (var i = startBefore; i > 0; --i) last.old.unshift(old[i - 1]); - for (var i = endAfter; i > 0; --i) last.old.push(old[old.length - i]); - if (startBefore) last.start = start; - last.added += added - (old.length - startBefore - endAfter); - } - this.time = time; - }, - startCompound: function() { - if (!this.compound++) this.closed = true; - }, - endCompound: function() { - if (!--this.compound) this.closed = true; + function addChange(history, start, added, old) { + history.undone.length = 0; + var time = +new Date, cur = lst(history.done), last = cur && lst(cur); + var dtime = time - history.time; + + if (cur && !history.closed && history.compound) { + cur.push({start: start, added: added, old: old}); + } else if (dtime > 400 || !last || history.closed || + last.start > start + old.length || last.start + last.added < start) { + history.done.push([{start: start, added: added, old: old}]); + history.closed = false; + } else { + var startBefore = Math.max(0, last.start - start), + endAfter = Math.max(0, (start + old.length) - (last.start + last.added)); + for (var i = startBefore; i > 0; --i) last.old.unshift(old[i - 1]); + for (var i = endAfter; i > 0; --i) last.old.push(old[old.length - i]); + if (startBefore) last.start = start; + last.added += added - (old.length - startBefore - endAfter); } - }; + history.time = time; + } + + function compoundChange(cm, f) { + var hist = cm.view.history; + if (!hist.compound++) hist.closed = true; + try { return f(); } + finally { if (!--hist.compound) hist.closed = true; } + } // EVENT OPERATORS @@ -3996,7 +4118,7 @@ window.CodeMirror = (function() { // THE END - CodeMirror.version = "3.0 B"; + CodeMirror.version = "3.0 B2"; return CodeMirror; })(); diff --git a/lib/client/editor/codemirror3/mode/javascript.js b/lib/client/editor/codemirror3/mode/javascript.js index e754a047..37f6f873 100644 --- a/lib/client/editor/codemirror3/mode/javascript.js +++ b/lib/client/editor/codemirror3/mode/javascript.js @@ -1,6 +1,9 @@ +// TODO actually recognize syntax of TypeScript constructs + CodeMirror.defineMode("javascript", function(config, parserConfig) { var indentUnit = config.indentUnit; var jsonMode = parserConfig.json; + var isTS = parserConfig.typescript; // Tokenizer @@ -8,7 +11,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { function kw(type) {return {type: type, style: "keyword"};} var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"); var operator = kw("operator"), atom = {type: "atom", style: "atom"}; - return { + + var jsKeywords = { "if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C, "var": kw("var"), "const": kw("var"), "let": kw("var"), @@ -17,6 +21,35 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { "in": operator, "typeof": operator, "instanceof": operator, "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom }; + + // Extend the 'normal' keywords with the TypeScript language extensions + if (isTS) { + var type = {type: "variable", style: "variable-3"}; + var tsKeywords = { + // object-like things + "interface": kw("interface"), + "class": kw("class"), + "extends": kw("extends"), + "constructor": kw("constructor"), + + // scope modifiers + "public": kw("public"), + "private": kw("private"), + "protected": kw("protected"), + "static": kw("static"), + + "super": kw("super"), + + // types + "string": type, "number": type, "bool": type, "any": type + }; + + for (var attr in tsKeywords) { + jsKeywords[attr] = tsKeywords[attr]; + } + } + + return jsKeywords; }(); var isOperatorChar = /[+\-*&%=<>!?|]/; @@ -66,7 +99,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { stream.skipToEnd(); return ret("comment", "comment"); } - else if (state.reAllowed) { + else if (state.lastType == "operator" || state.lastType == "keyword c" || + /^[\[{}\(,;:]$/.test(state.lastType)) { nextUntilUnescaped(stream, "/"); stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla return ret("regexp", "string-2"); @@ -87,7 +121,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { else { stream.eatWhile(/[\w\$_]/); var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word]; - return (known && state.kwAllowed) ? ret(known.type, known.style, word) : + return (known && state.lastType != ".") ? ret(known.type, known.style, word) : ret("variable", "variable", word); } } @@ -275,19 +309,30 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "}") return cont(); return pass(statement, block); } + function maybetype(type) { + if (type == ":") return cont(typedef); + return pass(); + } + function typedef(type) { + if (type == "variable"){cx.marked = "variable-3"; return cont();} + return pass(); + } function vardef1(type, value) { - if (type == "variable"){register(value); return cont(vardef2);} - return cont(); + if (type == "variable") { + register(value); + return isTS ? cont(maybetype, vardef2) : cont(vardef2); + } + return pass(); } function vardef2(type, value) { if (value == "=") return cont(expression, vardef2); if (type == ",") return cont(vardef1); } function forspec1(type) { - if (type == "var") return cont(vardef1, forspec2); - if (type == ";") return pass(forspec2); + if (type == "var") return cont(vardef1, expect(";"), forspec2); + if (type == ";") return cont(forspec2); if (type == "variable") return cont(formaybein); - return pass(forspec2); + return cont(forspec2); } function formaybein(type, value) { if (value == "in") return cont(expression); @@ -306,7 +351,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext); } function funarg(type, value) { - if (type == "variable") {register(value); return cont();} + if (type == "variable") {register(value); return isTS ? cont(maybetype) : cont();} } // Interface @@ -315,8 +360,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { startState: function(basecolumn) { return { tokenize: jsTokenBase, - reAllowed: true, - kwAllowed: true, + lastType: null, cc: [], lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false), localVars: parserConfig.localVars, @@ -334,19 +378,21 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (stream.eatSpace()) return null; var style = state.tokenize(stream, state); if (type == "comment") return style; - state.reAllowed = !!(type == "operator" || type == "keyword c" || type.match(/^[\[{}\(,;:]$/)); - state.kwAllowed = type != '.'; + state.lastType = type; return parseJS(state, style, type, content, stream); }, indent: function(state, textAfter) { + if (state.tokenize == jsTokenComment) return CodeMirror.Pass; if (state.tokenize != jsTokenBase) return 0; var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical; if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev; var type = lexical.type, closing = firstChar == type; - if (type == "vardef") return lexical.indented + 4; + if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? 4 : 0); else if (type == "form" && firstChar == "{") return lexical.indented; - else if (type == "stat" || type == "form") return lexical.indented + indentUnit; + else if (type == "form") return lexical.indented + indentUnit; + else if (type == "stat") + return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? indentUnit : 0); else if (lexical.info == "switch" && !closing) return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit); else if (lexical.align) return lexical.column + (closing ? 0 : 1); @@ -359,3 +405,5 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { CodeMirror.defineMIME("text/javascript", "javascript"); CodeMirror.defineMIME("application/json", {name: "javascript", json: true}); +CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true }); +CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true }); diff --git a/lib/client/editor/codemirror3/package.json b/lib/client/editor/codemirror3/package.json index 7caef588..ea18e6b9 100644 --- a/lib/client/editor/codemirror3/package.json +++ b/lib/client/editor/codemirror3/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version":"3.0.1", + "version":"3.0.2", "main": "codemirror.js", "description": "In-browser code editing made bearable", "licenses": [{"type": "MIT", From 29e43707e05cd9dbac1f3d524cbb78d5b4edf1a0 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 15 Nov 2012 05:25:48 -0500 Subject: [PATCH 159/281] fixed bug with positioning of CodeMirror on the right panel --- ChangeLog | 5 ++++- lib/client/editor/_codemirror.js | 14 +++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/ChangeLog b/ChangeLog index bbcd31f4..30cdfe40 100644 --- a/ChangeLog +++ b/ChangeLog @@ -87,7 +87,10 @@ dom.js and util.js. * CodeMirror upgraded to version 2.35.0. -* Fixed bug with keys panel and fm bottom margin. +* Fixed bug with keys panel and fm bottom margin, +when CodeMirror is open on the right panel. + +* Fixed bug with positioning of CodeMirror on the right panel. 2012.10.01, Version 0.1.7 diff --git a/lib/client/editor/_codemirror.js b/lib/client/editor/_codemirror.js index 063e4ad9..7fa051a8 100644 --- a/lib/client/editor/_codemirror.js +++ b/lib/client/editor/_codemirror.js @@ -71,15 +71,15 @@ var CloudCommander, Util, DOM, CloudFunc, CodeMirror; 'font-family :\'Droid Sans Mono\';' + 'font-size :15px;' + /* codemirror v3 */ - //'height : ' + cloudcmd.HEIGHT + 'px' + + //'height : ' + cloudcmd.HEIGHT + 'px' + '}' + '.CodeMirror-scroll{' + - 'height : ' + (cloudcmd.HEIGHT) + 'px' + - '}', //+ - /* codemirror v3 */ - //'#CodeMirrorEditor{' + - // 'padding :20px 20px 20px 20px;' + - // '}' + 'height : ' + cloudcmd.HEIGHT + 'px' + + '}' + + '#CodeMirrorEditor{' + + 'float: right' + + // 'padding :20px 20px 20px 20px;' + + '}', parent: document.head }, lDir + 'codemirror.css', From d4fe51789acb9cb3bb91d6ff06cf4abba6c81b69 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 15 Nov 2012 05:26:19 -0500 Subject: [PATCH 160/281] added --- lib/client/editor/codemirror/package.json | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 lib/client/editor/codemirror/package.json diff --git a/lib/client/editor/codemirror/package.json b/lib/client/editor/codemirror/package.json new file mode 100644 index 00000000..8b959767 --- /dev/null +++ b/lib/client/editor/codemirror/package.json @@ -0,0 +1,21 @@ +{ + "name": "codemirror", + "version":"2.35.0", + "main": "codemirror.js", + "description": "In-browser code editing made bearable", + "licenses": [{"type": "MIT", + "url": "http://codemirror.net/LICENSE"}], + "directories": {"lib": "./lib"}, + "scripts": {"test": "node ./test/run.js"}, + "devDependencies": {"node-static": "0.6.0"}, + "bugs": "http://github.com/marijnh/CodeMirror/issues", + "keywords": ["JavaScript", "CodeMirror", "Editor"], + "homepage": "http://codemirror.net", + "maintainers":[{"name": "Marijn Haverbeke", + "email": "marijnh@gmail.com", + "web": "http://marijnhaverbeke.nl"}], + "repositories": [{"type": "git", + "url": "http://marijnhaverbeke.nl/git/codemirror"}, + {"type": "git", + "url": "https://github.com/marijnh/CodeMirror.git"}] +} From 9d626975d192b20f51a90da59c981f94e04cbe0b Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 15 Nov 2012 05:43:39 -0500 Subject: [PATCH 161/281] fixed bug with positioning of CodeMirror on the right panel --- lib/client/dom.js | 13 ++++++++-- lib/client/editor/_codemirror.js | 42 +++++++++++++++++--------------- 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/lib/client/dom.js b/lib/client/dom.js index 87e253cf..5696dc2f 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -701,11 +701,20 @@ var CloudCommander, $, Util, DOM, CloudFunc; return lRet; }; - DOM.hide = function(pElement){ + DOM.hide = function(pElement){ return DOM.addClass(pElement, 'hidden'); }; - DOM.removeClass = function(pElement, pClass){ + /** + * remove child of element + * @pChild + * @pElement + */ + DOM.remove = function(pChild, pElement){ + return (pElement || document.body).removeChild(pChild); + }; + + DOM.removeClass = function(pElement, pClass){ var lRet_b = true, lClassList = pElement.classList; diff --git a/lib/client/editor/_codemirror.js b/lib/client/editor/_codemirror.js index 7fa051a8..eaf11c55 100644 --- a/lib/client/editor/_codemirror.js +++ b/lib/client/editor/_codemirror.js @@ -20,6 +20,25 @@ var CloudCommander, Util, DOM, CloudFunc, CodeMirror; /* private functions */ + function setCSS(){ + return DOM.cssSet({ + id : 'editor', + inner : '.CodeMirror{' + + 'font-family' + ': \'Droid Sans Mono\';' + + 'font-size' + ': 15px;' + + /* codemirror v3 */ + //'height : ' + cloudcmd.HEIGHT + 'px' + + '}' + + '.CodeMirror-scroll{' + + 'height' + ':' + cloudcmd.HEIGHT + 'px' + + '}' + + '#CodeMirrorEditor{' + + 'float' + ':' + DOM.getPanel().id + + // 'padding :20px 20px 20px 20px;' + + '}' + }); + } + /** * function initialize CodeMirror * @param {value, callback} @@ -31,6 +50,8 @@ var CloudCommander, Util, DOM, CloudFunc, CodeMirror; if(!FM) FM = DOM.getFM(); + var lCSS = setCSS(); + CodeMirrorElement = DOM.anyload({ name : 'div', id : 'CodeMirrorEditor', @@ -49,6 +70,7 @@ var CloudCommander, Util, DOM, CloudFunc, CodeMirror; extraKeys: { //Сохранение 'Esc': function(){ + DOM.remove(lCSS, document.head); Util.exec(pData.callback); } }, @@ -63,25 +85,7 @@ var CloudCommander, Util, DOM, CloudFunc, CodeMirror; console.time('codemirror load'); var lDir = cloudcmd.LIBDIRCLIENT + 'editor/codemirror/'; - DOM.anyLoadOnLoad( - [{ - name: 'style', - id:'editor', - inner : '.CodeMirror{' + - 'font-family :\'Droid Sans Mono\';' + - 'font-size :15px;' + - /* codemirror v3 */ - //'height : ' + cloudcmd.HEIGHT + 'px' + - '}' + - '.CodeMirror-scroll{' + - 'height : ' + cloudcmd.HEIGHT + 'px' + - '}' + - '#CodeMirrorEditor{' + - 'float: right' + - // 'padding :20px 20px 20px 20px;' + - '}', - parent: document.head - }, + DOM.anyLoadOnLoad([ lDir + 'codemirror.css', lDir + 'theme/night.css', lDir + 'mode/javascript.js', From 9c98bd901fa860538cc6e4c7d433cdee90e8409d Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 15 Nov 2012 06:49:53 -0500 Subject: [PATCH 162/281] refactored --- cloudcmd.js | 29 ++++++++++-------- config.json | 3 +- lib/server/main.js | 7 +++-- lib/server/rest.js | 73 +++++++++++++++++++++++++++++++++++++--------- server.js | 16 ++++++---- 5 files changed, 93 insertions(+), 35 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index b887772f..fc1c4906 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -23,9 +23,14 @@ if(update) update.get(); - - function indexProcessing(pIndex, pList){ - var lReplace_s; + /** + * additional processing of index file + * + */ + function indexProcessing(pData){ + var lReplace_s, + lData = pData.data, + lAdditional = pData.additional; /* если выбрана опция минифизировать скрпиты * меняем в index.html обычные css на * минифицированый @@ -33,26 +38,26 @@ if(srv.Minify._allowed.css){ lReplace_s = ''; - pIndex = Util.removeStr(pIndex, lReplace_s); - pIndex = pIndex.replace('/css/style.css', srv.Minify.MinFolder + 'all.min.css'); + lData = Util.removeStr(lData, lReplace_s); + lData = lData.replace('/css/style.css', srv.Minify.MinFolder + 'all.min.css'); } lReplace_s = '
'; - pIndex = pIndex.replace(lReplace_s, lReplace_s + pList); + lData = lData.replace(lReplace_s, lReplace_s + lAdditional); /* меняем title */ - pIndex = pIndex.replace('Cloud Commander', + lData = lData.replace('Cloud Commander', '' + CloudFunc.setTitle() + ''); if(!srv.Config.appcache) - pIndex = Util.removeStr(pIndex, ' manifest=/cloudcmd.appcache'); + lData = Util.removeStr(lData, ' manifest=/cloudcmd.appcache'); if(!srv.Config.show_keys_panel){ var lKeysPanel = '
Date: Thu, 15 Nov 2012 07:22:17 -0500 Subject: [PATCH 163/281] work on rest module --- cloudcmd.js | 15 ++++++++++++++- lib/server/main.js | 1 + lib/server/rest.js | 34 ++++++++++++++++----------------- server.js | 47 ++++++++++++++++++++++++++++++---------------- 4 files changed, 62 insertions(+), 35 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index fc1c4906..a8039787 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -18,7 +18,11 @@ Config = main.config; readConfig(); - Server.start(Config, indexProcessing, appCacheProcessing); + Server.start(Config, { + index : indexProcessing, + appcache : appCacheProcessing, + rest : rest + }); if(update) update.get(); @@ -75,6 +79,15 @@ lAppCache.createManifest(); } + /** + * rest interface + * @pConnectionData {request, responce} + */ + function rest(pConnectionData){ + console.log('rest'); + Util.exec(main.rest, pConnectionData); + } + function readConfig(){ /* Determining server.js directory diff --git a/lib/server/main.js b/lib/server/main.js index b5daa013..11931c62 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -24,6 +24,7 @@ exports.http = require('http'), exports.https = require('https'), exports.path = require('path'), + exports.url = require('url'), exports.querystring = require('querystring'), /* Needed Modules */ diff --git a/lib/server/rest.js b/lib/server/rest.js index 982ae4c4..2b49f909 100644 --- a/lib/server/rest.js +++ b/lib/server/rest.js @@ -4,28 +4,26 @@ "use strict"; var DIR = process.cwd() + '/', - main = require(DIR + 'lib/server/main.js'), - SRVDIR = main.SRVDIR, + main = require(DIR + 'lib/server/main'), Util = main.util, - APIURL = '/api/v1/'; + APIURL = '/api/v1/'; - exports.rest = function(req, res, pCallBack){ - var lUrl = req.url, - lMethod = req.method; - - console.log(lUrl); - /* if lUrl contains api url */ - if( Util.isContainStr(lUrl, APIURL) ){ - lUrl = lUrl.replace(APIURL, ''); + /** + * rest interface + * @pConnectionData {request, responce} + */ + exports.rest = function(pConnectionData){ + var lReq = pConnectionData.request, + lRes = pConnectionData.response, + lUrl = lReq.url, + lMethod = lReq.method; + console.log(lUrl); - } - - console.log(req.url); - console.log(lMethod); - - Util.exec(pCallBack); - + console.log(lMethod); + + if( Util.isContainStr(lUrl, APIURL) ) + console.log('api !!!!!!!!!!!! '); /* switch(req.method){ case 'GET': diff --git a/server.js b/server.js index 529e7595..99098c4e 100644 --- a/server.js +++ b/server.js @@ -140,7 +140,10 @@ CloudServer.init = (function(pAppCachProcessing){ * Функция создаёт сервер * @param pConfig */ -CloudServer.start = function (pConfig, pIndexProcessing, pAppCachProcessing) { +CloudServer.start = function (pConfig, pProcessing) { + if(!pProcessing) + pProcessing = {}; + if(pConfig) this.Config = pConfig; @@ -151,9 +154,10 @@ CloudServer.start = function (pConfig, pIndexProcessing, pAppCachProcessing) { var lConfig = this.Config; - CloudServer.indexProcessing = pIndexProcessing; + CloudServer.indexProcessing = pProcessing.index; + CloudServer.rest = pProcessing.rest; - this.init(pAppCachProcessing); + this.init(pProcessing.appcache); this.Port = process.env.PORT || /* c9 */ process.env.app_port || /* nodester */ @@ -255,10 +259,11 @@ CloudServer.generateHeaders = function(pName, pGzip){ CloudServer._controller = function(pReq, pRes) { /* Читаем содержимое папки, переданное в url */ - var url = require("url"), - lParsedUrl = url.parse(pReq.url), - pathname = lParsedUrl.pathname, - + var lConfig = CloudServer.Config, + url = main.url, + lParsedUrl = url.parse(pReq.url), + pathname = lParsedUrl.pathname, + /* varible contain one of queris: * download - change content-type for * make downloading process @@ -269,7 +274,7 @@ CloudServer._controller = function(pReq, pRes) * query like this * ?json */ - lQuery = lParsedUrl.query; + lQuery = lParsedUrl.query; if(lQuery) console.log('query = ' + lQuery); @@ -295,16 +300,21 @@ CloudServer._controller = function(pReq, pRes) console.log("request for " + pathname + " received..."); + if( lConfig.rest ) + Util.exec(CloudServer.rest, { + request : pReq, + response : pRes + }); /* если в пути нет информации ни о ФС, * ни об отсутствии js, * ни о том, что это корневой * каталог - загружаем файлы проэкта */ - if ( !Util.isContainStr(pathname, lFS_s) && - !Util.isContainStr(pathname, lNoJS_s) && - !Util.strCmp(pathname, '/') && - !Util.strCmp(lQuery, 'json') ) { + else if ( !Util.isContainStr(pathname, lFS_s) && + !Util.isContainStr(pathname, lNoJS_s) && + !Util.strCmp(pathname, '/') && + !Util.strCmp(lQuery, 'json') ) { /* если имена файлов проекта - загружаем их*/ /* убираем слеш и читаем файл с текущец директории*/ @@ -313,7 +323,7 @@ CloudServer._controller = function(pReq, pRes) console.log('reading '+lName); /* watching is file changed */ - if(CloudServer.Config.appcache) + if(lConfig.appcache) CloudServer.AppCache.watch(lName); /* сохраняем указатель на response и имя */ @@ -372,7 +382,7 @@ CloudServer._controller = function(pReq, pRes) */ else if(lName.indexOf('min') < 0 && CloudServer.Minify){ - var lMin_o = CloudServer.Config.minification, + var lMin_o = lConfig.minification, lCheck_f = function(pExt){ return CloudFunc.checkExtension(lName,pExt); @@ -799,7 +809,12 @@ CloudServer.sendResponse = function(pHead, pData, pName){ } }; -exports.start = function(pConfig, pIndexProcessing, pAppCachProcessing){ - CloudServer.start(pConfig, pIndexProcessing, pAppCachProcessing); +/** + * start server function + * @param pConfig + * @param pProcessing {index, appcache, rest} + */ +exports.start = function(pConfig, pProcessing){ + CloudServer.start(pConfig, pProcessing); }; exports.CloudServer = CloudServer; \ No newline at end of file From 022b7f71a6c2c700960ce0769699c33780260e9c Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 15 Nov 2012 07:30:06 -0500 Subject: [PATCH 164/281] minor changes --- cloudcmd.js | 2 +- lib/server/rest.js | 9 +++++++-- server.js | 12 ++++++++---- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index a8039787..3ee4d743 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -85,7 +85,7 @@ */ function rest(pConnectionData){ console.log('rest'); - Util.exec(main.rest, pConnectionData); + return Util.exec(main.rest, pConnectionData); } function readConfig(){ diff --git a/lib/server/rest.js b/lib/server/rest.js index 2b49f909..65e53654 100644 --- a/lib/server/rest.js +++ b/lib/server/rest.js @@ -14,7 +14,8 @@ * @pConnectionData {request, responce} */ exports.rest = function(pConnectionData){ - var lReq = pConnectionData.request, + var lRet = false, + lReq = pConnectionData.request, lRes = pConnectionData.response, lUrl = lReq.url, lMethod = lReq.method; @@ -22,8 +23,12 @@ console.log(lUrl); console.log(lMethod); - if( Util.isContainStr(lUrl, APIURL) ) + if( Util.isContainStr(lUrl, APIURL) ){ console.log('api !!!!!!!!!!!! '); + return true; + } + + return lRet; /* switch(req.method){ case 'GET': diff --git a/server.js b/server.js index 99098c4e..e61d143c 100644 --- a/server.js +++ b/server.js @@ -299,19 +299,23 @@ CloudServer._controller = function(pReq, pRes) lFS_s = CloudFunc.FS; console.log("request for " + pathname + " received..."); - - if( lConfig.rest ) - Util.exec(CloudServer.rest, { + + if( lConfig.rest ){ + var lRestWas = Util.exec(CloudServer.rest, { request : pReq, response : pRes }); + + if(lRestWas) + return; + } /* если в пути нет информации ни о ФС, * ни об отсутствии js, * ни о том, что это корневой * каталог - загружаем файлы проэкта */ - else if ( !Util.isContainStr(pathname, lFS_s) && + if ( !Util.isContainStr(pathname, lFS_s) && !Util.isContainStr(pathname, lNoJS_s) && !Util.strCmp(pathname, '/') && !Util.strCmp(lQuery, 'json') ) { From febd28984cc0e304eeba6286cc7e71df7c84b138 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 15 Nov 2012 07:33:41 -0500 Subject: [PATCH 165/281] minor changes --- cloudcmd.js | 1 - lib/server/main.js | 2 +- lib/server/rest.js | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 3ee4d743..2ef9885b 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -84,7 +84,6 @@ * @pConnectionData {request, responce} */ function rest(pConnectionData){ - console.log('rest'); return Util.exec(main.rest, pConnectionData); } diff --git a/lib/server/main.js b/lib/server/main.js index 11931c62..209b644b 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -40,7 +40,7 @@ exports.appcache = srvrequire('appcache'), exports.cache = srvrequire('cache').Cache, exports.cloudfunc = librequire('cloudfunc'), - exports.rest = srvrequire('rest'), + exports.rest = srvrequire('rest').api, exports.socket = srvrequire('socket'), exports.update = srvrequire('update'), exports.minify = srvrequire('minify').Minify, diff --git a/lib/server/rest.js b/lib/server/rest.js index 65e53654..3dcecaeb 100644 --- a/lib/server/rest.js +++ b/lib/server/rest.js @@ -13,7 +13,7 @@ * rest interface * @pConnectionData {request, responce} */ - exports.rest = function(pConnectionData){ + exports.api = function(pConnectionData){ var lRet = false, lReq = pConnectionData.request, lRes = pConnectionData.response, From 3e02747e1b7331d236b673fc0b9d458a5b124495 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 15 Nov 2012 09:23:45 -0500 Subject: [PATCH 166/281] setted up comments --- lib/client/dom.js | 213 +++++++++++++++++++++++++++++++++------------ lib/server/rest.js | 6 +- lib/util.js | 6 +- server.js | 4 +- 4 files changed, 165 insertions(+), 64 deletions(-) diff --git a/lib/client/dom.js b/lib/client/dom.js index 5696dc2f..fc887b25 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -7,6 +7,7 @@ var CloudCommander, $, Util, DOM, CloudFunc; /* PRIVATE */ + function getCurrentFile(){ return CloudCommander.CURRENT_FILE; } @@ -52,7 +53,12 @@ var CloudCommander, $, Util, DOM, CloudFunc; } }; - DOM.addClass = function(pElement, pClass){ + /** + * add class to current element + * @param pElement + * @param pClass + */ + DOM.addClass = function(pElement, pClass){ var lRet_b = true; var lClassList = pElement.classList; @@ -71,6 +77,7 @@ var CloudCommander, $, Util, DOM, CloudFunc; * @param pType * @param pListener * @param pUseCapture + * @param pElement {document by default} */ DOM.addListener = function(pType, pListener, pUseCapture, pElement){ return (pElement || document).addEventListener( @@ -82,7 +89,6 @@ var CloudCommander, $, Util, DOM, CloudFunc; /** * safe add event keydown listener - * @param pType * @param pListener * @param pUseCapture */ @@ -90,7 +96,9 @@ var CloudCommander, $, Util, DOM, CloudFunc; return DOM.addListener('keydown', pListener, pUseCapture); }; - /* Load file countent thrue ajax */ + /** + * load file countent thrue ajax + */ DOM.ajax = function(pParams){ /* if on webkit */ if(!XMLHTTP) @@ -141,10 +149,11 @@ var CloudCommander, $, Util, DOM, CloudFunc; }; }; - /* + /** * Function gets id by src - * from http://domain.com/1.js to - * 1_js + * @param pSrc + * + * Example: http://domain.com/1.js -> 1_js */ DOM.getIdBySrc = function(pSrc){ var lID = pSrc.replace(pSrc.substr(pSrc, @@ -159,6 +168,13 @@ var CloudCommander, $, Util, DOM, CloudFunc; }, + /** + * create elements and load them to DOM-tree + * one-by-one + * + * @param pParams_a + * @param pFunc - onload function + */ DOM.anyLoadOnLoad = function(pParams_a, pFunc){ if( Util.isArray(pParams_a) ) { var lParam = pParams_a.pop(); @@ -180,20 +196,24 @@ var CloudCommander, $, Util, DOM, CloudFunc; }; /** - * Функция создаёт элемент и - * загружает файл с src. - * @pName - название тэга - * @pSrc - путь к файлу - * @pFunc - обьект, содержаий одну из функций - * или сразу две onload и onerror - * {onload: function(){}, onerror: function();} - * @pStyle - стиль - * @pId - id - * @pElement - элемент, дочерним которо будет этот - * @param pParams_o = {name: '', src: ' ',func: '', style: '', id: '', parent: '', - async: false, inner: 'id{color:red, }, class:'', not_append: false} + * Функция создаёт элемент и загружает файл с 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 + * } */ - DOM.anyload = function(pParams_o){ + DOM.anyload = function(pParams_o){ if( !pParams_o ) return; @@ -317,8 +337,13 @@ var CloudCommander, $, Util, DOM, CloudFunc; return element; }, - /* Функция загружает js-файл */ - DOM.jsload = function(pSrc, pFunc){ + /** + * Функция загружает js-файл + * + * @param pSrc + * @param pFunc + */ + DOM.jsload = function(pSrc, pFunc){ if(pSrc instanceof Array){ for(var i=0; i < pSrc.length; i++) pSrc[i].name = 'script'; @@ -333,25 +358,26 @@ var CloudCommander, $, Util, DOM, CloudFunc; }); }, - /* Функция создаёт елемент style и записывает туда стили - * @pParams_o - структура параметров, заполняеться таким + /** + * Функция создаёт елемент style и записывает туда стили + * @param pParams_o - структура параметров, заполняеться таким * образом: {src: ' ',func: '', id: '', element: '', inner: ''} * все параметры опциональны - */ - - DOM.cssSet = function(pParams_o){ + */ + DOM.cssSet = function(pParams_o){ pParams_o.name = 'style'; pParams_o.parent = pParams_o.parent || document.head; return DOM.anyload(pParams_o); }, - /* Function loads external css files + /** + * Function loads external css files * @pParams_o - структура параметров, заполняеться таким * образом: {src: ' ',func: '', id: '', element: '', inner: ''} * все параметры опциональны */ - DOM.cssLoad = function(pParams_o){ + DOM.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'; @@ -370,7 +396,11 @@ var CloudCommander, $, Util, DOM, CloudFunc; return DOM.anyload(pParams_o); }; - DOM.jqueryLoad = function(pCallBack){ + /** + * load jquery from google cdn or local copy + * @param pCallBack + */ + DOM.jqueryLoad = function(pCallBack){ /* загружаем jquery: */ DOM.jsload('//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js',{ onload: Util.retExec(pCallBack), @@ -394,7 +424,11 @@ var CloudCommander, $, Util, DOM, CloudFunc; }); }; - DOM.socketLoad = function(pCallBack){ + /** + * load socket.io + * @param pCallBack + */ + DOM.socketLoad = function(pCallBack){ DOM.jsload('lib/client/socket.js', pCallBack); }; @@ -402,17 +436,17 @@ var CloudCommander, $, Util, DOM, CloudFunc; /** * Function search element by tag - * @pTag - className - * @pElement - element + * @param pTag - className + * @param pElement - element */ - DOM.getByTag = function(pTag, pElement){ + DOM.getByTag = function(pTag, pElement){ return (pElement || document).getElementsByTagName(pTag); }; /** * Function search element by id - * @Id - className - * @pElement - element + * @param Id - className + * @param pElement - element */ DOM.getById = function(pId, pElement){ return (pElement || document).getElementById(pId); @@ -420,18 +454,17 @@ var CloudCommander, $, Util, DOM, CloudFunc; /** * Function search element by class name - * @pClass - className - * @pElement - element + * @param pClass - className + * @param pElement - element */ - DOM.getByClass = function(pClass, pElement){ + DOM.getByClass = function(pClass, pElement){ return (pElement || document).getElementsByClassName(pClass); }; - DOM.Images = { - /* + DOM.Images = { + /** * Function shows loading spinner - * @pElem - top element of screen * pPosition = {top: true}; */ showLoad : function(pPosition){ @@ -474,11 +507,17 @@ var CloudCommander, $, Util, DOM, CloudFunc; return lRet_b; }, + /** + * hide load image + */ hideLoad : function(){ LoadingImage = Images_o.loading(); DOM.hide(LoadingImage); }, + /** + * show error image (usualy after error on ajax request) + */ showError : function(jqXHR, textStatus, errorThrown){ LoadingImage = Images_o.loading(); @@ -511,6 +550,9 @@ var CloudCommander, $, Util, DOM, CloudFunc; } }; + /** + * unified way to get current file + */ DOM.getCurrentFile = function(){ var lCurrent = DOM.getByClass(getCurrentFile())[0]; if(!lCurrent) @@ -524,6 +566,10 @@ var CloudCommander, $, Util, DOM, CloudFunc; return lCurrent; }; + + /** + * unified way to get RefreshButton + */ DOM.getRefreshButton = function(){ var lPanel = DOM.getPanel(), lRefresh = DOM.getByClass(CloudFunc.REFRESHICON, lPanel); @@ -541,6 +587,9 @@ var CloudCommander, $, Util, DOM, CloudFunc; return lRefresh; }; + /** + * unified way to set current file + */ DOM.setCurrentFile = function(pCurrentFile){ var lRet_b = true; @@ -574,12 +623,16 @@ var CloudCommander, $, Util, DOM, CloudFunc; }; /** - * set onclick handler on button f1-f10 + * set onclick handler on buttons f1-f10 + * @param pKey - 'f1'-'f10' */ DOM.setButtonKey = function(pKey, pFunc){ return CloudCommander.KeysPanel[pKey].onclick = pFunc; }; + /** + * private function thet unset currentfile + */ var lUnSetCurrentFile = function(pCurrentFile){ if(!pCurrentFile) DOM.addCloudStatus({ @@ -597,6 +650,11 @@ var CloudCommander, $, Util, DOM, CloudFunc; return lRet_b; }; + /** + * current file check + * + * @param pCurrentFile + */ DOM.isCurrentFile = function(pCurrentFile){ if(!pCurrentFile) DOM.addCloudStatus({ @@ -613,7 +671,12 @@ var CloudCommander, $, Util, DOM, CloudFunc; }; - DOM.getCurrentLink = function(pCurrentFile){ + /** + * get link from current (or param) file + * + * @param pCurrentFile - current file by default + */ + DOM.getCurrentLink = function(pCurrentFile){ var lLink = DOM.getByTag('a', pCurrentFile || DOM.getCurrentFile()), @@ -628,7 +691,12 @@ var CloudCommander, $, Util, DOM, CloudFunc; return lRet; }; - DOM.getCurrentName = function(pCurrentFile){ + /** + * get name from current (or param) file + * + * @param pCurrentFile + */ + DOM.getCurrentName = function(pCurrentFile){ var lLink = DOM.getCurrentLink( pCurrentFile || DOM.getCurrentFile()); @@ -645,14 +713,14 @@ var CloudCommander, $, Util, DOM, CloudFunc; /** function getting FM * @param pPanel_o = {active: true} */ - DOM.getFM = function(){ + DOM.getFM = function(){ return DOM.getPanel().parentElement; }; /** function getting panel active, or passive * @param pPanel_o = {active: true} */ - DOM.getPanel = function(pActive){ + DOM.getPanel = function(pActive){ var lPanel = DOM.getCurrentFile().parentElement; /* if {active : false} getting passive panel */ @@ -675,11 +743,14 @@ var CloudCommander, $, Util, DOM, CloudFunc; return lPanel; }; - DOM.show = function(pElement){ + DOM.show = function(pElement){ DOM.removeClass(pElement, 'hidden'); }; - DOM.showPanel = function(pActive){ + /** + * shows panel right or left (or active) + */ + DOM.showPanel = function(pActive){ var lRet = true, lPanel = DOM.getPanel(pActive); @@ -691,7 +762,10 @@ var CloudCommander, $, Util, DOM, CloudFunc; return lRet; }; - DOM.hidePanel = function(pActive){ + /** + * hides panel right or left (or active) + */ + DOM.hidePanel = function(pActive){ var lRet = false, lPanel = DOM.getPanel(pActive); @@ -701,19 +775,29 @@ var CloudCommander, $, Util, DOM, CloudFunc; return lRet; }; + /** + * add class=hidden to element + * + * @param pElement + */ DOM.hide = function(pElement){ return DOM.addClass(pElement, 'hidden'); }; /** * remove child of element - * @pChild - * @pElement + * @param pChild + * @param pElement */ DOM.remove = function(pChild, pElement){ return (pElement || document.body).removeChild(pChild); }; + /** + * remove class pClass from element pElement + * @param pElement + * @param pClass + */ DOM.removeClass = function(pElement, pClass){ var lRet_b = true, lClassList = pElement.classList; @@ -727,7 +811,11 @@ var CloudCommander, $, Util, DOM, CloudFunc; return lRet_b; }; - DOM.removeCurrent = function(pCurrent){ + /** + * remove current file from file table + * @pCurrent + */ + DOM.removeCurrent = function(pCurrent){ var lParent = pCurrent.parentElement; if(!pCurrent) @@ -760,7 +848,12 @@ var CloudCommander, $, Util, DOM, CloudFunc; return pCurrent; }; - DOM.scrollIntoViewIfNeeded = function(pElement){ + /** + * unified way to scrollIntoViewIfNeeded + * (native suporte by webkit only) + * @param pElement + */ + DOM.scrollIntoViewIfNeeded = function(pElement){ var lRet = true; if(pElement && pElement.scrollIntoViewIfNeeded) @@ -771,11 +864,10 @@ var CloudCommander, $, Util, DOM, CloudFunc; return lRet; }; - /** * function gets time */ - DOM.getTime = function(){ + DOM.getTime = function(){ var date = new Date(), hours = date.getHours(), minutes = date.getMinutes(), @@ -787,9 +879,16 @@ var CloudCommander, $, Util, DOM, CloudFunc; return hours + ":" + minutes + ":" + seconds; }; - DOM.CloudStatus = []; + /** + * array of all statuses of opertattions + */ + DOM.CloudStatus = []; - DOM.addCloudStatus = function(pStatus){ + /** + * adds status of operation + * @param pStatus + */ + DOM.addCloudStatus = function(pStatus){ DOM.CloudStatus[DOM.CloudStatus.length] = pStatus; }; })(); \ No newline at end of file diff --git a/lib/server/rest.js b/lib/server/rest.js index 3dcecaeb..8137a191 100644 --- a/lib/server/rest.js +++ b/lib/server/rest.js @@ -7,7 +7,7 @@ main = require(DIR + 'lib/server/main'), Util = main.util, - APIURL = '/api/v1/'; + APIURL = '/api/v1'; /** * rest interface @@ -24,8 +24,8 @@ console.log(lMethod); if( Util.isContainStr(lUrl, APIURL) ){ - console.log('api !!!!!!!!!!!! '); - return true; + lRes.end(APIURL); + lRet = true; } return lRet; diff --git a/lib/util.js b/lib/util.js index e713b5ab..14a1a412 100644 --- a/lib/util.js +++ b/lib/util.js @@ -225,7 +225,9 @@ var Util, exports; return Util.log(lRet); }; /** - * function do save exec + * function do save exec of function + * @param pCallBack + * @param pArg */ Util.exec = function(pCallBack, pArg){ var lRet = false; @@ -237,7 +239,7 @@ var Util, exports; }; /** - * function gets time + * Gets current time in format hh:mm:ss */ Util.getTime = function(){ var date = new Date(), diff --git a/server.js b/server.js index e61d143c..02a3c1e5 100644 --- a/server.js +++ b/server.js @@ -806,7 +806,7 @@ CloudServer.sendResponse = function(pHead, pData, pName){ lStatus = CloudServer.Statuses[pName]; if(lResponse){ - lResponse.writeHead(lStatus, pHead); + lResponse.writeHead(lStatus, pHead); lResponse.end(pData); console.log(pName + ' sended'); @@ -821,4 +821,4 @@ CloudServer.sendResponse = function(pHead, pData, pName){ exports.start = function(pConfig, pProcessing){ CloudServer.start(pConfig, pProcessing); }; -exports.CloudServer = CloudServer; \ No newline at end of file +exports.CloudServer = CloudServer; \ No newline at end of file From d986d358bcc10404f0c1e052946e0c0c557befbd Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 15 Nov 2012 10:12:44 -0500 Subject: [PATCH 167/281] added comments --- lib/client/dom.js | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/lib/client/dom.js b/lib/client/dom.js index fc887b25..92fe7211 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -7,11 +7,30 @@ var CloudCommander, $, Util, DOM, CloudFunc; /* PRIVATE */ - function getCurrentFile(){ return CloudCommander.CURRENT_FILE; } + /** + * private function thet unset currentfile + */ + function UnSetCurrentFile(pCurrentFile){ + if(!pCurrentFile) + DOM.addCloudStatus({ + code : -1, + msg : 'Error pCurrentFile in' + + 'unSetCurrentFile' + + 'could not be none' + }); + + var lRet_b = DOM.isCurrentFile(pCurrentFile); + + if(lRet_b) + DOM.removeClass(pCurrentFile, getCurrentFile()); + + return lRet_b; + } + /* private members */ var XMLHTTP, LoadingImage, @@ -612,8 +631,8 @@ var CloudCommander, $, Util, DOM, CloudFunc; pCurrentFile = pCurrentFile.nextSibling; if(lCurrentFileWas) - lUnSetCurrentFile(lCurrentFileWas); - + UnSetCurrentFile(lCurrentFileWas); + DOM.addClass(pCurrentFile, getCurrentFile()); /* scrolling to current file */ @@ -630,26 +649,6 @@ var CloudCommander, $, Util, DOM, CloudFunc; return CloudCommander.KeysPanel[pKey].onclick = pFunc; }; - /** - * private function thet unset currentfile - */ - var lUnSetCurrentFile = function(pCurrentFile){ - if(!pCurrentFile) - DOM.addCloudStatus({ - code : -1, - msg : 'Error pCurrentFile in' + - 'unSetCurrentFile' + - 'could not be none' - }); - - var lRet_b = DOM.isCurrentFile(pCurrentFile); - - if(lRet_b) - DOM.removeClass(pCurrentFile, getCurrentFile()); - - return lRet_b; - }; - /** * current file check * From 894e8fc8bde3005ab8d3ce2ad99fb317412987e3 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 15 Nov 2012 10:27:33 -0500 Subject: [PATCH 168/281] function generateHeaders moved to main module --- lib/server/main.js | 63 +++++++++++++++++++++++- lib/server/rest.js | 2 +- server.js | 117 ++++++++++++--------------------------------- 3 files changed, 94 insertions(+), 88 deletions(-) diff --git a/lib/server/main.js b/lib/server/main.js index 209b644b..6259bdf1 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -4,7 +4,18 @@ var DIR, LIBDIR, SRVDIR, - Util; + Util, + + Extensions = { + '.css' : 'text/css', + '.js' : 'text/javascript', + '.png' : 'image/png', + '.json' : 'application/json', + '.html' : 'text/html', + '.woff' : 'font/woff', + '.appcache' : 'text/cache-manifest', + '.mp3' : 'audio/mpeg' + }; /* Constants */ exports.DIR = DIR = process.cwd() + '/', @@ -13,6 +24,7 @@ exports.WIN32 = isWin32(); /* Functions */ + exports.generateHeaders = generateHeaders, exports.require = mrequire, exports.librequire = librequire, exports.srvrequire = srvrequire, @@ -74,4 +86,53 @@ */ function isWin32(){ return process.platform === 'win32'; } + /** + * Функция создаёт заголовки файлов + * в зависимости от расширения файла + * перед отправкой их клиенту + * @param pName - имя файла + * @param pGzip - данные сжаты gzip'ом + */ + function generateHeaders(pName, pGzip, pQuery){ + var lType = '', + lCacheControl = 0, + lContentEncoding = '', + lRet, + + lDot = pName.lastIndexOf('.'), + lExt = pName.substr(lDot); + + if( Util.strCmp(lExt, '.appcache') ) + lCacheControl = 1; + + lType = Extensions[lExt] || 'text/plain'; + + if( !Util.isContainStr(lType, 'img') ) + lContentEncoding = '; charset=UTF-8'; + + if(Util.strCmp(pQuery, 'download') ) + lType = 'application/octet-stream'; + + + if(!lCacheControl) + lCacheControl = 31337 * 21; + + lRet = { + /* if type of file any, but img - + * then we shoud specify charset + */ + 'Content-Type': lType + lContentEncoding, + 'cache-control': 'max-age=' + lCacheControl, + 'last-modified': new Date().toString(), + /* https://developers.google.com/speed/docs/best-practices + /caching?hl=ru#LeverageProxyCaching */ + 'Vary': 'Accept-Encoding' + }; + + if(pGzip) + lRet['content-encoding'] = 'gzip'; + + return lRet; +} + })(); diff --git a/lib/server/rest.js b/lib/server/rest.js index 8137a191..b03c9893 100644 --- a/lib/server/rest.js +++ b/lib/server/rest.js @@ -23,7 +23,7 @@ console.log(lUrl); console.log(lMethod); - if( Util.isContainStr(lUrl, APIURL) ){ + if( Util.isContainStr(lUrl, APIURL) ){ lRes.end(APIURL); lRet = true; } diff --git a/server.js b/server.js index 02a3c1e5..90e9c83f 100644 --- a/server.js +++ b/server.js @@ -68,17 +68,7 @@ var CloudServer = { Server :{}, /* КОНСТАНТЫ */ - INDEX : 'index.html', - Extensions :{ - '.css' : 'text/css', - '.js' : 'text/javascript', - '.png' : 'image/png', - '.json' : 'application/json', - '.html' : 'text/html', - '.woff' : 'font/woff', - '.appcache' : 'text/cache-manifest', - '.mp3' : 'audio/mpeg' - }, + INDEX : 'index.html' }, DirPath = '/', @@ -197,59 +187,6 @@ CloudServer.start = function (pConfig, pProcessing) { }; -/** - * Функция создаёт заголовки файлов - * в зависимости от расширения файла - * перед отправкой их клиенту - * @param pName - имя файла - * @param pGzip - данные сжаты gzip'ом - */ -CloudServer.generateHeaders = function(pName, pGzip){ - var lType = '', - lCacheControl = 0, - lContentEncoding = '', - lRet, - - lDot = pName.lastIndexOf('.'), - lExt = pName.substr(lDot); - - if( Util.strCmp(lExt, '.appcache') ) - lCacheControl = 1; - - lType = CloudServer.Extensions[lExt] || 'text/plain'; - - if( !Util.isContainStr(lType, 'img') ) - lContentEncoding = '; charset=UTF-8'; - - var lQuery = CloudServer.Queries[pName]; - if(lQuery){ - if( Util.strCmp(lQuery, 'download') ) - lType = 'application/octet-stream'; - - console.log(pName + lQuery); - } - - if(!lCacheControl) - lCacheControl = 31337 * 21; - - lRet = { - /* if type of file any, but img - - * then we shoud specify charset - */ - 'Content-Type': lType + lContentEncoding, - 'cache-control': 'max-age=' + lCacheControl, - 'last-modified': new Date().toString(), - /* https://developers.google.com/speed/docs/best-practices - /caching?hl=ru#LeverageProxyCaching */ - 'Vary': 'Accept-Encoding' - }; - - if(pGzip) - lRet['content-encoding'] = 'gzip'; - - return lRet; -}; - /** * Главная функция, через которую проихсодит * взаимодействие, обмен данными с клиентом @@ -644,15 +581,17 @@ CloudServer._fillJSON = function(pStats, pFiles){ } }else{ DirPath = DirPath.substr(DirPath, DirPath.lastIndexOf('/') ); + var lQuyery = CloudServer.Queries[DirPath]; - DirPath += '.json'; + DirPath += '.json'; CloudServer.Queries[DirPath] = lQuyery; + /* в обычном режиме(когда js включен * высылаем json-структуру файлов * с соответствующими заголовками */ lList = JSON.stringify(lJSON); - lHeader = CloudServer.generateHeaders(DirPath, CloudServer.Gzip); + lHeader = main.generateHeaders(DirPath, CloudServer.Gzip, lQuyery); /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ if(CloudServer.Gzip){ @@ -670,17 +609,20 @@ CloudServer._fillJSON = function(pStats, pFiles){ CloudServer.indexReaded = function(pList){ return function(pError, pIndex){ if(pError){ - return console.log(pError); + return console.log(pError); } - + + var lSrv = CloudServer, + lIndexName = lSrv.INDEX; + /* и сохраняем в кэш */ - CloudServer.Cache.set(CloudServer.INDEX, pIndex); + lSrv.Cache.set(lIndexName, pIndex); pIndex = pIndex.toString(); var lProccessed, - lIndexProccessing = CloudServer.indexProcessing; + lIndexProccessing = lSrv.indexProcessing; lProccessed = Util.exec(lIndexProccessing, { data : pIndex, @@ -691,19 +633,20 @@ CloudServer.indexReaded = function(pList){ pIndex = lProccessed; /* * если браузер поддерживает gzip-сжатие - * высылаем заголовок в зависимости от типа файла - */ - var lHeader = CloudServer.generateHeaders('index.html', CloudServer.Gzip); + * высылаем заголовок в зависимости от типа файла + */ + var lQuery = lSrv.Queries[lIndexName], + lHeader = main.generateHeaders(lIndexName, lSrv.Gzip, lQuery); - /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ - if(CloudServer.Gzip) { + /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ + if(lSrv.Gzip) { Zlib.gzip(pIndex, - CloudServer.getGzipDataFunc(lHeader, CloudServer.INDEX)); + lSrv.getGzipDataFunc(lHeader, lIndexName)); } /* если не поддерживаеться - отсылаем данные без сжатия*/ else - CloudServer.sendResponse(lHeader, pIndex, CloudServer.INDEX); + lSrv.sendResponse(lHeader, pIndex, lIndexName); }; }; @@ -722,6 +665,7 @@ CloudServer.getReadFileFunc = function(pName){ * Пример {cache: false, minify: true} */ var lReadFile = function(pError, pData, pFromCache_o){ + var lSrv = CloudServer; if (!pError){ console.log('file ' + pName + ' readed'); @@ -730,8 +674,8 @@ CloudServer.getReadFileFunc = function(pName){ * сохраняем */ if(pFromCache_o && !pFromCache_o.cache && - CloudServer.Cache.isAllowed) - CloudServer.Cache.set(pName, pData); + lSrv.Cache.isAllowed) + lSrv.Cache.set(pName, pData); /* если кэш есть * сохраняем его в переменную @@ -739,15 +683,16 @@ CloudServer.getReadFileFunc = function(pName){ * по скольку мы будем вызывать этот метод * сами, ведь файл уже вычитан */ - var lHeader = CloudServer.generateHeaders(pName, CloudServer.Gzip); + var lQuery = lSrv.Queries[pName], + lHeader = main.generateHeaders(pName, lSrv.Gzip, lQuery); /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ - if( CloudServer.Gzip && !(pFromCache_o && pFromCache_o.cache) ) + if( lSrv.Gzip && !(pFromCache_o && pFromCache_o.cache) ) /* сжимаем содержимое */ - Zlib.gzip(pData,CloudServer.getGzipDataFunc(lHeader, pName)); + Zlib.gzip(pData,lSrv.getGzipDataFunc(lHeader, pName)); else /* высылаем несжатые данные */ - CloudServer.sendResponse(lHeader, pData, pName); + lSrv.sendResponse(lHeader, pData, pName); } else{ console.log(pError.path); @@ -755,10 +700,10 @@ CloudServer.getReadFileFunc = function(pName){ console.log(pError); /* sending page not found */ - CloudServer.Statuses[pName] = 404; - CloudServer.sendResponse('file not found', pError.toString(), pName); + lSrv.Statuses[pName] = 404; + lSrv.sendResponse('file not found', pError.toString(), pName); }else - CloudServer.sendResponse('OK', 'passwd.json'); + lSrv.sendResponse('OK', 'passwd.json'); } }; From 37e34e868f5134f309c1495203a054d48f51e268 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 15 Nov 2012 10:27:50 -0500 Subject: [PATCH 169/281] minor chagnes --- ChangeLog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog b/ChangeLog index 30cdfe40..1761d433 100644 --- a/ChangeLog +++ b/ChangeLog @@ -92,6 +92,8 @@ when CodeMirror is open on the right panel. * Fixed bug with positioning of CodeMirror on the right panel. +* Function generateHeaders moved to main module. + 2012.10.01, Version 0.1.7 * Changed name of menu files, fixed npm and jitsu From 4da6cf8784d2a4f5bc3d4d417d8f10da625e244d Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 15 Nov 2012 10:39:04 -0500 Subject: [PATCH 170/281] setted up restapi in json --- lib/server/rest.js | 11 +++++++---- package.json | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/server/rest.js b/lib/server/rest.js index b03c9893..f172a329 100644 --- a/lib/server/rest.js +++ b/lib/server/rest.js @@ -6,9 +6,10 @@ var DIR = process.cwd() + '/', main = require(DIR + 'lib/server/main'), Util = main.util, - - APIURL = '/api/v1'; - + + APIURL = '/api/v1', + OK = 200; + /** * rest interface * @pConnectionData {request, responce} @@ -23,7 +24,9 @@ console.log(lUrl); console.log(lMethod); - if( Util.isContainStr(lUrl, APIURL) ){ + if( Util.isContainStr(lUrl, APIURL) ){ + var lHead = main.generateHeaders('api.json', false); + lRes.writeHead(OK, lHead); lRes.end(APIURL); lRet = true; } diff --git a/package.json b/package.json index 9c9c110b..74e76409 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cloudcmd", - "version": "0.1.7-20", + "version": "0.1.7", "author": "coderaiser (https://github.com/coderaiser)", "description": "Two-panels file manager, totally writed on js.", "homepage": "https://github.com/coderaiser/cloudcmd", From 055377d5abbd588e0c5b0fc99c191000455675a1 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 15 Nov 2012 10:43:16 -0500 Subject: [PATCH 171/281] minor changes --- ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog b/ChangeLog index 1761d433..d1ba1bb1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -94,6 +94,7 @@ when CodeMirror is open on the right panel. * Function generateHeaders moved to main module. + 2012.10.01, Version 0.1.7 * Changed name of menu files, fixed npm and jitsu From 70f648ef16fbe3bace08efe417952a9ac90a81b6 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 16 Nov 2012 05:22:55 -0500 Subject: [PATCH 172/281] improved rest module --- lib/server/main.js | 2 +- lib/server/rest.js | 114 +++++++++++++++++++++++++++++---------------- server.js | 24 +++++----- 3 files changed, 87 insertions(+), 53 deletions(-) diff --git a/lib/server/main.js b/lib/server/main.js index 6259bdf1..a4cfd70f 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -48,7 +48,7 @@ /* Additional Modules */ - exports.auth = srvrequire('auth'), + exports.auth = srvrequire('auth').auth, exports.appcache = srvrequire('appcache'), exports.cache = srvrequire('cache').Cache, exports.cloudfunc = librequire('cloudfunc'), diff --git a/lib/server/rest.js b/lib/server/rest.js index f172a329..3b628e5a 100644 --- a/lib/server/rest.js +++ b/lib/server/rest.js @@ -6,9 +6,9 @@ var DIR = process.cwd() + '/', main = require(DIR + 'lib/server/main'), Util = main.util, - APIURL = '/api/v1', - OK = 200; + OK = 200, + Header = main.generateHeaders('api.json', false); /** * rest interface @@ -21,49 +21,81 @@ lUrl = lReq.url, lMethod = lReq.method; - console.log(lUrl); - console.log(lMethod); - if( Util.isContainStr(lUrl, APIURL) ){ - var lHead = main.generateHeaders('api.json', false); - lRes.writeHead(OK, lHead); - lRes.end(APIURL); + var lCommand = Util.removeStr(lUrl, APIURL), + lData = getData(lMethod, lCommand); + + send(lRes, lData); + lRet = true; } return lRet; - /* - switch(req.method){ - case 'GET': - switch(lCommand){ - case 'count': - lResult = { - count : Users.length, - }; - break; - - case 'list': - lResult = Users; - break; - } - break; - - case 'PUT': - if( lCommand.indexOf('register') === 0 ){ - lResult = { - registered : true, - name : req.body.name, - server_time : Util.getTime(), - client_time : req.body.time - }; - Users.push(req.body.name); - - console.log(lResult); - console.log(req.connection.remoteAddress); - } - break; - } - */ - }; + + + /** + * send data + * + * @param pRes + * @param pData + */ + function send(pRes, pData){ + pRes.writeHead(OK, Header); + pRes.end( pData.toString() ); + } + + /** + * getting data on method and command + * + * @param pMethod + * @param pCommand + */ + function getData(pMethod, pCommand){ + var lResult; + + switch(pMethod){ + case 'GET': + lResult = onGET(pCommand); + break; + + case 'PUT': + lResult = onPUT(pCommand); + break; + } + + return lResult; + } + + /** + * process data on GET request + * + * @param pCommand + */ + function onGET(pCommand){ + var lResult; + + switch(pCommand){ + case '': + lResult = {info: 'Cloud Commander API v1'}; + break; + } + + return lResult; + } + + /** + * process data on PUT request + * + * @param pCommand + */ + function onPUT(pCommand){ + var lResult; + + switch(pCommand){ + } + + return lResult; + } + })(); diff --git a/server.js b/server.js index 90e9c83f..31c349c9 100644 --- a/server.js +++ b/server.js @@ -70,9 +70,10 @@ var CloudServer = { /* КОНСТАНТЫ */ INDEX : 'index.html' }, - + DirPath = '/', + OK = 200, DIR = process.cwd() + '/', main = require(DIR + 'lib/server/main.js'), @@ -271,7 +272,7 @@ CloudServer._controller = function(pReq, pRes) CloudServer.Responses[lName] = pRes; /* saving status OK for current file */ - CloudServer.Statuses[lName] = 200; + CloudServer.Statuses[lName] = OK; /* Берём значение из кэша * сжатый файл - если gzip-поддерживаеться браузером @@ -347,7 +348,7 @@ CloudServer._controller = function(pReq, pRes) lResult = false; if(!lResult) - Fs.readFile(lName, lReadFileFunc_f); + Fs.readFile(lName, lReadFileFunc_f); }else{/* если мы имеем дело с файловой системой*/ /* если путь не начинаеться с no-js - значит * js включен @@ -384,7 +385,7 @@ CloudServer._controller = function(pReq, pRes) /* если в итоге путь пустой * делаем его корневым - */ + */ if (pathname === '') pathname = '/'; @@ -392,10 +393,10 @@ CloudServer._controller = function(pReq, pRes) CloudServer.Responses[DirPath] = pRes; - CloudServer.Statuses[DirPath] = 200; + CloudServer.Statuses[DirPath] = OK; /* saving query of current file */ - CloudServer.Queries[DirPath] = lQuery; + CloudServer.Queries[DirPath] = lQuery; Util.log(lQuery); console.log(DirPath); @@ -403,12 +404,13 @@ CloudServer._controller = function(pReq, pRes) /* читаем основные данные о файле */ if( lQuery && lQuery.indexOf('code=') === 0){ var lAuth = main.auth; - - if(lAuth) lAuth.auth(lQuery, function(){ + + if( Util.isFunction(lAuth) ) + lAuth(lQuery, function(){ Fs.stat(DirPath, CloudServer._stated); }); console.log('***********'); - }else + }else Fs.stat(DirPath, CloudServer._stated); /* если установлено сжатие @@ -418,13 +420,13 @@ CloudServer._controller = function(pReq, pRes) CloudServer.INDEX = (CloudServer.Minify._allowed.html ? '.' + CloudServer.Minify.MinFolder + 'index.min.html' : CloudServer.INDEX); - + /* * сохраним указатель на response * и на статус ответа */ CloudServer.Responses[CloudServer.INDEX] = pRes; - CloudServer.Statuses [CloudServer.INDEX] = 200; + CloudServer.Statuses [CloudServer.INDEX] = OK; } }; From 21e5dae5144838fa783bdc72bb8af319a78223d7 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 16 Nov 2012 06:44:27 -0500 Subject: [PATCH 173/281] setted up auth on GitHub --- ChangeLog | 2 + lib/client/storage/_github.js | 11 +++++ lib/server/auth.js | 6 +-- lib/server/rest.js | 79 +++++++++++++++++++++++++---------- server.js | 12 +----- 5 files changed, 76 insertions(+), 34 deletions(-) diff --git a/ChangeLog b/ChangeLog index d1ba1bb1..812a1317 100644 --- a/ChangeLog +++ b/ChangeLog @@ -94,6 +94,8 @@ when CodeMirror is open on the right panel. * Function generateHeaders moved to main module. +* Setted up auth on GitHub thru rest. + 2012.10.01, Version 0.1.7 diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index 42359d26..2bbfb2bb 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -69,6 +69,16 @@ var CloudCommander, Util, DOM, $, Github; if ( Util.isContainStr(lCode, '?code=') ){ lCode = lCode.replace('?code=',''); + + $.ajax( + {type:'put', + url:'/api/v1/auth', + data: lCode, + success: function(pDate){ + console.log(pDate);} + }); + + /* $.post("https://github.com/login/oauth/access_token",{ client_id : CLIENT_ID, client_secret : CLIENT_SECRET, @@ -80,6 +90,7 @@ var CloudCommander, Util, DOM, $, Github; //GithubStore.Login(lToken); console.log(pDate); }, "json"); + */ } else cloudcmd.Auth(); diff --git a/lib/server/auth.js b/lib/server/auth.js index 7b43bc4c..0e808144 100644 --- a/lib/server/auth.js +++ b/lib/server/auth.js @@ -25,15 +25,15 @@ * @param pCallBack */ - exports.auth = function(pCode, pCallBack){ - pCode = pCode.replace('code=',''); + exports.auth = function(pCode, pCallBack){ + pCode = pCode.replace('code=',''); console.log(pCode); authenticate(pCode, function(token) { var result = { "token": token }; console.log(result); - Util.exec(pCallBack); + Util.exec(pCallBack, result); }); }; diff --git a/lib/server/rest.js b/lib/server/rest.js index 3b628e5a..c70955f9 100644 --- a/lib/server/rest.js +++ b/lib/server/rest.js @@ -22,12 +22,20 @@ lMethod = lReq.method; if( Util.isContainStr(lUrl, APIURL) ){ - var lCommand = Util.removeStr(lUrl, APIURL), - lData = getData(lMethod, lCommand); - - send(lRes, lData); - lRet = true; + + getBody(lReq, function(pBody){ + var lCommand = Util.removeStr(lUrl, APIURL), + lData = getData({ + command : lCommand, + method : lMethod, + body : pBody, + response : lRes + }); + + if(lData) + send(lRes, lData); + }); } return lRet; @@ -42,25 +50,31 @@ */ function send(pRes, pData){ pRes.writeHead(OK, Header); - pRes.end( pData.toString() ); + pRes.end( JSON.stringify(pData) ); } /** * getting data on method and command * - * @param pMethod - * @param pCommand + * @param pParams {command, method, body, response} */ - function getData(pMethod, pCommand){ - var lResult; + function getData(pParams){ + var lResult, + lCmd = pParams.command, + lMethod = pParams.method; - switch(pMethod){ + if(lCmd[0] === '/'){ + lCmd = Util.removeStr(lCmd, '/'); + pParams.command = lCmd; + } + + switch(lMethod){ case 'GET': - lResult = onGET(pCommand); + lResult = onGET(pParams); break; case 'PUT': - lResult = onPUT(pCommand); + lResult = onPUT(pParams); break; } @@ -70,15 +84,18 @@ /** * process data on GET request * - * @param pCommand + * @param pParams {command, method, body, response} */ - function onGET(pCommand){ - var lResult; + function onGET(pParams){ + var lResult, + lCmd = pParams.command; - switch(pCommand){ + switch(lCmd){ case '': lResult = {info: 'Cloud Commander API v1'}; break; + case 'kill': + process.exit(); } return lResult; @@ -87,15 +104,35 @@ /** * process data on PUT request * - * @param pCommand + * @param pParams {command, method, body, response} */ - function onPUT(pCommand){ - var lResult; + function onPUT(pParams){ + var lResult, + lCmd = pParams.command, + lBody = pParams.body, + lRes = pParams.response; - switch(pCommand){ + switch(lCmd){ + case 'auth': + main.auth(lBody, function(pTocken){ + send(lRes, pTocken); + }); + lResult = false; + break; } return lResult; } + function getBody(pReq, pCallBack){ + var lBody = ''; + pReq.on('data', function(chunk) { + lBody += chunk.toString(); + }); + + pReq.on('end', function() { + Util.exec(pCallBack, lBody); + }); + } + })(); diff --git a/server.js b/server.js index 31c349c9..0ed31905 100644 --- a/server.js +++ b/server.js @@ -401,17 +401,9 @@ CloudServer._controller = function(pReq, pRes) console.log(DirPath); + /* читаем основные данные о файле */ - if( lQuery && lQuery.indexOf('code=') === 0){ - var lAuth = main.auth; - - if( Util.isFunction(lAuth) ) - lAuth(lQuery, function(){ - Fs.stat(DirPath, CloudServer._stated); - }); - console.log('***********'); - }else - Fs.stat(DirPath, CloudServer._stated); + Fs.stat(DirPath, CloudServer._stated); /* если установлено сжатие * меняем название html-файла и From ef1bc4133c71bff4916dfe480a85fa64c3b76e97 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 16 Nov 2012 08:11:02 -0500 Subject: [PATCH 174/281] minor changes --- lib/client/storage/_github.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index 2bbfb2bb..2733df08 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -69,13 +69,14 @@ var CloudCommander, Util, DOM, $, Github; if ( Util.isContainStr(lCode, '?code=') ){ lCode = lCode.replace('?code=',''); - - $.ajax( - {type:'put', + $.ajax({ + type:'put', url:'/api/v1/auth', data: lCode, - success: function(pDate){ - console.log(pDate);} + success: function(pData){ + if(pData) + GithubStore.Login(pData.token); + } }); /* From 5b64662696701a70d4292bdee4682071414931d5 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 16 Nov 2012 08:59:08 -0500 Subject: [PATCH 175/281] minor changes --- lib/client/storage/_github.js | 31 +++++++++++++++++++++++++---- lib/client/storage/github/github.js | 4 +++- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index 2733df08..c4c59b3f 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -74,10 +74,15 @@ var CloudCommander, Util, DOM, $, Github; url:'/api/v1/auth', data: lCode, success: function(pData){ - if(pData) - GithubStore.Login(pData.token); + var lToken = pData.token; + + if(pData){ + GithubStore.Login(lToken); + + getUserData(lToken); } - }); + } + }); /* $.post("https://github.com/login/oauth/access_token",{ @@ -103,7 +108,25 @@ var CloudCommander, Util, DOM, $, Github; CLIENT_ID + '&&scope=repo,user,gist'; }; - cloudcmd.Storage.Keys = function(){ + function getUserData(pToken){ + var lHelloFunc = function(pData){ + var lName; + if(pData) + lName = ' ' + pData.name ; + + console.log('Hello' + lName + ' :)!'); + }; + + if(pToken) + $.ajax({ + url : 'https://api.github.com/user?access_token=' + pToken, + success : lHelloFunc + }); + else + lHelloFunc(); + } + + cloudcmd.Storage.Keys = function(){ DOM.jqueryLoad( Util.retLoadOnLoad([ init, setConfig, diff --git a/lib/client/storage/github/github.js b/lib/client/storage/github/github.js index 24764580..68b570ef 100644 --- a/lib/client/storage/github/github.js +++ b/lib/client/storage/github/github.js @@ -80,7 +80,9 @@ // ------- this.show = function(username, cb) { - _request("GET", "/users/"+username, null, function(err, res) { + var command = username ? "/users/"+username : "/user"; + + _request("GET", command, null, function(err, res) { cb(err, res); }); }; From d1597239c7a8502670f74fcd2ad29515a3bdaa88 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Sun, 18 Nov 2012 05:09:07 -0500 Subject: [PATCH 176/281] fixed bug with context menu. Now it disabled before load menu module to --- ChangeLog | 6 +++++- client.js | 26 +++++++++++++++++++------- lib/client/menu.js | 11 ++--------- lib/client/storage/_github.js | 22 ++++------------------ server.js | 10 +++++----- 5 files changed, 35 insertions(+), 40 deletions(-) diff --git a/ChangeLog b/ChangeLog index 812a1317..2136ace2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -90,12 +90,16 @@ dom.js and util.js. * Fixed bug with keys panel and fm bottom margin, when CodeMirror is open on the right panel. -* Fixed bug with positioning of CodeMirror on the right panel. +* Fixed bug with positioning of CodeMirror on the +right panel. * Function generateHeaders moved to main module. * Setted up auth on GitHub thru rest. +* Fixed bug with context menu. Now it disabled +before load menu module to. + 2012.10.01, Version 0.1.7 diff --git a/client.js b/client.js index e4894d26..7191b9b5 100644 --- a/client.js +++ b/client.js @@ -7,7 +7,7 @@ var Util, DOM, CloudCommander = (function(){ "use strict"; /* Клиентский обьект, содержащий функциональную часть*/ -var CloudClient = { +var CloudClient = { /* Конструктор CloudClient, который выполняет * весь функционал по инициализации */ @@ -109,7 +109,7 @@ var loadModule = function(pParams){ }; }; -/* +/** * Обьект для работы с кэшем * в него будут включены функции для * работы с LocalStorage, webdb, @@ -298,7 +298,8 @@ CloudClient._editFileName = function(pParent){ } }; -/* Функция устанавливает текущим файлом, тот +/** + * Функция устанавливает текущим файлом, тот * на который кликнули единожды */ CloudClient._setCurrent = function(){ @@ -419,12 +420,23 @@ function initModules(pCallBack){ DOM.ajax({ url:'/modules.json', success: function(pModules){ - var lFunc = Util.retFunc( DOM.Images.showLoad ), - lDoBefore = { - 'viewer' : lFunc, - "editor/_codemirror" : lFunc + var lShowLoadFunc = Util.retFunc( DOM.Images.showLoad ), + lDisableMenuFunc = function(){ + var lFunc = document.oncontextmenu; + document.oncontextmenu = function(){ + Util.exec(lFunc); + return cloudcmd.Menu.ENABLED || false; + }; + }, + + lDoBefore = { + 'viewer' : lShowLoadFunc, + 'editor/_codemirror' : lShowLoadFunc, + 'menu' : lDisableMenuFunc }; + lDisableMenuFunc(); + if( Util.isArray(pModules) ) for(var i = 0, n = pModules.length; i < n ; i++){ var lModule = pModules[i]; diff --git a/lib/client/menu.js b/lib/client/menu.js index 9585bcdf..297ada83 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -11,10 +11,10 @@ var CloudCommander, Util, DOM, CloudFunc, $; Menu = {}, Position; - Menu.dir = './lib/client/menu/'; + Menu.dir = './lib/client/menu/'; /* enable and disable menu constant */ - Menu.ENABLED = false; + Menu.ENABLED = false; /* PRIVATE FUNCTIONS */ @@ -204,13 +204,6 @@ var CloudCommander, Util, DOM, CloudFunc, $; Menu.show, load ])); - - var lFunc = document.oncontextmenu; - document.oncontextmenu = function(){ - Util.exec(lFunc); - return Menu.ENABLED; - }; - var key_event = (function(pEvent){ /* если клавиши можно обрабатывать */ diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index c4c59b3f..b76aedb6 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -6,8 +6,8 @@ var CloudCommander, Util, DOM, $, Github; var cloudcmd = CloudCommander, - CLIENT_ID, // = '891c251b925e4e967fa9', - CLIENT_SECRET,// = 'afe9bed1e810c5dc44c4c2a953fc6efb1e5b0545', + CLIENT_ID, + CLIENT_SECRET, GithubStore = {}; cloudcmd.Storage = {}; @@ -83,21 +83,7 @@ var CloudCommander, Util, DOM, $, Github; } } }); - - /* - $.post("https://github.com/login/oauth/access_token",{ - client_id : CLIENT_ID, - client_secret : CLIENT_SECRET, - code : lCode, - state : '' - }, - - function(pDate){ - //GithubStore.Login(lToken); - console.log(pDate); - }, "json"); - */ - } + } else cloudcmd.Auth(); } @@ -134,5 +120,5 @@ var CloudCommander, Util, DOM, $, Github; ])); }; - cloudcmd.Storage.Github = GithubStore; + cloudcmd.Storage.GithubStore = GithubStore; })(); diff --git a/server.js b/server.js index 0ed31905..092cd45f 100644 --- a/server.js +++ b/server.js @@ -105,10 +105,10 @@ CloudServer.Socket = main.socket; /* базовая инициализация */ CloudServer.init = (function(pAppCachProcessing){ - var lConfig = this.Config, - lMinify = this.Minify, - lCache = this.Cache, - lAppCache = this.AppCache; + var lConfig = this.Config, + lMinify = this.Minify, + lCache = this.Cache, + lAppCache = this.AppCache; /* Переменная в которой храниться кэш*/ lCache.setAllowed(lConfig.cache.allowed); @@ -121,7 +121,7 @@ CloudServer.init = (function(pAppCachProcessing){ /* Если нужно минимизируем скрипты */ lMinify._allowed = lMinify.doit(); - /* создаём файл app cache */ + /* создаём файл app cache */ if( lConfig.appcache && lAppCache && lConfig.server ) Util.exec( pAppCachProcessing ); }); From 515991429137b576782415f1614403ec8e8c164e Mon Sep 17 00:00:00 2001 From: coderaiser Date: Sun, 18 Nov 2012 07:36:16 -0500 Subject: [PATCH 177/281] throw out jquery from github module, moved Cache object to client --- ChangeLog | 4 + client.js | 92 +++-------------------- lib/client/dom.js | 78 +++++++++++++++++--- lib/client/storage/_github.js | 133 ++++++++++++++++++++-------------- 4 files changed, 157 insertions(+), 150 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2136ace2..7918c960 100644 --- a/ChangeLog +++ b/ChangeLog @@ -100,6 +100,10 @@ right panel. * Fixed bug with context menu. Now it disabled before load menu module to. +* Throw out jquery from github module, moved Cache +object from client to DOM module, +refactored Cache object and added polyfill. + 2012.10.01, Version 0.1.7 diff --git a/client.js b/client.js index 7191b9b5..8755285d 100644 --- a/client.js +++ b/client.js @@ -28,8 +28,6 @@ var CloudClient = { * загружает содержимое каталогов */ /* ОБЬЕКТЫ */ - /* Обьект для работы с кэшем */ - Cache : {}, /* ПРИВАТНЫЕ ФУНКЦИИ */ /* функция загружает json-данные о файловой системе */ @@ -109,78 +107,6 @@ var loadModule = function(pParams){ }; }; -/** - * Обьект для работы с кэшем - * в него будут включены функции для - * работы с LocalStorage, webdb, - * indexed db etc. - */ -CloudClient.Cache = { - _allowed : true, /* приватный переключатель возможности работы с кэшем */ - - /* функция проверяет возможно ли работать с кэшем каким-либо образом */ - isAllowed : function(){}, - - /* Тип кэша, который доступен*/ - type : {}, - - /* Функция устанавливает кэш, если выбранный вид поддерживаеться браузером*/ - set :function(){}, - - /* Функция достаёт кэш, если выбранный вид поддерживаеться браузером*/ - get : function(){}, - - /* функция чистит весь кэш для всех каталогов*/ - clear : function(){} -}; - - -/** функция проверяет поддерживаеться ли localStorage */ -CloudClient.Cache.isAllowed = (function(){ - if(window.localStorage && - localStorage.setItem && - localStorage.getItem){ - CloudClient.Cache._allowed=true; - }else - { - CloudClient.Cache._allowed=false; - /* загружаем PolyFill для localStorage, - * если он не поддерживаеться браузером - * https://gist.github.com/350433 - */ - /* - DOM.jsload('https://raw.github.com/gist/350433/c9d3834ace63e5f5d7c8e1f6e3e2874d477cb9c1/gistfile1.js', - function(){CloudClient.Cache._allowed=true; - }); - */ - } -}); - - /** если доступен localStorage и - * в нём есть нужная нам директория - - * записываем данные в него - */ -CloudClient.Cache.set = function(pName, pData){ - if(CloudClient.Cache._allowed && pName && pData){ - localStorage.setItem(pName,pData); - } -}; - -/** Если доступен Cache принимаем из него данные*/ -CloudClient.Cache.get = function(pName){ - if(CloudClient.Cache._allowed && pName){ - return localStorage.getItem(pName); - } - else return null; -}; - -/** Функция очищает кэш */ -CloudClient.Cache.clear = function(){ - if(CloudClient.Cache._allowed){ - localStorage.clear(); - } -}; - CloudClient.GoogleAnalytics = function(){ /* google analytics */ var lFunc = document.onmousemove; @@ -480,7 +406,7 @@ function initKeysPanel(pCallBack){ } function baseInit(pCallBack){ - if(applicationCache){ + if(applicationCache){ var lFunc = applicationCache.onupdateready; applicationCache.onupdateready = function(){ @@ -508,10 +434,10 @@ function baseInit(pCallBack){ cloudcmd._changeLinks(CloudFunc.RIGHTPANEL); /* устанавливаем переменную доступности кэша */ - cloudcmd.Cache.isAllowed(); + DOM.Cache.isAllowed(); /* Устанавливаем кэш корневого каталога */ - if(!cloudcmd.Cache.get('/')) - cloudcmd.Cache.set('/', cloudcmd._getJSONfromFileTable()); + if( !DOM.Cache.get('/') ) + DOM.Cache.set('/', cloudcmd._getJSONfromFileTable()); }); /* устанавливаем размер высоты таблицы файлов @@ -536,7 +462,7 @@ function baseInit(pCallBack){ var lHeight = window.screen.height; lHeight = lHeight - (lHeight/3).toFixed(); - lHeight = (lHeight/100).toFixed()*100; + lHeight = (lHeight / 100).toFixed() * 100; cloudcmd.HEIGHT = lHeight; @@ -569,7 +495,7 @@ CloudClient._changeLinks = function(pPanelID){ /* назначаем кнопку очистить кэш и показываем её */ var lClearcache = getById('clear-cache'); if(lClearcache) - lClearcache.onclick = CloudClient.Cache.clear; + lClearcache.onclick = DOM.Cache.clear; /* меняем ссылки на ajax-запросы */ var lPanel = getById(pPanelID), @@ -736,9 +662,9 @@ CloudClient._ajaxLoad = function(path, pNeedRefresh){ lError; if(pNeedRefresh === undefined && lPanel){ - var lJSON = CloudClient.Cache.get(lPath); + var lJSON = DOM.Cache.get(lPath); - if (lJSON !== null){ + if (lJSON){ /* переводим из текста в JSON */ if(window && !window.JSON){ lError = Util.tryCatchLog(function(){ @@ -784,7 +710,7 @@ CloudClient._ajaxLoad = function(path, pNeedRefresh){ * сохраняем их в кэше */ if(lJSON_s.length<50000) - CloudClient.Cache.set(lPath,lJSON_s); + DOM.Cache.set(lPath,lJSON_s); } }); }); diff --git a/lib/client/dom.js b/lib/client/dom.js index 92fe7211..93d1e810 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -119,22 +119,21 @@ var CloudCommander, $, Util, DOM, CloudFunc; * load file countent thrue ajax */ DOM.ajax = function(pParams){ - /* if on webkit */ + var lType = pParams.type || 'GET', + lData = pParams.data, + lSuccess_f = pParams.success; + if(!XMLHTTP) - XMLHTTP = new XMLHttpRequest(); + XMLHTTP = new XMLHttpRequest(); - var lMethod = 'GET'; - if(pParams.method) - lMethod = pParams.method; + XMLHTTP.open(lType, pParams.url, true); + XMLHTTP.send(lData); - XMLHTTP.open(lMethod, pParams.url, true); - XMLHTTP.send(null); - - var lSuccess_f = pParams.success; if( !Util.isFunction(lSuccess_f) ) - console.log('error in DOM.ajax onSuccess:', pParams); + console.log('error in DOM.ajax onSuccess:', pParams) && + console.log(pParams); - XMLHTTP.onreadystatechange = function(pEvent){ + XMLHTTP.onreadystatechange = function(pEvent){ if (XMLHTTP.readyState === 4 /* Complete */){ var lJqXHR = pEvent.target, lType = XMLHTTP.getResponseHeader('content-type'); @@ -166,8 +165,63 @@ var CloudCommander, $, Util, DOM, CloudFunc; } } }; - }; + }; + + /** + * Обьект для работы с кэшем + * в него будут включены функции для + * работы с LocalStorage, webdb, + * indexed db etc. + */ + DOM.Cache = function(){ + /* приватный переключатель возможности работы с кэшем */ + var CacheAllowed, + Data = {}; + /* функция проверяет возможно ли работать с кэшем каким-либо образом */ + this.isAllowed = function(){ + return ( CacheAllowed = Util.isObject( window.localStorage ) ); + }; + + /** remove element */ + this.remove = function(pItem){ + return CacheAllowed ? + localStorage.removeItem(pItem) : + (delete Data[pItem]); + }; + + /** если доступен localStorage и + * в нём есть нужная нам директория - + * записываем данные в него + */ + this.set = function(pName, pData){ + var lRet; + + if(pName && pData) + lRet = CacheAllowed ? + localStorage.setItem(pName,pData) : + Data[pName] = pData; + + return lRet; + }, + + /** Если доступен Cache принимаем из него данные*/ + this.get = function(pName){ + return CacheAllowed ? + localStorage.getItem(pName) : + Data[pName]; + }, + + /** функция чистит весь кэш для всех каталогов*/ + this.clear = function(){ + return CacheAllowed ? + localStorage.clear() : + (Data = {}); + }; + }; + + DOM.Cache = new DOM.Cache(); + /** * Function gets id by src * @param pSrc diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index b76aedb6..be1b2396 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -1,4 +1,5 @@ -var CloudCommander, Util, DOM, $, Github; +var CloudCommander, Util, DOM, $, Github, cb; + /* temporary callback function for work with github */ /* module for work with github */ (function(){ @@ -6,12 +7,17 @@ var CloudCommander, Util, DOM, $, Github; var cloudcmd = CloudCommander, + API_URL = '/api/v1/auth', CLIENT_ID, - CLIENT_SECRET, + Cache = DOM.Cache, + GitHub, + User, GithubStore = {}; cloudcmd.Storage = {}; - + + cb = function (err, data){ console.log(err || data);} + /* PRIVATE FUNCTIONS */ /** @@ -38,19 +44,23 @@ var CloudCommander, Util, DOM, $, Github; cloudcmd.loadConfig(function(){ var lConfig = cloudcmd.Config; CLIENT_ID = lConfig.oauth_client_id; - CLIENT_SECRET = lConfig.oauth_client_secret; Util.exec(pCallBack); }); } - function callback(pError, pData){ - console.log(pError || pData); + function saveToken(pToken){ + return Cache.set('token', pToken); } + function getToken(){ + return Cache.get('token'); + } + + /* PUBLICK FUNCTIONS */ GithubStore.basicLogin = function(pUser, pPasswd){ - cloudcmd.Storage.Github = new Github({ + cloudcmd.Storage.Github = GitHub = new Github({ username: pUser, password: pPasswd, auth : 'basic' @@ -58,66 +68,79 @@ var CloudCommander, Util, DOM, $, Github; }; GithubStore.Login = function(pToken){ - cloudcmd.Storage.Github = new Github({ + cloudcmd.Storage.Github = Github = new Github({ token : pToken, auth : 'oauth' }); - }; - - function init(){ - var lCode = window.location.search; - if ( Util.isContainStr(lCode, '?code=') ){ - lCode = lCode.replace('?code=',''); - - $.ajax({ - type:'put', - url:'/api/v1/auth', - data: lCode, - success: function(pData){ - var lToken = pData.token; - - if(pData){ - GithubStore.Login(lToken); - - getUserData(lToken); - } - } - }); - } - else - cloudcmd.Auth(); - } - - cloudcmd.Auth = function(){ - window.location = - 'https://github.com/login/oauth/authorize?client_id=' + - CLIENT_ID + '&&scope=repo,user,gist'; + + User = Github.getUser(); }; - function getUserData(pToken){ - var lHelloFunc = function(pData){ - var lName; - if(pData) - lName = ' ' + pData.name ; - - console.log('Hello' + lName + ' :)!'); - }; + function init(pCallBack){ + var lToken = getToken(); + if(lToken){ + GithubStore.Login(lToken); + Util.exec(pCallBack); + } + else{ + var lCode = window.location.search; + if ( Util.isContainStr(lCode, '?code=') ){ + lCode = lCode.replace('?code=',''); + + DOM.ajax({ + type : 'put', + url : API_URL, + data: lCode, + success: function(pData){ + if(pData && pData.token){ + lToken = pData.token; + + GithubStore.Login(lToken); + saveToken(lToken); + Util.exec(pCallBack); + } + else + Util.log("Worning: token not getted..."); + } + }); + } + else + window.location = + 'https://github.com/login/oauth/authorize?client_id=' + + CLIENT_ID + '&&scope=repo,user,gist'; + } + } + + function getUserData(){ + var lShowRepos = function(pError, pRepos){ + Util.log('Repositories: '); + if(!pError) + for(var i = 0, n = pRepos.length; i < n ; i++) + console.log(pRepos[i].name); + else + DOM.Cache.remove('token'); + }, - if(pToken) - $.ajax({ - url : 'https://api.github.com/user?access_token=' + pToken, - success : lHelloFunc - }); - else - lHelloFunc(); + lShowUserInfo = function(pError, pData){ + if(!pError){ + console.log('Hello ' + pData.name + ' :)!'); + User.repos(lShowRepos); + } + else + DOM.Cache.remove('token'); + }; + + + User.show(null, lShowUserInfo); } cloudcmd.Storage.Keys = function(){ - DOM.jqueryLoad( Util.retLoadOnLoad([ + Util.loadOnLoad([ + getUserData, init, setConfig, load - ])); + ]); }; cloudcmd.Storage.GithubStore = GithubStore; From df873c0faf08b412118c5d4873eb20994d8c0f5d Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 19 Nov 2012 03:40:58 -0500 Subject: [PATCH 178/281] removed jitsu (not free anymore) and nodester (do not working) --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a4c512ab..a3efc6db 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ Cloud Commander [![Build Status](https://secure.travis-ci.org/coderaiser/cloudcmd.png?branch=master)](http://travis-ci.org/coderaiser/cloudcmd) =============== **Cloud Commander** - two-panels file manager, totally writed on js. -View demo on [jitsu](http://cloudcmd.jit.su/ "jitsu"), +DEMO: [cloudfoundry] (http://cloudcmd.cloudfoundry.com "cloudfoundry"), -[appfog] (http://cloudcmd.aws.af.cm "appfog"), -[nodester](http://cloudcmd.nodester.com/ "nodester"). +[appfog] (http://cloudcmd.aws.af.cm "appfog"). -Google PageSpeed Score : [100](https://developers.google.com/speed/pagespeed/insights#url=http_3A_2F_2Fdemo-cloudcmd.cloudfoundry.com_2F&mobile=false "score") (out of 100). +Google PageSpeed Score : [100](https://developers.google.com/speed/pagespeed/insights#url=http_3A_2F_2Fcloudcmd.cloudfoundry.com_2F&mobile=false "score") (out of 100) +(or 96 if js or css minification disabled in config.json). Benefits --------------- From 0d7c892cab7c39aef09427c988f402b5dbee94e6 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 19 Nov 2012 05:26:44 -0500 Subject: [PATCH 179/281] added ability to connect github development id to cloud cmd working on any host --- ChangeLog | 6 ++++++ lib/client/storage/_github.js | 16 ++++++++++++++-- lib/server/auth.js | 14 +++++++++++--- lib/server/rest.js | 6 ++++++ 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7918c960..431af874 100644 --- a/ChangeLog +++ b/ChangeLog @@ -104,6 +104,12 @@ before load menu module to. object from client to DOM module, refactored Cache object and added polyfill. +* Added ability to connect github development id, +to Cloud Commander instance working in any host, +for this all the needed to be done is: +set enveronment varibles "oauth_client_id" and +"oauth_client_secret" values from github profile. + 2012.10.01, Version 0.1.7 diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index be1b2396..ca1538fb 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -7,7 +7,9 @@ var CloudCommander, Util, DOM, $, Github, cb; var cloudcmd = CloudCommander, - API_URL = '/api/v1/auth', + APIURL = '/api/v1', + AuthURL = APIURL + '/auth', + ClientIdURL = APIURL + '/client_id', CLIENT_ID, Cache = DOM.Cache, GitHub, @@ -41,12 +43,22 @@ var CloudCommander, Util, DOM, $, Github, cb; } function setConfig(pCallBack){ + /* cloudcmd.loadConfig(function(){ var lConfig = cloudcmd.Config; CLIENT_ID = lConfig.oauth_client_id; Util.exec(pCallBack); }); + */ + + DOM.ajax({ + url : ClientIdURL, + success : function(pData){ + CLIENT_ID = pData; + Util.exec(pCallBack); + } + }); } function saveToken(pToken){ @@ -89,7 +101,7 @@ var CloudCommander, Util, DOM, $, Github, cb; DOM.ajax({ type : 'put', - url : API_URL, + url : AuthURL, data: lCode, success: function(pData){ if(pData && pData.token){ diff --git a/lib/server/auth.js b/lib/server/auth.js index 0e808144..4793de97 100644 --- a/lib/server/auth.js +++ b/lib/server/auth.js @@ -37,11 +37,19 @@ }); }; - function authenticate(pCode, pCallBack) { + var lId = Config.oauth_client_id, + lSecret = Config.oauth_client_secret, + lEnv = process.env, + + lClientId = lEnv.oauth_client_id || lId, + lClientSecret = lEnv.oauth_client_secret || lSecret; + + console.log(lEnv); + var data = qs.stringify({ - client_id : Config.oauth_client_id, - client_secret : Config.oauth_client_secret, + client_id : lClientId, + client_secret : lClientSecret, code : pCode }); diff --git a/lib/server/rest.js b/lib/server/rest.js index c70955f9..dd713770 100644 --- a/lib/server/rest.js +++ b/lib/server/rest.js @@ -96,6 +96,12 @@ break; case 'kill': process.exit(); + break; + case 'client_id': + var lEnv = process.env, + lConfig = main.config; + + lResult = lEnv.oauth_client_id || lConfig.oauth_client_id; } return lResult; From b6aceb4a82917e3230bd7d959b18359e04259537 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 19 Nov 2012 07:51:44 -0500 Subject: [PATCH 180/281] added ability to read trees from github --- lib/client/storage/_github.js | 33 ++++++++++++++++++++++++++------- lib/server/auth.js | 4 +--- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index ca1538fb..f1219223 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -12,7 +12,7 @@ var CloudCommander, Util, DOM, $, Github, cb; ClientIdURL = APIURL + '/client_id', CLIENT_ID, Cache = DOM.Cache, - GitHub, + GithubLocal, User, GithubStore = {}; @@ -72,7 +72,7 @@ var CloudCommander, Util, DOM, $, Github, cb; /* PUBLICK FUNCTIONS */ GithubStore.basicLogin = function(pUser, pPasswd){ - cloudcmd.Storage.Github = GitHub = new Github({ + cloudcmd.Storage.Github = GithubLocal = new Github({ username: pUser, password: pPasswd, auth : 'basic' @@ -80,12 +80,12 @@ var CloudCommander, Util, DOM, $, Github, cb; }; GithubStore.Login = function(pToken){ - cloudcmd.Storage.Github = Github = new Github({ + cloudcmd.Storage.Github = GithubLocal = new Github({ token : pToken, auth : 'oauth' }); - User = Github.getUser(); + User = GithubLocal.getUser(); }; function init(pCallBack){ @@ -124,18 +124,37 @@ var CloudCommander, Util, DOM, $, Github, cb; } function getUserData(){ - var lShowRepos = function(pError, pRepos){ - Util.log('Repositories: '); + var lName, lRepoNames, + lGetTree = function(pError ,pData){ + Util.log('Tree of ripository ' + lRepoNames[0].name + ': '); + var lTree = pData || []; if(!pError) + for(var i = 0, n = lTree.length; i < n ; i++) + if( !Util.isContainStr(lTree[i].path, '/') ) + console.log(lTree[i].path); + else + Util.log(pError); + }, + + lShowRepos = function(pError, pRepos){ + lRepoNames = pRepos || []; + Util.log('Repositories: '); + if(!pError){ for(var i = 0, n = pRepos.length; i < n ; i++) console.log(pRepos[i].name); + + var lRepo = GithubLocal.getRepo(lName, pRepos[0].name); + lRepo.getTree('master?recursive=true', lGetTree); + } else DOM.Cache.remove('token'); }, lShowUserInfo = function(pError, pData){ if(!pError){ - console.log('Hello ' + pData.name + ' :)!'); + lName = pData.name; + + console.log('Hello ' + lName + ' :)!'); User.repos(lShowRepos); } else diff --git a/lib/server/auth.js b/lib/server/auth.js index 4793de97..025e9ee6 100644 --- a/lib/server/auth.js +++ b/lib/server/auth.js @@ -44,9 +44,7 @@ lClientId = lEnv.oauth_client_id || lId, lClientSecret = lEnv.oauth_client_secret || lSecret; - - console.log(lEnv); - + var data = qs.stringify({ client_id : lClientId, client_secret : lClientSecret, From dea57e091bc4bf548b7b4b6ca640ecce9960d58a Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 19 Nov 2012 09:34:58 -0500 Subject: [PATCH 181/281] fixed styles --- css/reset.css | 19 ++++++++++++++----- css/style.css | 14 +++++++------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/css/reset.css b/css/reset.css index 4311d51e..199fee29 100644 --- a/css/reset.css +++ b/css/reset.css @@ -7,7 +7,13 @@ * 2. Prevent iOS text size adjust on device orientation change, without disabling user zoom: h5bp.com/g */ -html { font-size: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; font-family: sans-serif; color: #222;} +html{ + font-family: sans-serif; + font-size: 100%; + color: #222; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} body { margin: 0; font-size: 1em; line-height: 1.4; } /* @@ -26,7 +32,10 @@ body { margin: 0; font-size: 1em; line-height: 1.4; } Links ========================================================================== */ -a {text-decoration:none; color: #00e; } +a{ + color: #00e; + text-decoration:none; +} a:visited { color: #551a8b; } a:hover { color: #06e; } a:focus { outline: thin dotted; } @@ -37,11 +46,11 @@ a:hover, a:active { outline: 0; } /* changed ul to panel, it using only in this case for now, and will css * processing cost 33% with name of tag, so it should be match faster */ -.panel{ +.panel{ /* removed default margins */ - margin: 0; padding: 20px; - } + margin: 0; +} /* * 1. Display hand cursor for clickable form elements diff --git a/css/style.css b/css/style.css index d64dc79c..864a0a76 100644 --- a/css/style.css +++ b/css/style.css @@ -80,9 +80,9 @@ body{ .error::before{ position: relative; bottom : 4px; - content:'\f026'; + color:rgb(222, 41, 41); cursor:default; - color:rgb(222, 41, 41); + content:'\f026'; } .loading{ position:relative; @@ -102,22 +102,22 @@ body{ } .cmd-button{ - border: 1.5px solid rgba(49,123,249,.40); - background-color: white; - color: rgb(49,123,249); + width: 10%; margin: 20px 2px 0 2px; overflow: hidden; + color: rgb(49,123,249); text-overflow: ellipsis; - width: 10%; white-space: nowrap; + background-color: white; + border: 1.5px solid rgba(49,123,249,.40); } .cmd-button:hover{ border: 1.5px solid rgb(0,0,0); } .cmd-button:active{ - background-color: rgb(49,123,249); color: white; + background-color: rgb(49,123,249); } .clear-cache{ From aa89ff109df288b86c0666d69254a858fdeacc57 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 19 Nov 2012 10:31:45 -0500 Subject: [PATCH 182/281] little cleaning --- client.js | 25 ++++++--------- lib/client/keyBinding.js | 67 +++++++++++++++++----------------------- 2 files changed, 39 insertions(+), 53 deletions(-) diff --git a/client.js b/client.js index 8755285d..37299bfa 100644 --- a/client.js +++ b/client.js @@ -236,9 +236,10 @@ CloudClient._setCurrent = function(){ */ return function(pFromEnter){ var lCurrentFile = DOM.getCurrentFile(); - if(lCurrentFile){ - if (DOM.isCurrentFile(this) && - !Util.isBoolean(pFromEnter)){ + if(lCurrentFile){/* устанавливаем курсор на файл, на который нажали */ + DOM.setCurrentFile(this); + //if (DOM.isCurrentFile(this) && + // !Util.isBoolean(pFromEnter)){ //var lParent = this; //setTimeout(function(){ @@ -251,11 +252,7 @@ CloudClient._setCurrent = function(){ // if(DOM.getCurrentFile() === lParent) // CloudClient._editFileName(lParent); // },1000); - } - else{ - /* устанавливаем курсор на файл, на который нажали */ - DOM.setCurrentFile(this); - } + //} } /* если мы попали сюда с энтера */ if(pFromEnter===true){ @@ -788,8 +785,8 @@ CloudClient._getJSONfromFileTable = function(){ var lIsDir = lAttr['mini-icon directory'] ? true : false, lName = lAttr.name; - lName && - (lName = lName.getElementsByTagName('a')); + if(lName) + lName = DOM.getByTag(lName, 'a'); /* if found link to folder * cheking is it a full name @@ -798,12 +795,10 @@ CloudClient._getJSONfromFileTable = function(){ /* if short we got title * if full - getting textConent */ - lName.length && - (lName = lName[0]); + if(lName.length) + lName = lName[0]; - lName.title && - (lName = lName.title) || - (lName = lName.textContent); + lName = lName.title || lName.textContent; /* если это папка - выводим слово dir вместо размера*/ var lSize = lIsDir ? 'dir' : lAttr.size.textContent, diff --git a/lib/client/keyBinding.js b/lib/client/keyBinding.js index d32f17cf..df9c3a92 100644 --- a/lib/client/keyBinding.js +++ b/lib/client/keyBinding.js @@ -110,10 +110,6 @@ var CloudCommander, Util, DOM; }); event.preventDefault();//запрет на дальнейшее действие - } - /* if f2 pressed */ - else if(lKeyCode === KEY.F2){ - } else if(lKeyCode === KEY.Delete) DOM.removeCurrent(lCurrentFile); @@ -234,7 +230,7 @@ var CloudCommander, Util, DOM; /* если нажали клавишу page up * проматываем экран */ - else if(lKeyCode === KEY.PAGE_UP){ + else if(lKeyCode === KEY.PAGE_UP){ DOM.getPanel().scrollByPages(-1); var lC = lCurrentFile, @@ -248,13 +244,13 @@ var CloudCommander, Util, DOM; }); }; - for(i=0; i<30; i++){ + for(i = 0; i < 30; i++){ if(!lC.previousSibling || tryCatch(lC) ) break; - + lC = lC.previousSibling; } DOM.setCurrentFile(lC); - + event.preventDefault();//запрет на дальнейшее действие } @@ -262,7 +258,7 @@ var CloudCommander, Util, DOM; else if(lKeyCode === KEY.ENTER){ /* если ненайдены выделенные файлы - выходим*/ if(!lCurrentFile)return; - + /* из него достаём спан с именем файла*/ lName = DOM.getByClass('name', lCurrentFile); @@ -301,9 +297,10 @@ var CloudCommander, Util, DOM; */ else if(lKeyCode === KEY.R && event.ctrlKey){ - console.log('+r pressed'); - console.log('reloading page...'); - console.log('press +q to remove all key-handlers'); + console.log('+r pressed\n' + + 'reloading page...\n' + + 'press +q to remove all key-handlers'); + /* Программно нажимаем на кнопку перезагрузки * содержимого каталога */ @@ -326,14 +323,14 @@ var CloudCommander, Util, DOM; /* если нажали +d чистим кэш */ else if(lKeyCode === KEY.D && event.ctrlKey){ - console.log('+d pressed'); - console.log('clearing cache...'); - console.log('press +q to remove all key-handlers'); - + console.log('+d pressed\n' + + 'clearing cache...\n' + + 'press +q to remove all key-handlers'); + var lClearCache = DOM.getById('clear-cache'); if(lClearCache && lClearCache.onclick) lClearCache.onclick(); - + event.preventDefault();//запрет на дальнейшее действие } @@ -341,18 +338,16 @@ var CloudCommander, Util, DOM; * убираем все обработчики * нажатий клавиш */ - else if(lKeyCode === KEY.Q && - event.altKey){ - //document.removeEventListener('keydown', key_event,false); - console.log('+q pressed'); - console.log('+r reload key-handerl - removed'); - console.log('+s clear cache key-handler - removed'); - console.log('press +s to to set them'); - - /* обработчик нажатий клавиш снят*/ - keyBinded = false; - - event.preventDefault();//запрет на дальнейшее действие + else if(lKeyCode === KEY.Q && event.altKey){ + console.log('+q pressed\n' + + '+r reload key-handerl - removed' + + '+s clear cache key-handler - removed'+ + 'press +s to to set them'); + + /* обработчик нажатий клавиш снят*/ + keyBinded = false; + + event.preventDefault();//запрет на дальнейшее действие } } @@ -364,21 +359,17 @@ var CloudCommander, Util, DOM; /* обрабатываем нажатия на клавиши*/ keyBinded = true; - console.log('+s pressed'); - console.log('+r reload key-handerl - set'); - console.log('+s clear cache key-handler - set'); - console.log('press +q to remove them'); + console.log('+s pressed\n' + + '+r reload key-handerl - set\n' + + '+s clear cache key-handler - set\n' + + 'press +q to remove them'); event.preventDefault();//запрет на дальнейшее действие } - - return false; }; /* добавляем обработчик клавишь */ - if(document.addEventListener) - document.addEventListener('keydown', key_event, false); - else document.onkeydown = key_event; + DOM.addKeyListener(key_event); /* клавиши назначены*/ keyBinded = true; From 23fbcf43f76f4c8000896891bce174e1d84a40a3 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 19 Nov 2012 11:02:12 -0500 Subject: [PATCH 183/281] minor changes --- test/test.sh | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) mode change 100644 => 100755 test/test.sh diff --git a/test/test.sh b/test/test.sh old mode 100644 new mode 100755 index 8529a6f9..209ecf5f --- a/test/test.sh +++ b/test/test.sh @@ -1,16 +1,15 @@ -#!/bin/sh -#linting js files -npm i jshint +set jshint = "../node_modules/jshint/bin/hint --config ./test/.jshintrc" echo "jshint server.js client.js cloudcmd.js" -./node_modules/jshint/bin/hint --config ./test/.jshintrc ./server.js ./client.js ./cloudcmd.js +jshint server.js client.js cloudcmd.js echo "jshint lib/cloudfunc.js lib/client/keyBinding.js" -./node_modules/jshint/bin/hint --config ./test/.jshintrc ./lib/cloudfunc.js ./node_modules/minify/minify.js ./lib/client/keyBinding.js +jshint ./lib/util.js ./lib/cloudfunc.js ./node_modules/minify/minify.js ./lib/client/keyBinding.js +jshint ./lib/client/dom.js ./lib/client/ie.js ./lib/client/menu.js ./lib/client/socket.js ./lib/client/terminal.js ./lib/client/viewer.js ./lib/client/storage/_github.js ./lib/client/menu.js ./lib/client/editor/_codemirror.js echo "jshint ./package.json ./config.json" -./node_modules/jshint/bin/hint --config ./test/.jshintrc ./package.json ./config.json +jshint ./package.json ./config.json #linting css files npm i recess echo "recess css/*.css" -./node_modules/recess/bin/recess css/*.css +./node_modules/recess/bin/recess ../css/*.css node ./test/test.js node cloudcmd.js test ls ./min \ No newline at end of file From 5cf8820fdb8f5fdf45dbe4d3b18cf3c89cbd05c0 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 20 Nov 2012 04:12:02 -0500 Subject: [PATCH 184/281] changed funcyBox version to 2.1.3. --- ChangeLog | 2 + client.js | 2 +- .../helpers/jquery.fancybox-buttons.js | 15 +- .../fancybox/helpers/jquery.fancybox-media.js | 12 +- .../helpers/jquery.fancybox-thumbs.js | 52 ++- .../viewer/fancybox/jquery.fancybox.css | 9 +- lib/client/viewer/fancybox/jquery.fancybox.js | 411 +++++++++++------- 7 files changed, 290 insertions(+), 213 deletions(-) diff --git a/ChangeLog b/ChangeLog index 431af874..1b1aaabf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -110,6 +110,8 @@ for this all the needed to be done is: set enveronment varibles "oauth_client_id" and "oauth_client_secret" values from github profile. +* Changed funcyBox version to 2.1.3. + 2012.10.01, Version 0.1.7 diff --git a/client.js b/client.js index 37299bfa..9047c6f5 100644 --- a/client.js +++ b/client.js @@ -786,7 +786,7 @@ CloudClient._getJSONfromFileTable = function(){ lName = lAttr.name; if(lName) - lName = DOM.getByTag(lName, 'a'); + lName = DOM.getByTag('a', lName); /* if found link to folder * cheking is it a full name diff --git a/lib/client/viewer/fancybox/helpers/jquery.fancybox-buttons.js b/lib/client/viewer/fancybox/helpers/jquery.fancybox-buttons.js index 7ba560c6..50baeca4 100644 --- a/lib/client/viewer/fancybox/helpers/jquery.fancybox-buttons.js +++ b/lib/client/viewer/fancybox/helpers/jquery.fancybox-buttons.js @@ -1,6 +1,6 @@ /*! * Buttons helper for fancyBox - * version: 1.0.3 + * version: 1.0.5 (Mon, 15 Oct 2012) * @requires fancyBox v2.0 or later * * Usage: @@ -12,10 +12,6 @@ * } * }); * - * Options: - * tpl - HTML template - * position - 'top' or 'bottom' - * */ (function ($) { //Shortcut for fancyBox object @@ -23,7 +19,12 @@ //Add helper object F.helpers.buttons = { - tpl : '
', + defaults : { + skipSingle : false, // disables if gallery contains single image + position : 'top', // 'top' or 'bottom' + tpl : '
' + }, + list : null, buttons: null, @@ -57,7 +58,7 @@ var buttons = this.buttons; if (!buttons) { - this.list = $(opts.tpl || this.tpl).addClass(opts.position || 'top').appendTo('body'); + this.list = $(opts.tpl).addClass(opts.position).appendTo('body'); buttons = { prev : this.list.find('.btnPrev').click( F.prev ), diff --git a/lib/client/viewer/fancybox/helpers/jquery.fancybox-media.js b/lib/client/viewer/fancybox/helpers/jquery.fancybox-media.js index d066294c..4b5e7835 100644 --- a/lib/client/viewer/fancybox/helpers/jquery.fancybox-media.js +++ b/lib/client/viewer/fancybox/helpers/jquery.fancybox-media.js @@ -1,6 +1,6 @@ /*! * Media helper for fancyBox - * version: 1.0.3 (Mon, 13 Aug 2012) + * version: 1.0.5 (Tue, 23 Oct 2012) * @requires fancyBox v2.0 or later * * Usage: @@ -37,6 +37,7 @@ * * Youtube * http://www.youtube.com/watch?v=opj24KnzrWo + * http://www.youtube.com/embed/opj24KnzrWo * http://youtu.be/opj24KnzrWo * Vimeo * http://vimeo.com/40648169 @@ -85,9 +86,9 @@ //Add helper object F.helpers.media = { - types : { + defaults : { youtube : { - matcher : /(youtube\.com|youtu\.be)\/(watch\?v=|v\/|u\/|embed)?([\w-]{11}|\?listType=(.*)&list=(.*)).*/i, + matcher : /(youtube\.com|youtu\.be)\/(watch\?v=|v\/|u\/|embed\/?)?(videoseries\?list=(.*)|[\w-]{11}|\?listType=(.*)&list=(.*)).*/i, params : { autoplay : 1, autohide : 1, @@ -108,7 +109,6 @@ show_title : 1, show_byline : 1, show_portrait : 0, - color : '', fullscreen : 1 }, type : 'iframe', @@ -170,8 +170,8 @@ rez, params; - for (what in this.types) { - item = this.types[ what ]; + for (what in opts) { + item = opts[ what ]; rez = url.match( item.matcher ); if (rez) { diff --git a/lib/client/viewer/fancybox/helpers/jquery.fancybox-thumbs.js b/lib/client/viewer/fancybox/helpers/jquery.fancybox-thumbs.js index df00ebe6..5db3d4ac 100644 --- a/lib/client/viewer/fancybox/helpers/jquery.fancybox-thumbs.js +++ b/lib/client/viewer/fancybox/helpers/jquery.fancybox-thumbs.js @@ -1,6 +1,6 @@ /*! * Thumbnail helper for fancyBox - * version: 1.0.6 + * version: 1.0.7 (Mon, 01 Oct 2012) * @requires fancyBox v2.0 or later * * Usage: @@ -13,12 +13,6 @@ * } * }); * - * Options: - * width - thumbnail width - * height - thumbnail height - * source - function to obtain the URL of the thumbnail image - * position - 'top' or 'bottom' - * */ (function ($) { //Shortcut for fancyBox object @@ -26,31 +20,35 @@ //Add helper object F.helpers.thumbs = { + defaults : { + width : 50, // thumbnail width + height : 50, // thumbnail height + position : 'bottom', // 'top' or 'bottom' + source : function ( item ) { // function to obtain the URL of the thumbnail image + var href; + + if (item.element) { + href = $(item.element).find('img').attr('src'); + } + + if (!href && item.type === 'image' && item.href) { + href = item.href; + } + + return href; + } + }, + wrap : null, list : null, width : 0, - //Default function to obtain the URL of the thumbnail image - source: function ( item ) { - var href; - - if (item.element) { - href = $(item.element).find('img').attr('src'); - } - - if (!href && item.type === 'image' && item.href) { - href = item.href; - } - - return href; - }, - init: function (opts, obj) { var that = this, list, - thumbWidth = opts.width || 50, - thumbHeight = opts.height || 50, - thumbSource = opts.source || this.source; + thumbWidth = opts.width, + thumbHeight = opts.height, + thumbSource = opts.source; //Build list structure list = ''; @@ -59,7 +57,7 @@ list += '
  • '; } - this.wrap = $('
    ').addClass(opts.position || 'bottom').appendTo('body'); + this.wrap = $('
    ').addClass(opts.position).appendTo('body'); this.list = $('
      ' + list + '
    ').appendTo(this.wrap); //Load each thumbnail @@ -125,7 +123,7 @@ } //Increase bottom margin to give space for thumbs - obj.margin[ opts.position === 'top' ? 0 : 2 ] += ((opts.height || 50) + 15); + obj.margin[ opts.position === 'top' ? 0 : 2 ] += ((opts.height) + 15); }, afterShow: function (opts, obj) { diff --git a/lib/client/viewer/fancybox/jquery.fancybox.css b/lib/client/viewer/fancybox/jquery.fancybox.css index b1d4c020..d6ff8a17 100644 --- a/lib/client/viewer/fancybox/jquery.fancybox.css +++ b/lib/client/viewer/fancybox/jquery.fancybox.css @@ -1,4 +1,4 @@ -/*! fancyBox v2.1.0 fancyapps.com | fancyapps.com/fancybox/#license */ +/*! fancyBox v2.1.3 fancyapps.com | fancyapps.com/fancybox/#license */ .fancybox-wrap, .fancybox-skin, .fancybox-outer, @@ -154,9 +154,12 @@ .fancybox-tmp { position: absolute; - top: -9999px; - left: -9999px; + top: -99999px; + left: -99999px; visibility: hidden; + max-width: 99999px; + max-height: 99999px; + overflow: visible !important; } /* Overlay helper */ diff --git a/lib/client/viewer/fancybox/jquery.fancybox.js b/lib/client/viewer/fancybox/jquery.fancybox.js index 911ca767..ee88e35a 100644 --- a/lib/client/viewer/fancybox/jquery.fancybox.js +++ b/lib/client/viewer/fancybox/jquery.fancybox.js @@ -1,6 +1,6 @@ /*! * fancyBox - jQuery Plugin - * version: 2.1.0 (Mon, 20 Aug 2012) + * version: 2.1.3 (Tue, 23 Oct 2012) * @requires jQuery v1.6 or later * * Examples at http://fancyapps.com/fancybox/ @@ -33,14 +33,14 @@ isScrollable = function(el) { return (el && !(el.style.overflow && el.style.overflow === 'hidden') && ((el.clientWidth && el.scrollWidth > el.clientWidth) || (el.clientHeight && el.scrollHeight > el.clientHeight))); }, - getScalar = function(value, dim) { - var value_ = parseInt(value, 10); + getScalar = function(orig, dim) { + var value = parseInt(orig, 10) || 0; - if (dim && isPercentage(value)) { - value_ = F.getViewport()[ dim ] / 100 * value_; + if (dim && isPercentage(orig)) { + value = F.getViewport()[ dim ] / 100 * value; } - return Math.ceil(value_); + return Math.ceil(value); }, getValue = function(value, dim) { return getScalar(value, dim) + 'px'; @@ -48,7 +48,7 @@ $.extend(F, { // The current version of fancyBox - version: '2.1.0', + version: '2.1.3', defaults: { padding : 15, @@ -65,7 +65,7 @@ autoHeight : false, autoWidth : false, - autoResize : !isTouch, + autoResize : true, autoCenter : !isTouch, fitToView : true, aspectRatio : false, @@ -136,7 +136,7 @@ tpl: { wrap : '
    ', image : '', - iframe : '', + iframe : '', error : '

    The requested content cannot be loaded.
    Please try again later.

    ', closeBtn : '', next : '', @@ -170,17 +170,10 @@ prevEasing : 'swing', prevMethod : 'changeOut', - // Enabled helpers + // Enable default helpers helpers : { - overlay : { - closeClick : true, - speedOut : 200, - showEarly : true, - css : {} - }, - title : { - type : 'float' // 'float', 'inside', 'outside' or 'over' - } + overlay : true, + title : true }, // Callbacks @@ -264,8 +257,8 @@ if (isQuery(element)) { obj = { - href : element.attr('href'), - title : element.attr('title'), + href : element.data('fancybox-href') || element.attr('href'), + title : element.data('fancybox-title') || element.attr('title'), isDom : true, element : element }; @@ -383,20 +376,20 @@ F.imgPreload.onload = F.imgPreload.onerror = null; } - // If the first item has been canceled, then clear everything if (coming.wrap) { - coming.wrap.stop(true).trigger('onReset').remove(); - } - - if (!F.current) { - F.trigger('afterClose'); + coming.wrap.stop(true, true).trigger('onReset').remove(); } F.coming = null; + + // If the first item has been canceled, then clear everything + if (!F.current) { + F._afterZoomOut( coming ); + } }, // Start closing animation if is open; remove immediately if opening/closing - close: function (immediately) { + close: function (event) { F.cancel(); if (false === F.trigger('beforeClose')) { @@ -405,7 +398,11 @@ F.unbindEvents(); - if (!F.isOpen || immediately === true) { + if (!F.isActive) { + return; + } + + if (!F.isOpen || event === true) { $('.fancybox-wrap').stop(true).trigger('onReset').remove(); F._afterZoomOut(); @@ -418,10 +415,6 @@ F.wrap.stop(true, true).removeClass('fancybox-opened'); - if (F.wrap.css('position') === 'fixed') { - F.wrap.css(F._getPosition( true )); - } - F.transitions[ F.current.closeMethod ](); } }, @@ -529,18 +522,22 @@ // Center inside viewport and toggle position type to fixed or absolute if needed reposition: function (e, onlyAbsolute) { - var pos; + var current = F.current, + wrap = current ? current.wrap : null, + pos; - if (F.isOpen) { + if (wrap) { pos = F._getPosition(onlyAbsolute); if (e && e.type === 'scroll') { delete pos.position; - F.wrap.stop(true, true).animate(pos, 200); + wrap.stop(true, true).animate(pos, 200); } else { - F.wrap.css(pos); + wrap.css(pos); + + current.pos = $.extend({}, current.dim, pos); } } }, @@ -559,23 +556,16 @@ return; } - // Help browser to restore document dimensions - if (anyway || isTouch) { - F.wrap.removeAttr('style').addClass('fancybox-tmp'); - - F.trigger('onUpdate'); - } - didUpdate = setTimeout(function() { var current = F.current; - if (!current) { + if (!current || F.isClosing) { return; } F.wrap.removeClass('fancybox-tmp'); - if (type !== 'scroll') { + if (anyway || type === 'load' || (type === 'resize' && current.autoResize)) { F._setDimension(); } @@ -587,7 +577,7 @@ didUpdate = null; - }, (isTouch ? 500 : (anyway ? 20 : 300))); + }, (anyway && !isTouch ? 0 : 300)); }, // Shrink content to fit inside viewport or restore if resized @@ -595,12 +585,19 @@ if (F.isOpen) { F.current.fitToView = $.type(action) === "boolean" ? action : !F.current.fitToView; + // Help browser to restore document dimensions + if (isTouch) { + F.wrap.removeAttr('style').addClass('fancybox-tmp'); + + F.trigger('onUpdate'); + } + F.update(); } }, hideLoading: function () { - D.unbind('keypress.fb'); + D.unbind('.loading'); $('#fancybox-loading').remove(); }, @@ -610,16 +607,17 @@ F.hideLoading(); + el = $('
    ').click(F.cancel).appendTo('body'); + // If user will press the escape-button, the request will be canceled - D.bind('keypress.fb', function(e) { + D.bind('keydown.loading', function(e) { if ((e.which || e.keyCode) === 27) { e.preventDefault(); + F.cancel(); } }); - el = $('
    ').click(F.cancel).appendTo('body'); - if (!F.defaults.fixed) { viewport = F.getViewport(); @@ -632,15 +630,15 @@ }, getViewport: function () { - var lock = F.current ? F.current.locked : false, - rez = { + var locked = (F.current && F.current.locked) || false, + rez = { x: W.scrollLeft(), y: W.scrollTop() }; - if (lock) { - rez.w = lock[0].clientWidth; - rez.h = lock[0].clientHeight; + if (locked) { + rez.w = locked[0].clientWidth; + rez.h = locked[0].clientHeight; } else { // See http://bugs.jquery.com/ticket/6724 @@ -671,7 +669,7 @@ // Changing document height on iOS devices triggers a 'resize' event, // that can change document height... repeating infinitely - W.bind('orientationchange.fb' + (isTouch ? '' : ' resize.fb' ) + (current.autoCenter && !current.locked ? ' scroll.fb' : ''), F.update); + W.bind('orientationchange.fb' + (isTouch ? '' : ' resize.fb') + (current.autoCenter && !current.locked ? ' scroll.fb' : ''), F.update); keys = current.keys; @@ -680,6 +678,11 @@ var code = e.which || e.keyCode, target = e.target || e.srcElement; + // Skip esc key if loading, because showLoading will cancel preloading + if (code === 27 && F.coming) { + return false; + } + // Ignore key combinations and key events within form elements if (!e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey && !(target && (target.type || $(target).is('[contenteditable]')))) { $.each(keys, function(i, val) { @@ -747,13 +750,11 @@ return false; } - if (event === 'onCancel' && !F.isOpened) { - F.isActive = false; - } - if (obj.helpers) { $.each(obj.helpers, function (helper, opts) { if (opts && F.helpers[helper] && $.isFunction(F.helpers[helper][event])) { + opts = $.extend(true, {}, F.helpers[helper].defaults, opts); + F.helpers[helper][event](opts, obj); } }); @@ -763,7 +764,7 @@ }, isImage: function (str) { - return isString(str) && str.match(/\.(jp(e|g|eg)|gif|png|bmp|webp)((\?|#).*)?$/i); + return isString(str) && str.match(/(^data:image\/.*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp)((\?|#).*)?$)/i); }, isSWF: function (str) { @@ -880,7 +881,7 @@ } // Build the neccessary markup - coming.wrap = $(coming.tpl.wrap).addClass('fancybox-' + (isTouch ? 'mobile' : 'desktop') + ' fancybox-type-' + type + ' fancybox-tmp ' + coming.wrapCSS).appendTo( coming.parent ); + coming.wrap = $(coming.tpl.wrap).addClass('fancybox-' + (isTouch ? 'mobile' : 'desktop') + ' fancybox-type-' + type + ' fancybox-tmp ' + coming.wrapCSS).appendTo( coming.parent || 'body' ); $.extend(coming, { skin : $('.fancybox-skin', coming.wrap), @@ -954,7 +955,7 @@ img.src = F.coming.href; - if (img.complete === undefined || !img.complete) { + if (img.complete !== true) { F.showLoading(); } }, @@ -1072,10 +1073,6 @@ previous.wrap.stop(true).removeClass('fancybox-opened') .find('.fancybox-item, .fancybox-nav') .remove(); - - if (previous.wrap.css('position') === 'fixed') { - previous.wrap.css(F._getPosition( true )); - } } F.unbindEvents(); @@ -1123,7 +1120,7 @@ break; case 'swf': - content = ''; + content = ''; embed = ''; $.each(current.swf, function(name, val) { @@ -1148,9 +1145,7 @@ // Set initial dimensions and start position F._setDimension(); - current.wrap.removeClass('fancybox-tmp'); - - current.pos = $.extend({}, current.dim, F._getPosition( true )); + F.reposition(); F.isOpen = false; F.coming = null; @@ -1187,8 +1182,8 @@ scrolling = current.scrolling, scrollOut = current.scrollOutside ? current.scrollbarWidth : 0, margin = current.margin, - wMargin = margin[1] + margin[3], - hMargin = margin[0] + margin[2], + wMargin = getScalar(margin[1] + margin[3]), + hMargin = getScalar(margin[0] + margin[2]), wPadding, hPadding, wSpace, @@ -1206,10 +1201,10 @@ body; // Reset dimensions so we could re-check actual size - wrap.add(skin).add(inner).width('auto').height('auto'); + wrap.add(skin).add(inner).width('auto').height('auto').removeClass('fancybox-tmp'); - wPadding = skin.outerWidth(true) - skin.width(); - hPadding = skin.outerHeight(true) - skin.height(); + wPadding = getScalar(skin.outerWidth(true) - skin.width()); + hPadding = getScalar(skin.outerHeight(true) - skin.height()); // Any space between content and viewport (margin, padding, border, title) wSpace = wMargin + wPadding; @@ -1277,43 +1272,52 @@ origMaxWidth = maxWidth; origMaxHeight = maxHeight; + if (current.fitToView) { + maxWidth = Math.min(viewport.w - wSpace, maxWidth); + maxHeight = Math.min(viewport.h - hSpace, maxHeight); + } + maxWidth_ = viewport.w - wMargin; maxHeight_ = viewport.h - hMargin; if (current.aspectRatio) { if (width > maxWidth) { width = maxWidth; - height = width / ratio; + height = getScalar(width / ratio); } if (height > maxHeight) { height = maxHeight; - width = height * ratio; + width = getScalar(height * ratio); } if (width < minWidth) { width = minWidth; - height = width / ratio; + height = getScalar(width / ratio); } if (height < minHeight) { height = minHeight; - width = height * ratio; + width = getScalar(height * ratio); } } else { - width = Math.max(minWidth, Math.min(width, maxWidth)); + width = Math.max(minWidth, Math.min(width, maxWidth)); + + if (current.autoHeight && current.type !== 'iframe') { + inner.width( width ); + + height = inner.height(); + } + height = Math.max(minHeight, Math.min(height, maxHeight)); } // Try to fit inside viewport (including the title) if (current.fitToView) { - maxWidth = Math.min(viewport.w - wSpace, maxWidth); - maxHeight = Math.min(viewport.h - hSpace, maxHeight); + inner.width( width ).height( height ); - inner.width( getScalar( width ) ).height( getScalar( height ) ); - - wrap.width( getScalar( width + wPadding ) ); + wrap.width( width + wPadding ); // Real wrap dimensions width_ = wrap.width(); @@ -1326,21 +1330,21 @@ } height = Math.max(minHeight, Math.min(maxHeight, height - 10)); - width = height * ratio; + width = getScalar(height * ratio); if (width < minWidth) { width = minWidth; - height = width / ratio; + height = getScalar(width / ratio); } if (width > maxWidth) { width = maxWidth; - height = width / ratio; + height = getScalar(width / ratio); } - inner.width( getScalar( width ) ).height( getScalar( height ) ); + inner.width( width ).height( height ); - wrap.width( getScalar( width + wPadding ) ); + wrap.width( width + wPadding ); width_ = wrap.width(); height_ = wrap.height(); @@ -1356,9 +1360,9 @@ width += scrollOut; } - inner.width( getScalar( width ) ).height( getScalar( height ) ); + inner.width( width ).height( height ); - wrap.width( getScalar( width + wPadding ) ); + wrap.width( width + wPadding ); width_ = wrap.width(); height_ = wrap.height(); @@ -1421,14 +1425,16 @@ F.isOpen = F.isOpened = true; - F.wrap.addClass('fancybox-opened').css('overflow', 'visible'); + F.wrap.css('overflow', 'visible').addClass('fancybox-opened'); - F.reposition(); + F.update(); // Assign a click event - if (current.closeClick || current.nextClick) { + if ( current.closeClick || (current.nextClick && F.group.length > 1) ) { F.inner.css('cursor', 'pointer').bind('click.fb', function(e) { if (!$(e.target).is('a') && !$(e.target).parent().is('a')) { + e.preventDefault(); + F[ current.closeClick ? 'close' : 'next' ](); } }); @@ -1436,7 +1442,11 @@ // Create a close button if (current.closeBtn) { - $(current.tpl.closeBtn).appendTo(F.skin).bind('click.fb', F.close); + $(current.tpl.closeBtn).appendTo(F.skin).bind( isTouch ? 'touchstart.fb' : 'click.fb', function(e) { + e.preventDefault(); + + F.close(); + }); } // Create navigation arrows @@ -1463,10 +1473,10 @@ } }, - _afterZoomOut: function () { - var current = F.current; + _afterZoomOut: function ( obj ) { + obj = obj || F.current; - $('.fancybox-wrap').stop(true).trigger('onReset').remove(); + $('.fancybox-wrap').trigger('onReset').remove(); $.extend(F, { group : {}, @@ -1483,7 +1493,7 @@ inner : null }); - F.trigger('afterClose', current); + F.trigger('afterClose', obj); } }); @@ -1524,7 +1534,7 @@ pos.left = viewport.x + (viewport.w - width) * current.leftRatio; } - if (current.locked) { + if (F.wrap.css('position') === 'fixed' || current.locked) { pos.top -= viewport.y; pos.left -= viewport.x; } @@ -1646,7 +1656,10 @@ F.wrap.css(startPos).animate(endPos, { duration : current.nextSpeed, easing : current.nextEasing, - complete : F._afterZoomIn + complete : function() { + // This helps FireFox to properly render the box + setTimeout(F._afterZoomIn, 20); + } }); } }, @@ -1677,9 +1690,90 @@ */ F.helpers.overlay = { - overlay: null, + defaults : { + closeClick : true, // if true, fancyBox will be closed when user clicks on the overlay + speedOut : 200, // duration of fadeOut animation + showEarly : true, // indicates if should be opened immediately or wait until the content is ready + css : {}, // custom CSS properties + locked : !isTouch, // if true, the content will be locked into overlay + fixed : true // if false, the overlay CSS position property will not be set to "fixed" + }, - update: function () { + overlay : null, // current handle + fixed : false, // indicates if the overlay has position "fixed" + + // Public methods + create : function(opts) { + opts = $.extend({}, this.defaults, opts); + + if (this.overlay) { + this.close(); + } + + this.overlay = $('
    ').appendTo( 'body' ); + this.fixed = false; + + if (opts.fixed && F.defaults.fixed) { + this.overlay.addClass('fancybox-overlay-fixed'); + + this.fixed = true; + } + }, + + open : function(opts) { + var that = this; + + opts = $.extend({}, this.defaults, opts); + + if (this.overlay) { + this.overlay.unbind('.overlay').width('auto').height('auto'); + + } else { + this.create(opts); + } + + if (!this.fixed) { + W.bind('resize.overlay', $.proxy( this.update, this) ); + + this.update(); + } + + if (opts.closeClick) { + this.overlay.bind('click.overlay', function(e) { + if ($(e.target).hasClass('fancybox-overlay')) { + if (F.isActive) { + F.close(); + } else { + that.close(); + } + } + }); + } + + this.overlay.css( opts.css ).show(); + }, + + close : function() { + $('.fancybox-overlay').remove(); + + W.unbind('resize.overlay'); + + this.overlay = null; + + if (this.margin !== false) { + $('body').css('margin-right', this.margin); + + this.margin = false; + } + + if (this.el) { + this.el.removeClass('fancybox-lock'); + } + }, + + // Private, callbacks + + update : function () { var width = '100%', offsetWidth; // Reset width/height so it will not mess @@ -1701,25 +1795,19 @@ }, // This is where we can manipulate DOM, because later it would cause iframes to reload - onReady: function (opts, obj) { + onReady : function (opts, obj) { $('.fancybox-overlay').stop(true, true); if (!this.overlay) { - $.extend(this, { - overlay : $('
    ').appendTo( obj.parent ), - margin : D.height() > W.height() || $('body').css('overflow-y') === 'scroll' ? $('body').css('margin-right') : false, - el : document.all && !document.querySelector ? $('html') : $('body') - }); + this.margin = D.height() > W.height() || $('body').css('overflow-y') === 'scroll' ? $('body').css('margin-right') : false; + this.el = document.all && !document.querySelector ? $('html') : $('body'); + + this.create(opts); } - if (obj.fixed && !isTouch) { - this.overlay.addClass('fancybox-overlay-fixed'); - - if (obj.autoCenter) { - this.overlay.append( obj.wrap ); - - obj.locked = this.overlay; - } + if (opts.locked && this.fixed) { + obj.locked = this.overlay.append( obj.wrap ); + obj.fixed = false; } if (opts.showEarly === true) { @@ -1728,54 +1816,28 @@ }, beforeShow : function(opts, obj) { - var overlay = this.overlay.unbind('.fb').width('auto').height('auto').css( opts.css ); + if (obj.locked) { + this.el.addClass('fancybox-lock'); - if (opts.closeClick) { - overlay.bind('click.fb', function(e) { - if ($(e.target).hasClass('fancybox-overlay')) { - F.close(); - } - }); - } - - if (obj.fixed && !isTouch) { - if (obj.locked) { - this.el.addClass('fancybox-lock'); - - if (this.margin !== false) { - $('body').css('margin-right', getScalar( this.margin ) + obj.scrollbarWidth); - } + if (this.margin !== false) { + $('body').css('margin-right', getScalar( this.margin ) + obj.scrollbarWidth); } - - } else { - this.update(); } - overlay.show(); + this.open(opts); }, - onUpdate : function(opts, obj) { - if (!obj.fixed || isTouch) { + onUpdate : function() { + if (!this.fixed) { this.update(); } }, afterClose: function (opts) { - var that = this, - speed = opts.speedOut || 0; - // Remove overlay if exists and fancyBox is not opening // (e.g., it is not being open using afterClose callback) - if (that.overlay && !F.isActive) { - that.overlay.fadeOut(speed || 0, function () { - $('body').css('margin-right', that.margin); - - that.el.removeClass('fancybox-lock'); - - that.overlay.remove(); - - that.overlay = null; - }); + if (this.overlay && !F.isActive) { + this.overlay.fadeOut(opts.speedOut, $.proxy( this.close, this )); } } }; @@ -1785,12 +1847,22 @@ */ F.helpers.title = { + defaults : { + type : 'float', // 'float', 'inside', 'outside' or 'over', + position : 'bottom' // 'top' or 'bottom' + }, + beforeShow: function (opts) { - var text = F.current.title, - type = opts.type, + var current = F.current, + text = current.title, + type = opts.type, title, target; + if ($.isFunction(text)) { + text = text.call(current.element, current); + } + if (!isString(text) || $.trim(text) === '') { return; } @@ -1813,22 +1885,20 @@ default: // 'float' target = F.skin; - title - .appendTo('body') - .width(title.width()) //This helps for some browsers - .wrapInner(''); + title.appendTo('body'); - //Increase bottom margin so this title will also fit into viewport - F.current.margin[2] += Math.abs( getScalar(title.css('margin-bottom')) ); + if ($.browser.msie) { + title.width( title.width() ); + } + + title.wrapInner(''); + + //Increase bottom margin so this title will also fit into viewport + F.current.margin[2] += Math.abs( getScalar(title.css('margin-bottom')) ); break; } - if (opts.position === 'top') { - title.prependTo(target); - - } else { - title.appendTo(target); - } + title[ (opts.position === 'top' ? 'prependTo' : 'appendTo') ](target); } }; @@ -1869,10 +1939,13 @@ if (!selector || options.live === false) { that.unbind('click.fb-start').bind('click.fb-start', run); + } else { D.undelegate(selector, 'click.fb-start').delegate(selector + ":not('.fancybox-item, .fancybox-nav')", 'click.fb-start', run); } + this.filter('[data-fancybox-start=1]').trigger('click'); + return this; }; From d6f3cdbb5502f08901119e5b36d6abf3c96f74e3 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 20 Nov 2012 06:11:44 -0500 Subject: [PATCH 185/281] fixed tests --- lib/client/dom.js | 11 +++-- lib/client/ie.js | 4 +- lib/client/keyBinding.js | 6 +-- lib/client/socket.js | 4 +- lib/client/storage/_github.js | 2 +- lib/client/viewer.js | 2 +- lib/cloudfunc.js | 4 +- lib/util.js | 2 +- test/test.js | 89 ++++++++++++++++++++++------------- test/test.sh | 29 ++++++------ 10 files changed, 88 insertions(+), 65 deletions(-) diff --git a/lib/client/dom.js b/lib/client/dom.js index 93d1e810..f2632656 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -1,4 +1,4 @@ -var CloudCommander, $, Util, DOM, CloudFunc; +var CloudCommander, Util, DOM, CloudFunc; (function(){ "use strict"; @@ -15,6 +15,8 @@ var CloudCommander, $, Util, DOM, CloudFunc; * private function thet unset currentfile */ function UnSetCurrentFile(pCurrentFile){ + var lRet_b = DOM.isCurrentFile(pCurrentFile); + if(!pCurrentFile) DOM.addCloudStatus({ code : -1, @@ -23,8 +25,6 @@ var CloudCommander, $, Util, DOM, CloudFunc; 'could not be none' }); - var lRet_b = DOM.isCurrentFile(pCurrentFile); - if(lRet_b) DOM.removeClass(pCurrentFile, getCurrentFile()); @@ -129,9 +129,10 @@ var CloudCommander, $, Util, DOM, CloudFunc; XMLHTTP.open(lType, pParams.url, true); XMLHTTP.send(lData); - if( !Util.isFunction(lSuccess_f) ) - console.log('error in DOM.ajax onSuccess:', pParams) && + if( !Util.isFunction(lSuccess_f) ){ + console.log('error in DOM.ajax onSuccess:', pParams); console.log(pParams); + } XMLHTTP.onreadystatechange = function(pEvent){ if (XMLHTTP.readyState === 4 /* Complete */){ diff --git a/lib/client/ie.js b/lib/client/ie.js index e64f715a..010db487 100644 --- a/lib/client/ie.js +++ b/lib/client/ie.js @@ -112,8 +112,8 @@ var Util, DOM, $; parentBorderLeftWidth + pElement.clientWidth / 2; - if ( (overTop || overBottom || overLeft || overRight) - && !centerIfNeeded) + if ( (overTop || overBottom || overLeft || overRight) && + !centerIfNeeded) pElement.scrollIntoView(alignWithTop); }; diff --git a/lib/client/keyBinding.js b/lib/client/keyBinding.js index df9c3a92..084a7f01 100644 --- a/lib/client/keyBinding.js +++ b/lib/client/keyBinding.js @@ -236,11 +236,11 @@ var CloudCommander, Util, DOM; var lC = lCurrentFile, tryCatch = function(pCurrentFile){ Util.tryCatch(function(){ - pCurrentFile - .previousSibling + return pCurrentFile .previousSibling .previousSibling - .previousSibling; + .previousSibling + .previousSibling; }); }; diff --git a/lib/client/socket.js b/lib/client/socket.js index 168f97f5..89a730ac 100644 --- a/lib/client/socket.js +++ b/lib/client/socket.js @@ -82,14 +82,14 @@ var CloudCommander, DOM, Util, io; } Messages = []; } - + lStdout = pMsg.stdout; lStderr = pMsg.stderr; if(lStdout) lResult = lTerm.echo(lStdout); - if(lStderr && lStderr.code != 1) + if(lStderr && lStderr.code !== 1) lResult = lTerm.error(lStderr.toString()); } else{ diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index f1219223..c56be6b1 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -18,7 +18,7 @@ var CloudCommander, Util, DOM, $, Github, cb; cloudcmd.Storage = {}; - cb = function (err, data){ console.log(err || data);} + cb = function (err, data){ console.log(err || data);}; /* PRIVATE FUNCTIONS */ diff --git a/lib/client/viewer.js b/lib/client/viewer.js index 73fe9e5e..3e973f51 100644 --- a/lib/client/viewer.js +++ b/lib/client/viewer.js @@ -87,7 +87,7 @@ var CloudCommander, Util, DOM, CloudFunc, $; }, padding : 0 }; - }; + } /** * function loads css and js of FancyBox diff --git a/lib/cloudfunc.js b/lib/cloudfunc.js index 0605850b..35be329b 100644 --- a/lib/cloudfunc.js +++ b/lib/cloudfunc.js @@ -248,7 +248,7 @@ var CloudFunc, exports; lFS_s + lNoJS_s + lTitle + - '"/"' + + '/' + _l + '/' + lHrefEnd; @@ -430,7 +430,7 @@ var CloudFunc, exports; '' : ' target="_blank"') + ' title="' + files[i].name +'"' + - 'draggable=true>' + files[i].name + + ' draggable=true>' + files[i].name + "" + ''; /* если папка - не выводим размер */ diff --git a/lib/util.js b/lib/util.js index 14a1a412..5c6c968a 100644 --- a/lib/util.js +++ b/lib/util.js @@ -31,7 +31,7 @@ var Util, exports; */ Util.strCmp = function (pStr1, pStr2){ return this.isContainStr(pStr1, pStr2) && - pStr1.length == pStr2.length; + pStr1.length === pStr2.length; }; /** diff --git a/test/test.js b/test/test.js index 7d7d5584..242d48d8 100644 --- a/test/test.js +++ b/test/test.js @@ -1,8 +1,12 @@ -var CloudFunc = require('../lib/cloudfunc'); -var assert = require('assert'); - -try{ - var lJSON = [{ +(function(){ + "use strict"; + + var DIR = process.cwd() + '/', + main = require(DIR + 'lib/server/main'), + + CloudFunc = main.cloudfunc, + + lJSON = [{ "path": "/etc/X11/", "size": "dir" }, { @@ -17,8 +21,10 @@ try{ "mode": "100755" }]; - console.time('CloudFunc.buildFromJSON'); + start(); var lResult = CloudFunc.buildFromJSON(lJSON); + end(); + var lExpect = '
  • ' + ''+ '' + '' + - '/' + + '/' + '' + 'etc' + '/X11/' + @@ -40,38 +46,53 @@ try{ 'owner' + 'mode' + '
  • ' + - '
  • ' + - '' + - '..' + - '<dir>' + - '.' + - '
  • ' + - '
  • ' + + '
  • ' + '' + '' + - 'applnk' + + '..' + '' + '<dir>' + - 'root' + - 'rwx r-x r-x' + + '.' + + '' + '
  • ' + - '
  • ' + - '' + - '' + - '' + - 'prefdm' + + '
  • ' + + '' + + '' + + 'applnk' + + '' + + '<dir>' + + 'root' + + 'rwx r-x r-x' + + '
  • ' + + '
  • ' + + '' + + '' + + '' + + 'prefdm' + '' + '' + - '1.30kb' + - 'root' + - 'rwx r-x r-x' + - '
  • 1'; + '1.30kb' + + 'root' + + 'rwx r-x r-x' + + ''; + + for(var i = 0, n = lExpect.length; i < n; i++) + if(lResult[i] !== lExpect[i]){ + console.log('Error in char number: ' + i + '\n' + + 'Expect: ' + lExpect.substr(i) + '\n' + + 'Result: ' + lResult.substr(i) ); + break; + } + if(i===n) + console.log('CloudFunc.buildFromJSON: OK'); - console.timeEnd('CloudFunc.buildFromJSON'); - assert.equal( - lResult, - lExpect, 'Something wrong in buildFromJSON'); -} -catch(pError){ - console.log(pError); -} \ No newline at end of file + + function start(){ + return console.time('CloudFunc.buildFromJSON'); + } + function end(){ + return console.timeEnd('CloudFunc.buildFromJSON'); + } +})(); diff --git a/test/test.sh b/test/test.sh index 209ecf5f..1605986a 100755 --- a/test/test.sh +++ b/test/test.sh @@ -1,15 +1,16 @@ -set jshint = "../node_modules/jshint/bin/hint --config ./test/.jshintrc" -echo "jshint server.js client.js cloudcmd.js" -jshint server.js client.js cloudcmd.js -echo "jshint lib/cloudfunc.js lib/client/keyBinding.js" -jshint ./lib/util.js ./lib/cloudfunc.js ./node_modules/minify/minify.js ./lib/client/keyBinding.js -jshint ./lib/client/dom.js ./lib/client/ie.js ./lib/client/menu.js ./lib/client/socket.js ./lib/client/terminal.js ./lib/client/viewer.js ./lib/client/storage/_github.js ./lib/client/menu.js ./lib/client/editor/_codemirror.js -echo "jshint ./package.json ./config.json" -jshint ./package.json ./config.json -#linting css files -npm i recess -echo "recess css/*.css" -./node_modules/recess/bin/recess ../css/*.css -node ./test/test.js -node cloudcmd.js test +#!/bin/sh +echo "jshint server.js client.js cloudcmd.js" +node_modules/jshint/bin/hint --config test/.jshintrc server.js client.js cloudcmd.js +echo "jshint lib/cloudfunc.js lib/client/keyBinding.js" +node_modules/jshint/bin/hint --config test/.jshintrc lib/util.js lib/cloudfunc.js node_modules/minify/minify.js lib/client/keyBinding.js +echo "lib/client/dom.js lib/client/ie.js lib/client/menu.js lib/client/socket.js ./lib/client/terminal.js lib/client/viewer.js lib/client/storage/_github.js lib/client/menu.js lib/client/editor/_codemirror.js" +node_modules/jshint/bin/hint --config test/.jshintrc lib/client/dom.js lib/client/ie.js lib/client/menu.js lib/client/socket.js ./lib/client/terminal.js lib/client/viewer.js lib/client/storage/_github.js lib/client/menu.js lib/client/editor/_codemirror.js +echo "jshint ./package.json ./config.json" +jshint ./package.json ./config.json +#linting css files +npm i recess +echo "recess css/*.css" +./node_modules/recess/bin/recess css/*.css +node test/test.js +node cloudcmd.js test ls ./min \ No newline at end of file From 8e9df6f3bb8a39dee851114d33bf4542e8c745e1 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 20 Nov 2012 06:14:57 -0500 Subject: [PATCH 186/281] minor changes --- test/test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test.sh b/test/test.sh index 1605986a..7659e584 100755 --- a/test/test.sh +++ b/test/test.sh @@ -6,7 +6,7 @@ node_modules/jshint/bin/hint --config test/.jshintrc lib/util.js lib/cloudfunc.j echo "lib/client/dom.js lib/client/ie.js lib/client/menu.js lib/client/socket.js ./lib/client/terminal.js lib/client/viewer.js lib/client/storage/_github.js lib/client/menu.js lib/client/editor/_codemirror.js" node_modules/jshint/bin/hint --config test/.jshintrc lib/client/dom.js lib/client/ie.js lib/client/menu.js lib/client/socket.js ./lib/client/terminal.js lib/client/viewer.js lib/client/storage/_github.js lib/client/menu.js lib/client/editor/_codemirror.js echo "jshint ./package.json ./config.json" -jshint ./package.json ./config.json +node_modules/jshint/bin/hint --config test/.jshintrc ./package.json ./config.json #linting css files npm i recess echo "recess css/*.css" From 6450c3b3a5a3debb84ff428ad1f742e9ff0cf1e8 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 20 Nov 2012 09:18:50 -0500 Subject: [PATCH 187/281] impreoved readme --- README.md | 17 +++++++++++++++-- config.json | 2 +- test/test.sh | 2 +- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a3efc6db..be73d6b1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ Cloud Commander [![Build Status](https://secure.travis-ci.org/coderaiser/cloudcmd.png?branch=master)](http://travis-ci.org/coderaiser/cloudcmd) =============== -**Cloud Commander** - two-panels file manager, totally writed on js. +**Cloud Commander** - user friendly cloud file manager. DEMO: [cloudfoundry] (http://cloudcmd.cloudfoundry.com "cloudfoundry"), [appfog] (http://cloudcmd.aws.af.cm "appfog"). @@ -80,12 +80,25 @@ All main configuration could be done thrue config.json. "html" : true, "img" : false }, + "oauth_client_id" : "891c251b925e4e967fa9", /* github app id */ + "oauth_client_secret" : "afe9bed1e810c5dc44c4c2a953fc6efb1e5b0545", /* github app secret */ + "show_keys_panel" : true, /* show classic panel with buttons of keys */ "server" : true, /* server mode or testing mode */ "logs" : false, /* logs or console ouput */ + "socket" : true /* enable web sockets */ "port" : 80, /* Cloud Commander port */ - "ip" : "127.0.0.1" /* Cloud Commander IP */ + "ip" : "127.0.0.1", /* Cloud Commander IP */ + "rest" : true /* enable rest interface */ } ``` +Authorization +--------------- +Thru openID Cloud Commander could Authorize clients. +All things that should be done is must be added id and secret of application +from github settings page and added to config.json or env varibles with names: +"oauth_client_id" and "oauth_client_secret" that is more secure way. + + Starting --------------- To start **Cloud Commander** only one command neaded: diff --git a/config.json b/config.json index 73d27d8e..4d024bf8 100644 --- a/config.json +++ b/config.json @@ -9,7 +9,7 @@ }, "oauth_client_id" : "891c251b925e4e967fa9", "oauth_client_secret" : "afe9bed1e810c5dc44c4c2a953fc6efb1e5b0545", - "show_keys_panel" : true, + "show_keys_panel" : true, "server" : true, "logs" : false, "socket" : true, diff --git a/test/test.sh b/test/test.sh index 7659e584..72d745b1 100755 --- a/test/test.sh +++ b/test/test.sh @@ -1,4 +1,5 @@ #!/bin/sh +npm i recess jshint echo "jshint server.js client.js cloudcmd.js" node_modules/jshint/bin/hint --config test/.jshintrc server.js client.js cloudcmd.js echo "jshint lib/cloudfunc.js lib/client/keyBinding.js" @@ -8,7 +9,6 @@ node_modules/jshint/bin/hint --config test/.jshintrc lib/client/dom.js lib/clien echo "jshint ./package.json ./config.json" node_modules/jshint/bin/hint --config test/.jshintrc ./package.json ./config.json #linting css files -npm i recess echo "recess css/*.css" ./node_modules/recess/bin/recess css/*.css node test/test.js From 52dbaa9a9585bb1439aab393f3345755c064fd49 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 20 Nov 2012 16:22:59 +0200 Subject: [PATCH 188/281] Update README.md --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index be73d6b1..e78614d1 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ There is a short list: - Alt + q - disable key bindings - Alt + s - get all key bindings back - up, down, enter - filesystem navigation +- Alt + g - authorization Viewer's hot keys --------------- @@ -93,10 +94,10 @@ All main configuration could be done thrue config.json. ``` Authorization --------------- -Thru openID Cloud Commander could Authorize clients. -All things that should be done is must be added id and secret of application -from github settings page and added to config.json or env varibles with names: -"oauth_client_id" and "oauth_client_secret" that is more secure way. +Thru openID Cloud Commander could authorize clients on GitHub. +All things that should be done is must be added **id** and **secret** of application +from github settings page and added to **config.json** or env varibles with names: +*oauth_client_id* and *oauth_client_secret* that is more secure way. Starting From aca952bbdb680c27e14d4eec5e799ddd12fa9fb2 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 20 Nov 2012 16:28:37 +0200 Subject: [PATCH 189/281] Update README.md --- README.md | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index e78614d1..9dc8602b 100644 --- a/README.md +++ b/README.md @@ -34,23 +34,23 @@ Hot keys --------------- In all modern web browsers (but not in IE, becouse he special) hot keys works. There is a short list: -- Ctrl + r - reload dir content -- Ctrl + d - clear local cache (wich contains dir contents) -- Alt + q - disable key bindings -- Alt + s - get all key bindings back -- up, down, enter - filesystem navigation -- Alt + g - authorization +- **Ctrl + r** - reload dir content +- **Ctrl + d** - clear local cache (wich contains dir contents) +- **Alt + q** - disable key bindings +- **Alt + s** - get all key bindings back +- **up, down, enter** - filesystem navigation +- **Alt + g** - authorization Viewer's hot keys --------------- -- Shift + F3 - open viewer window -- Esc - close viewer window +- **Shift + F3** - open viewer window +- **Esc** - close viewer window Editor's hot keys --------------- -- F3 - open CodeMirror editor in read only mode -- F4 - open CodeMirror editor -- Esc - close CodeMirror editor +- **F3** - open CodeMirror editor in read only mode +- **F4** - open CodeMirror editor +- **Esc** - close CodeMirror editor Documentation --------------- @@ -119,14 +119,17 @@ or http://localhost Updating --------------- -**Cloud Commander** is very buggy and alfa so it's very often updated. For update -you can just type in cloudcmd directory: +**Cloud Commander** is very alfa and it's very often updatings. +Update is doing automagically but it could be done also manualy +by typing a few commands in cloudcmd directory: git pull or check new version on npm + npm info cloudcmd and then, if there is new version + npm r cloudcmd npm i cloudcmd @@ -141,7 +144,7 @@ Install addtitional modules: npm i -**Cloud Commander's Client Side** use module jquery for ajaxing. +**Cloud Commander's Client Side** use module jquery for ajaxing. But only for old browsers. We could not use this module, but this way is fast: - google cdn - gzip From c41769ca505681280962efe676543537050d5a59 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 20 Nov 2012 16:59:51 +0200 Subject: [PATCH 190/281] improved --- README.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9dc8602b..3790bbe8 100644 --- a/README.md +++ b/README.md @@ -158,10 +158,15 @@ Extensions --------------- **Cloud Commander** desinged to easily porting extensions. For extend main functionality Cloud Commander use next modules: -- [CodeMirror] (http://codemirror.net "CodeMirror") -- [FancyBox] (https://github.com/fancyapps/fancyBox "FancyBox") -- [jQuery-contextMenu] (https://github.com/medialize/jQuery-contextMenu "jQuery-contextMenu") -- [jquery.terminal] (https://github.com/jcubic/jquery.terminal "jquery.terminal") +- [CodeMirror] [CodeMirrorURL] +- [FancyBox] [FancyBoxURL] +- [jQuery-contextMenu] [jQuery-contextMenuURL] +- [jquery.terminal] [jquery.terminalURL] + +[CodeMirrorURL]: https://github.com/marijnh/CodeMirror "CodeMirror" +[FancyBoxURL]: https://github.com/fancyapps/fancyBox "FancyBox" +[jQuery-contextMenuURL]: https://github.com/medialize/jQuery-contextMenu "jQuery-contextMenu" +[jquery.terminalURL]: https://github.com/jcubic/jquery.terminal "jquery.terminal" Contributing --------------- From c4f253848b1415bda4844dddd864f8b48c516ac0 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 21 Nov 2012 03:31:50 -0500 Subject: [PATCH 191/281] refactored --- cloudcmd.js | 2 +- lib/client/viewer.js | 2 +- lib/cloudfunc.js | 32 +------------------------------- lib/server/main.js | 13 +++++++------ lib/util.js | 29 +++++++++++++++++++++++++++++ server.js | 2 +- 6 files changed, 40 insertions(+), 40 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 2ef9885b..91d12eea 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -8,7 +8,7 @@ SRVDIR = main.SRVDIR, path = main.path, - fs = main.path, + fs = main.fs, CloudFunc = main.cloudfunc, Util = main.util, update = main.update, diff --git a/lib/client/viewer.js b/lib/client/viewer.js index 3e973f51..d15a5edf 100644 --- a/lib/client/viewer.js +++ b/lib/client/viewer.js @@ -44,7 +44,7 @@ var CloudCommander, Util, DOM, CloudFunc, $; lName = lA.title || lA.textContent; lA.className = 'fancybox'; - if(CloudFunc.checkExtension(lName, ['png','jpg', 'gif','ico'])) + if(Util.checkExtension(lName, ['png','jpg', 'gif','ico'])) lA.rel = 'gallery'; lA.ondblclick = lDblClick_f(lA); diff --git a/lib/cloudfunc.js b/lib/cloudfunc.js index 35be329b..6daf20e7 100644 --- a/lib/cloudfunc.js +++ b/lib/cloudfunc.js @@ -267,37 +267,7 @@ var CloudFunc, exports; lHtmlPath+=lShortName+'/'; } /* *** */ - return lHtmlPath; - }; - - /** - * Функция ищет в имени файла расширение - * и если находит возвращает true - * @param pName - получает имя файла - * @param pExt - расширение - */ - CloudFunc.checkExtension = function(pName, pExt) - { - /* если длина имени больше - * длинны расширения - - * имеет смысл продолжать - */ - if (typeof pExt === 'string' && - pName.length > pExt.length) { - var lLength = pName.length; /* длина имени*/ - var lExtNum = pName.lastIndexOf(pExt); /* последнее вхождение расширения*/ - var lExtSub = lLength - lExtNum; /* длина расширения*/ - - /* если pExt - расширение pName */ - return lExtSub === pExt.length; - - }else if(typeof pExt === 'object' && - pExt.length){ - for(var i=0; i < pName.length; i++) - if(this.checkExtension(pName, pExt[i])) - return true; - }else - return false; + return lHtmlPath; }; /** diff --git a/lib/server/main.js b/lib/server/main.js index a4cfd70f..fbf30bc5 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -31,6 +31,7 @@ exports.rootrequire = rootrequire, /* Native Modules*/ + exports.crypto = require('crypto'), exports.child_process = require('child_process'), exports.fs = require('fs'), exports.http = require('http'), @@ -87,12 +88,12 @@ function isWin32(){ return process.platform === 'win32'; } /** - * Функция создаёт заголовки файлов - * в зависимости от расширения файла - * перед отправкой их клиенту - * @param pName - имя файла - * @param pGzip - данные сжаты gzip'ом - */ + * Функция создаёт заголовки файлов + * в зависимости от расширения файла + * перед отправкой их клиенту + * @param pName - имя файла + * @param pGzip - данные сжаты gzip'ом + */ function generateHeaders(pName, pGzip, pQuery){ var lType = '', lCacheControl = 0, diff --git a/lib/util.js b/lib/util.js index 5c6c968a..8ca872c7 100644 --- a/lib/util.js +++ b/lib/util.js @@ -22,6 +22,35 @@ var Util, exports; return lRet; }; + /** + * Функция ищет в имени файла расширение + * и если находит возвращает true + * @param pName - получает имя файла + * @param pExt - расширение + */ + Util.checkExtension = function(pName, pExt) + { + /* если длина имени больше + * длинны расширения - + * имеет смысл продолжать + */ + if (typeof pExt === 'string' && + pName.length > pExt.length) { + var lLength = pName.length; /* длина имени*/ + var lExtNum = pName.lastIndexOf(pExt); /* последнее вхождение расширения*/ + var lExtSub = lLength - lExtNum; /* длина расширения*/ + + /* если pExt - расширение pName */ + return lExtSub === pExt.length; + + }else if(typeof pExt === 'object' && + pExt.length){ + for(var i=0; i < pName.length; i++) + if(this.checkExtension(pName, pExt[i])) + return true; + }else + return false; + }; /* STRINGS */ /** diff --git a/server.js b/server.js index 092cd45f..da2c9b0b 100644 --- a/server.js +++ b/server.js @@ -327,7 +327,7 @@ CloudServer._controller = function(pReq, pRes) var lMin_o = lConfig.minification, lCheck_f = function(pExt){ - return CloudFunc.checkExtension(lName,pExt); + return Util.checkExtension(lName,pExt); }, isAllowd_b = (lCheck_f('js') && lMin_o.js) || From 7ffe7837773eef065a68b17849d29070a5ab4440 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 21 Nov 2012 05:27:11 -0500 Subject: [PATCH 192/281] minor changes --- cloudcmd.js | 5 +++-- config.json | 8 ++++---- index.html | 2 +- server.js | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 91d12eea..6ce33d91 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -39,7 +39,7 @@ * меняем в index.html обычные css на * минифицированый */ - if(srv.Minify._allowed.css){ + if(srv.Minify._allowed.css){ lReplace_s = ''; lData = Util.removeStr(lData, lReplace_s); @@ -53,8 +53,9 @@ lData = lData.replace('Cloud Commander', '' + CloudFunc.setTitle() + ''); + console.log(lData); if(!srv.Config.appcache) - lData = Util.removeStr(lData, ' manifest=/cloudcmd.appcache'); + lData = Util.removeStr(lData, ' manifest="/cloudcmd.appcache"'); if(!srv.Config.show_keys_panel){ var lKeysPanel = '
    - + diff --git a/server.js b/server.js index da2c9b0b..bd4fe67c 100644 --- a/server.js +++ b/server.js @@ -299,7 +299,7 @@ CloudServer._controller = function(pReq, pRes) console.log('trying to read data from Minify.Cache'); lFromCache_o.cache = false; lFileData = CloudServer.Minify.Cache[ - Path.basename(lName)]; + Path.basename(lName)]; } var lReadFileFunc_f = CloudServer.getReadFileFunc(lName), /* если там что-то есть передаём данные в функцию readFile */ From 73fc21b40358fdd1b62ce74deebc20afd52d3bc8 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 21 Nov 2012 05:34:38 -0500 Subject: [PATCH 193/281] minor changes --- cloudcmd.js | 1 - server.js | 1465 +++++++++++++++++++++++++-------------------------- 2 files changed, 727 insertions(+), 739 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 6ce33d91..14681c99 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -53,7 +53,6 @@ lData = lData.replace('Cloud Commander', '' + CloudFunc.setTitle() + ''); - console.log(lData); if(!srv.Config.appcache) lData = Util.removeStr(lData, ' manifest="/cloudcmd.appcache"'); diff --git a/server.js b/server.js index bd4fe67c..3153ce4a 100644 --- a/server.js +++ b/server.js @@ -1,763 +1,752 @@ -"use strict"; - -/* Обьект содержащий все функции и переменные - * серверной части Cloud Commander'а - */ -var CloudServer = { - /* main Cloud Commander configuration - * readed from config.json if it's - * exist +(function(){ + "use strict"; + + /* Обьект содержащий все функции и переменные + * серверной части Cloud Commander'а */ - Config : { - cache : { - allowed : true + var CloudServer = { + /* base configuration */ + Config : { + server : true, + socket : true, + port : 80, + ip : '127.0.0.1' }, - appcache : false, - minification : { - js : false, - css : false, - html : true, - img : false - }, - server : true, - logs : false, - socket : true, - port : 80, - ip : '127.0.0.1' - }, - - /* функция, которая генерирует заголовки - * файлов, отправляемые сервером клиенту - */ - generateHeaders : function () {}, - - /* функция высылает - * данные клиенту - */ - sendResponse : function () {}, - - /* Асоциативный масив обьектов для - * работы с ответами сервера - * высылаемыми на запрос о файле и - * хранащий информацию в виде - * Responses[name]=responce; - */ - Responses : {}, - - /* - * Асоциативный масив статусов - * ответов сервера - * высылаемыми на запрос о файле и - * хранащий информацию в виде - * Statuses[name] = 404; - */ - Statuses : {}, - - /* - * queries of file params - * example: ?download - */ - Queries : {}, - /* ПЕРЕМЕННЫЕ - * Поддержка браузером JS */ - NoJS : true, - /* Поддержка gzip-сжатия браузером */ - Gzip : undefined, - - /* server varible */ - Server :{}, - - /* КОНСТАНТЫ */ - INDEX : 'index.html' -}, - - DirPath = '/', - - OK = 200, - DIR = process.cwd() + '/', - main = require(DIR + 'lib/server/main.js'), - - DIR = main.DIR, - LIBDIR = main.LIBDIR, - SRVDIR = main.SRVDIR, - -/* модуль для работы с путями*/ - Path = main.path, - Fs = main.fs, /* модуль для работы с файловой системой*/ - Querystring = main.querystring, - -/* node v0.4 not contains zlib */ - Zlib = main.zlib; /* модуль для сжатия данных gzip-ом*/ -if(!Zlib) - console.log('to use gzip-commpression' + - 'you should use newer node version\n'); - - /* добавляем модуль с функциями */ -var CloudFunc = main.cloudfunc, - Util = main.util; - -/* Обьект для работы с кэшем */ -CloudServer.Cache = main.cache, - -CloudServer.Minify = main.minify, -CloudServer.AppCache = main.appcache, -CloudServer.Socket = main.socket; - -/* базовая инициализация */ -CloudServer.init = (function(pAppCachProcessing){ - var lConfig = this.Config, - lMinify = this.Minify, - lCache = this.Cache, - lAppCache = this.AppCache; - - /* Переменная в которой храниться кэш*/ - lCache.setAllowed(lConfig.cache.allowed); - - /* Change default parameters of - * js/css/html minification - */ - lMinify.setAllowed(lConfig.minification); - - /* Если нужно минимизируем скрипты */ - lMinify._allowed = lMinify.doit(); - - /* создаём файл app cache */ - if( lConfig.appcache && lAppCache && lConfig.server ) - Util.exec( pAppCachProcessing ); -}); - - -/** - * Функция создаёт сервер - * @param pConfig - */ -CloudServer.start = function (pConfig, pProcessing) { - if(!pProcessing) - pProcessing = {}; - - if(pConfig) - this.Config = pConfig; - - else - console.log('warning: configuretion file config.json not found...\n' + - 'using default values...\n' + - JSON.stringify(this.Config)); - - var lConfig = this.Config; - - CloudServer.indexProcessing = pProcessing.index; - CloudServer.rest = pProcessing.rest; - - this.init(pProcessing.appcache); - - this.Port = process.env.PORT || /* c9 */ - process.env.app_port || /* nodester */ - process.env.VCAP_APP_PORT || /* cloudfoundry */ - lConfig.port; - - this.IP = process.env.IP || /* c9 */ - this.Config.ip; - - /* if Cloud Server started on jitsu */ - if(process.env.HOME && - !process.env.HOME.indexOf('/opt/haibu')) { - this.IP = '0.0.0.0'; - } - - /* server mode or testing mode */ - if (lConfig.server) { - var http = main.http, - lError = Util.tryCatchLog(Util.bind(function(){ - this.Server = http.createServer(this._controller); - this.Server.listen(this.Port, this.IP); - - if(lConfig.socket && CloudServer.Socket){ - CloudServer.Socket.listen(this.Server); - console.log('sockets running'); - } - else - console.log('sockets disabled'); - - console.log('Cloud Commander server running at http://' + - this.IP + ':' + this.Port); - }, this)); - if(lError) - console.log('Cloud Commander server could not started'); - }else - console.log('Cloud Commander testing mode'); -}; - - -/** - * Главная функция, через которую проихсодит - * взаимодействие, обмен данными с клиентом - * @param req - запрос клиента (Request) - * @param res - ответ сервера (Response) - */ -CloudServer._controller = function(pReq, pRes) -{ - /* Читаем содержимое папки, переданное в url */ - var lConfig = CloudServer.Config, - url = main.url, - lParsedUrl = url.parse(pReq.url), - pathname = lParsedUrl.pathname, - /* varible contain one of queris: - * download - change content-type for - * make downloading process - * from client js - * json - /no-js/ will be removed, and - * if we will wont get directory - * content wi will set json - * query like this - * ?json + /* функция, которая генерирует заголовки + * файлов, отправляемые сервером клиенту */ - lQuery = lParsedUrl.query; - if(lQuery) - console.log('query = ' + lQuery); + generateHeaders : function () {}, - /* added supporting of Russian language in directory names */ - pathname = Querystring.unescape(pathname); - console.log('pathname: ' + pathname); - - /* получаем поддерживаемые браузером кодировки*/ - var lAcceptEncoding = pReq.headers['accept-encoding']; - /* запоминаем поддерживает ли браузер - * gzip-сжатие при каждом обращении к серверу - * и доступен ли нам модуль zlib - */ - if (lAcceptEncoding && - lAcceptEncoding.match(/\bgzip\b/) && Zlib) - CloudServer.Gzip = true; - - /* путь в ссылке, который говорит - * что js отключен - */ - var lNoJS_s = CloudFunc.NOJS, - lFS_s = CloudFunc.FS; - - console.log("request for " + pathname + " received..."); - - if( lConfig.rest ){ - var lRestWas = Util.exec(CloudServer.rest, { - request : pReq, - response : pRes - }); - - if(lRestWas) - return; - } - - /* если в пути нет информации ни о ФС, - * ни об отсутствии js, - * ни о том, что это корневой - * каталог - загружаем файлы проэкта - */ - if ( !Util.isContainStr(pathname, lFS_s) && - !Util.isContainStr(pathname, lNoJS_s) && - !Util.strCmp(pathname, '/') && - !Util.strCmp(lQuery, 'json') ) { - /* если имена файлов проекта - загружаем их*/ - /* убираем слеш и читаем файл с текущец директории*/ - - /* добавляем текующий каталог к пути */ - var lName = '.' + pathname; - console.log('reading '+lName); - - /* watching is file changed */ - if(lConfig.appcache) - CloudServer.AppCache.watch(lName); - - /* сохраняем указатель на response и имя */ - CloudServer.Responses[lName] = pRes; - - /* saving status OK for current file */ - CloudServer.Statuses[lName] = OK; - - /* Берём значение из кэша - * сжатый файл - если gzip-поддерживаеться браузером - * не сжатый - в обратном случае - */ - var lFileData = CloudServer.Cache.get( - CloudServer.Gzip?(lName+'_gzip') : lName); - - console.log(Path.basename(lName)); - - var lMinify = CloudServer.Minify; - - /* object thet contains information - * about the source of file data - */ - var lFromCache_o = {'cache': true}; - - /* if cache is empty and Cache allowed and Minify_allowed - * and in Minifys cache is files, so save it to - * CloudServer cache - */ - if(!lFileData && - lMinify._allowed){ - console.log('trying to read data from Minify.Cache'); - lFromCache_o.cache = false; - lFileData = CloudServer.Minify.Cache[ - Path.basename(lName)]; - } - var lReadFileFunc_f = CloudServer.getReadFileFunc(lName), - /* если там что-то есть передаём данные в функцию readFile */ - lResult = true; - - if(lFileData){ - /* if file readed not from cache - - * he readed from minified cache - */ - lFromCache_o.minify = !lFromCache_o.cache; - - console.log(lName + ' readed from cache'); - - /* передаём данные с кэша, - * если gzip включен - сжатые - * в обратном случае - несжатые - */ - lReadFileFunc_f(undefined, lFileData, lFromCache_o); - } - /* if file not in one of caches - * and minimizing setted then minimize it - */ - else if(lName.indexOf('min') < 0 && - CloudServer.Minify){ - var lMin_o = lConfig.minification, - - lCheck_f = function(pExt){ - return Util.checkExtension(lName,pExt); - }, - - isAllowd_b = (lCheck_f('js') && lMin_o.js) || - (lCheck_f('css') && lMin_o.css) || - (lCheck_f('html') && lMin_o.html); - - if(isAllowd_b){ - lResult = CloudServer.Minify.optimize(lName, { - cache: true, - callback: function(pFileData){ - lReadFileFunc_f(undefined, pFileData, false); - } - }); - } - else - lResult = false; - } else - lResult = false; - - if(!lResult) - Fs.readFile(lName, lReadFileFunc_f); - }else{/* если мы имеем дело с файловой системой*/ - /* если путь не начинаеться с no-js - значит - * js включен + /* функция высылает + * данные клиенту */ - /* убираем пометку cloud, без которой c9.io - * не работает поскольку путь из двух слешей - * (/fs/no-js/) - очень короткий, нужно - * длиннее + sendResponse : function () {}, + + /* Асоциативный масив обьектов для + * работы с ответами сервера + * высылаемыми на запрос о файле и + * хранащий информацию в виде + * Responses[name]=responce; */ - - if(pathname.indexOf(lNoJS_s) !== lFS_s.length && pathname !== '/'){ - CloudServer.NoJS = false; - - }else - pathname = Util.removeStr(pathname, lNoJS_s); - - /* убираем индекс файловой системы */ - if(pathname.indexOf(lFS_s) === 0){ - pathname = Util.removeStr(pathname, lFS_s); - /* if query json setted up - * load json data, no-js false. - */ - - if(lQuery === 'json') - CloudServer.NoJS = false; - - /* если посетитель только зашел на сайт - * no-js будет пустым, как и fs. - * Если в пути нету fs - посетитель только зашел на сайт - * загружаем его полностью. - */ - }else - CloudServer.NoJS = true; - - /* если в итоге путь пустой - * делаем его корневым - */ - if (pathname === '') - pathname = '/'; - - DirPath = pathname; - - CloudServer.Responses[DirPath] = pRes; - - CloudServer.Statuses[DirPath] = OK; - - /* saving query of current file */ - CloudServer.Queries[DirPath] = lQuery; - Util.log(lQuery); - - console.log(DirPath); - - - /* читаем основные данные о файле */ - Fs.stat(DirPath, CloudServer._stated); - - /* если установлено сжатие - * меняем название html-файла и - * загружаем сжатый html-файл в дальнейшем - */ - CloudServer.INDEX = (CloudServer.Minify._allowed.html ? - '.' + CloudServer.Minify.MinFolder + 'index.min.html' - : CloudServer.INDEX); + Responses : {}, /* - * сохраним указатель на response - * и на статус ответа - */ - CloudServer.Responses[CloudServer.INDEX] = pRes; - CloudServer.Statuses [CloudServer.INDEX] = OK; - } -}; - -/** - * Function geted stat information about file - * @param pError - * @param pStat - */ -CloudServer._stated = function(pError, pStat){ - if(pError){ - CloudServer.Statuses[DirPath] = 404; - CloudServer.sendResponse('OK',pError.toString(), DirPath); + * Асоциативный масив статусов + * ответов сервера + * высылаемыми на запрос о файле и + * хранащий информацию в виде + * Statuses[name] = 404; + */ + Statuses : {}, - return; - } - - /* если это каталог - читаем его содержимое */ - if(pStat){ - if(pStat.isDirectory()) - Fs.readdir(DirPath, CloudServer._readDir); - /* отдаём файл */ - else if(pStat.isFile()){ - Fs.readFile(DirPath, CloudServer.getReadFileFunc(DirPath)); - console.log('reading file: '+ DirPath); - } - } -}; - - -/** - * Функция читает ссылку или выводит информацию об ошибке - * @param pError - * @param pFiles - */ -CloudServer._readDir = function (pError, pFiles) -{ - if(pError){ - console.log(pError); + /* + * queries of file params + * example: ?download + */ + Queries : {}, - CloudServer.Statuses[DirPath] = 404; - CloudServer.sendResponse('OK',pError.toString(), - DirPath); - return; - } + /* ПЕРЕМЕННЫЕ + * Поддержка браузером JS */ + NoJS : true, + /* Поддержка gzip-сжатия браузером */ + Gzip : undefined, + + /* server varible */ + Server :{}, + + /* КОНСТАНТЫ */ + INDEX : 'index.html' + }, + + DirPath = '/', + + OK = 200, + DIR = process.cwd() + '/', + main = require(DIR + 'lib/server/main.js'), + + LIBDIR = main.LIBDIR, + SRVDIR = main.SRVDIR, - /* Если мы не в корне добавляем слеш к будующим ссылкам */ - if(DirPath !== '/') - DirPath += '/'; - - pFiles = pFiles.sort(); + /* модуль для работы с путями*/ + Path = main.path, + Fs = main.fs, /* модуль для работы с файловой системой*/ + Querystring = main.querystring, - var lCount = 0, - lStats = {}; + /* node v0.4 not contains zlib */ + Zlib = main.zlib; /* модуль для сжатия данных gzip-ом*/ + if(!Zlib) + console.log('to use gzip-commpression' + + 'you should use newer node version\n'); - /* asyn getting file states - * and putting it to lStats object + /* добавляем модуль с функциями */ + var CloudFunc = main.cloudfunc, + Util = main.util; + + /* Обьект для работы с кэшем */ + CloudServer.Cache = main.cache, + + CloudServer.Minify = main.minify, + CloudServer.AppCache = main.appcache, + CloudServer.Socket = main.socket; + + /* базовая инициализация */ + CloudServer.init = (function(pAppCachProcessing){ + var lConfig = this.Config, + lMinify = this.Minify, + lCache = this.Cache, + lAppCache = this.AppCache; + + /* Переменная в которой храниться кэш*/ + lCache.setAllowed(lConfig.cache.allowed); + + /* Change default parameters of + * js/css/html minification + */ + lMinify.setAllowed(lConfig.minification); + + /* Если нужно минимизируем скрипты */ + lMinify._allowed = lMinify.doit(); + + /* создаём файл app cache */ + if( lConfig.appcache && lAppCache && lConfig.server ) + Util.exec( pAppCachProcessing ); + }); + + + /** + * Функция создаёт сервер + * @param pConfig */ - var getFilesStat_f = function(pName){ - return function(pError, pStat){ + CloudServer.start = function (pConfig, pProcessing) { + if(!pProcessing) + pProcessing = {}; + + if(pConfig) + this.Config = pConfig; + + else + console.log('warning: configuretion file config.json not found...\n' + + 'using default values...\n' + + JSON.stringify(this.Config)); + + var lConfig = this.Config; + + CloudServer.indexProcessing = pProcessing.index; + CloudServer.rest = pProcessing.rest; + + this.init(pProcessing.appcache); + + this.Port = process.env.PORT || /* c9 */ + process.env.app_port || /* nodester */ + process.env.VCAP_APP_PORT || /* cloudfoundry */ + lConfig.port; + + this.IP = process.env.IP || /* c9 */ + this.Config.ip; + + /* if Cloud Server started on jitsu */ + if(process.env.HOME && + !process.env.HOME.indexOf('/opt/haibu')) { + this.IP = '0.0.0.0'; + } + + /* server mode or testing mode */ + if (lConfig.server) { + var http = main.http, + lError = Util.tryCatchLog(Util.bind(function(){ + this.Server = http.createServer(this._controller); + this.Server.listen(this.Port, this.IP); + + if(lConfig.socket && CloudServer.Socket){ + CloudServer.Socket.listen(this.Server); + console.log('sockets running'); + } + else + console.log('sockets disabled'); + + console.log('Cloud Commander server running at http://' + + this.IP + ':' + this.Port); + }, this)); + if(lError) + console.log('Cloud Commander server could not started'); + }else + console.log('Cloud Commander testing mode'); + }; + + + /** + * Главная функция, через которую проихсодит + * взаимодействие, обмен данными с клиентом + * @param req - запрос клиента (Request) + * @param res - ответ сервера (Response) + */ + CloudServer._controller = function(pReq, pRes) + { + /* Читаем содержимое папки, переданное в url */ + var lConfig = CloudServer.Config, + url = main.url, + lParsedUrl = url.parse(pReq.url), + pathname = lParsedUrl.pathname, - var fReturnFalse = function(){ - return false; - }; + /* varible contain one of queris: + * download - change content-type for + * make downloading process + * from client js + * json - /no-js/ will be removed, and + * if we will wont get directory + * content wi will set json + * query like this + * ?json + */ + lQuery = lParsedUrl.query; + if(lQuery) + console.log('query = ' + lQuery); - if(pError) - lStats[pName] = { - 'mode':0, - 'size':0, - 'isDirectory':fReturnFalse + /* added supporting of Russian language in directory names */ + pathname = Querystring.unescape(pathname); + console.log('pathname: ' + pathname); + + /* получаем поддерживаемые браузером кодировки*/ + var lAcceptEncoding = pReq.headers['accept-encoding']; + /* запоминаем поддерживает ли браузер + * gzip-сжатие при каждом обращении к серверу + * и доступен ли нам модуль zlib + */ + if (lAcceptEncoding && + lAcceptEncoding.match(/\bgzip\b/) && Zlib) + CloudServer.Gzip = true; + + /* путь в ссылке, который говорит + * что js отключен + */ + var lNoJS_s = CloudFunc.NOJS, + lFS_s = CloudFunc.FS; + + console.log("request for " + pathname + " received..."); + + if( lConfig.rest ){ + var lRestWas = Util.exec(CloudServer.rest, { + request : pReq, + response : pRes + }); + + if(lRestWas) + return; + } + + /* если в пути нет информации ни о ФС, + * ни об отсутствии js, + * ни о том, что это корневой + * каталог - загружаем файлы проэкта + */ + if ( !Util.isContainStr(pathname, lFS_s) && + !Util.isContainStr(pathname, lNoJS_s) && + !Util.strCmp(pathname, '/') && + !Util.strCmp(lQuery, 'json') ) { + /* если имена файлов проекта - загружаем их*/ + /* убираем слеш и читаем файл с текущец директории*/ + + /* добавляем текующий каталог к пути */ + var lName = '.' + pathname; + console.log('reading '+lName); + + /* watching is file changed */ + if(lConfig.appcache) + CloudServer.AppCache.watch(lName); + + /* сохраняем указатель на response и имя */ + CloudServer.Responses[lName] = pRes; + + /* saving status OK for current file */ + CloudServer.Statuses[lName] = OK; + + /* Берём значение из кэша + * сжатый файл - если gzip-поддерживаеться браузером + * не сжатый - в обратном случае + */ + var lFileData = CloudServer.Cache.get( + CloudServer.Gzip?(lName+'_gzip') : lName); + + console.log(Path.basename(lName)); + + var lMinify = CloudServer.Minify; + + /* object thet contains information + * about the source of file data + */ + var lFromCache_o = {'cache': true}; + + /* if cache is empty and Cache allowed and Minify_allowed + * and in Minifys cache is files, so save it to + * CloudServer cache + */ + if(!lFileData && + lMinify._allowed){ + console.log('trying to read data from Minify.Cache'); + lFromCache_o.cache = false; + lFileData = CloudServer.Minify.Cache[ + Path.basename(lName)]; + } + var lReadFileFunc_f = CloudServer.getReadFileFunc(lName), + /* если там что-то есть передаём данные в функцию readFile */ + lResult = true; + + if(lFileData){ + /* if file readed not from cache - + * he readed from minified cache + */ + lFromCache_o.minify = !lFromCache_o.cache; + + console.log(lName + ' readed from cache'); + + /* передаём данные с кэша, + * если gzip включен - сжатые + * в обратном случае - несжатые + */ + lReadFileFunc_f(undefined, lFileData, lFromCache_o); + } + /* if file not in one of caches + * and minimizing setted then minimize it + */ + else if(lName.indexOf('min') < 0 && + CloudServer.Minify){ + var lMin_o = lConfig.minification, + + lCheck_f = function(pExt){ + return Util.checkExtension(lName,pExt); + }, + + isAllowd_b = (lCheck_f('js') && lMin_o.js) || + (lCheck_f('css') && lMin_o.css) || + (lCheck_f('html') && lMin_o.html); + + if(isAllowd_b){ + lResult = CloudServer.Minify.optimize(lName, { + cache: true, + callback: function(pFileData){ + lReadFileFunc_f(undefined, pFileData, false); + } + }); + } + else + lResult = false; + } else + lResult = false; + + if(!lResult) + Fs.readFile(lName, lReadFileFunc_f); + }else{/* если мы имеем дело с файловой системой*/ + /* если путь не начинаеться с no-js - значит + * js включен + */ + /* убираем пометку cloud, без которой c9.io + * не работает поскольку путь из двух слешей + * (/fs/no-js/) - очень короткий, нужно + * длиннее + */ + + if(pathname.indexOf(lNoJS_s) !== lFS_s.length && pathname !== '/'){ + CloudServer.NoJS = false; + + }else + pathname = Util.removeStr(pathname, lNoJS_s); + + /* убираем индекс файловой системы */ + if(pathname.indexOf(lFS_s) === 0){ + pathname = Util.removeStr(pathname, lFS_s); + /* if query json setted up + * load json data, no-js false. + */ + + if(lQuery === 'json') + CloudServer.NoJS = false; + + /* если посетитель только зашел на сайт + * no-js будет пустым, как и fs. + * Если в пути нету fs - посетитель только зашел на сайт + * загружаем его полностью. + */ + }else + CloudServer.NoJS = true; + + /* если в итоге путь пустой + * делаем его корневым + */ + if (pathname === '') + pathname = '/'; + + DirPath = pathname; + + CloudServer.Responses[DirPath] = pRes; + + CloudServer.Statuses[DirPath] = OK; + + /* saving query of current file */ + CloudServer.Queries[DirPath] = lQuery; + Util.log(lQuery); + + console.log(DirPath); + + + /* читаем основные данные о файле */ + Fs.stat(DirPath, CloudServer._stated); + + /* если установлено сжатие + * меняем название html-файла и + * загружаем сжатый html-файл в дальнейшем + */ + CloudServer.INDEX = (CloudServer.Minify._allowed.html ? + '.' + CloudServer.Minify.MinFolder + 'index.min.html' + : CloudServer.INDEX); + + /* + * сохраним указатель на response + * и на статус ответа + */ + CloudServer.Responses[CloudServer.INDEX] = pRes; + CloudServer.Statuses [CloudServer.INDEX] = OK; + } + }; + + /** + * Function geted stat information about file + * @param pError + * @param pStat + */ + CloudServer._stated = function(pError, pStat){ + if(pError){ + CloudServer.Statuses[DirPath] = 404; + CloudServer.sendResponse('OK',pError.toString(), DirPath); + + return; + } + + /* если это каталог - читаем его содержимое */ + if(pStat){ + if(pStat.isDirectory()) + Fs.readdir(DirPath, CloudServer._readDir); + /* отдаём файл */ + else if(pStat.isFile()){ + Fs.readFile(DirPath, CloudServer.getReadFileFunc(DirPath)); + console.log('reading file: '+ DirPath); + } + } + }; + + + /** + * Функция читает ссылку или выводит информацию об ошибке + * @param pError + * @param pFiles + */ + CloudServer._readDir = function (pError, pFiles) + { + if(pError){ + console.log(pError); + + CloudServer.Statuses[DirPath] = 404; + CloudServer.sendResponse('OK',pError.toString(), + DirPath); + return; + } + + /* Если мы не в корне добавляем слеш к будующим ссылкам */ + if(DirPath !== '/') + DirPath += '/'; + + pFiles = pFiles.sort(); + + var lCount = 0, + lStats = {}; + + /* asyn getting file states + * and putting it to lStats object + */ + var getFilesStat_f = function(pName){ + return function(pError, pStat){ + + var fReturnFalse = function(){ + return false; }; - else - lStats[pName] = pStat; + if(pError) + lStats[pName] = { + 'mode':0, + 'size':0, + 'isDirectory':fReturnFalse + }; + + else + lStats[pName] = pStat; + + /* if this file is last - moving next */ + if(++lCount === pFiles.length) + CloudServer._fillJSON(lStats, pFiles); + }; + }; + + if(pFiles.length) + for(var i = 0; i < pFiles.length; i++) + /* Получаем информацию о файле */ + Fs.stat(DirPath + pFiles[i], getFilesStat_f(pFiles[i])); + + else + CloudServer._fillJSON(null, pFiles); + }; + + /** + * Function fill JSON by file stats + * + * @param pStats - object, contain file stats. + * example {'1.txt': stat} + * + * @param pFiles - array of files of current directory + */ + CloudServer._fillJSON = function(pStats, pFiles){ + /* данные о файлах в формате JSON*/ + var lJSON = [], + lJSONFile = {}, + lHeader, /* заголовок ответа сервера */ + lList; - /* if this file is last - moving next */ - if(++lCount === pFiles.length) - CloudServer._fillJSON(lStats, pFiles); + lJSON[0] = { + path:DirPath, + size:'dir' + }; + + var fReturnFalse = function returnFalse(){return false;}; + + for(var i = 0; i < pFiles.length; i++){ + /* + *Переводим права доступа в 8-ричную систему + */ + var lName = pFiles[i], + + lMode = (pStats[lName].mode-0).toString(8), + lStats = pStats[lName], + lIsDir = lStats.isDirectory(); + + /* Если папка - выводим пиктограмму папки * + * В противоположном случае - файла */ + lJSONFile = { + 'name':pFiles[i], + 'size' : (lIsDir ? 'dir' : lStats.size), + 'uid' : lStats.uid, + 'mode' : lMode}; + + lJSON[i+1] = lJSONFile; + } + + /* если js недоступен + * или отключен выcылаем html-код + * и прописываем соответствующие заголовки + */ + if(CloudServer.NoJS){ + var lPanel = CloudFunc.buildFromJSON(lJSON); + lList = '
      ' + lPanel + '
    ' + + ''; + + /* пробуем достать данные из кэша + * с жатием или без, взависимости + * от настроек + */ + var lFileData = CloudServer.Cache.get(CloudServer.INDEX); + /* если их нет там - вычитываем из файла*/ + if(!lFileData) { + Fs.readFile(CloudServer.INDEX, + CloudServer.indexReaded(lList)); + }else { + var lReaded_f = CloudServer.indexReaded(lList); + lReaded_f(false, lFileData); + } + }else{ + DirPath = DirPath.substr(DirPath, DirPath.lastIndexOf('/') ); + + var lQuyery = CloudServer.Queries[DirPath]; + DirPath += '.json'; + CloudServer.Queries[DirPath] = lQuyery; + + /* в обычном режиме(когда js включен + * высылаем json-структуру файлов + * с соответствующими заголовками + */ + lList = JSON.stringify(lJSON); + lHeader = main.generateHeaders(DirPath, CloudServer.Gzip, lQuyery); + + /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ + if(CloudServer.Gzip){ + Zlib.gzip(lList,CloudServer.getGzipDataFunc(lHeader,CloudServer.INDEX)); + } + /* если не поддерживаеться - отсылаем данные без сжатия*/ + else + CloudServer.sendResponse(lHeader, lList, CloudServer.INDEX); + } + }; + + /** + *@param pList + */ + CloudServer.indexReaded = function(pList){ + return function(pError, pIndex){ + if(pError){ + return console.log(pError); + } + + var lSrv = CloudServer, + lIndexName = lSrv.INDEX; + + /* и сохраняем в кэш */ + lSrv.Cache.set(lIndexName, pIndex); + + pIndex = pIndex.toString(); + + + var lProccessed, + lIndexProccessing = lSrv.indexProcessing; + + lProccessed = Util.exec(lIndexProccessing, { + data : pIndex, + additional : pList + }); + + if(lProccessed) + pIndex = lProccessed; + /* + * если браузер поддерживает gzip-сжатие + * высылаем заголовок в зависимости от типа файла + */ + var lQuery = lSrv.Queries[lIndexName], + lHeader = main.generateHeaders(lIndexName, lSrv.Gzip, lQuery); + + /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ + if(lSrv.Gzip) { + Zlib.gzip(pIndex, + lSrv.getGzipDataFunc(lHeader, lIndexName)); + } + + /* если не поддерживаеться - отсылаем данные без сжатия*/ + else + lSrv.sendResponse(lHeader, pIndex, lIndexName); }; }; - if(pFiles.length) - for(var i = 0; i < pFiles.length; i++) - /* Получаем информацию о файле */ - Fs.stat(DirPath + pFiles[i], getFilesStat_f(pFiles[i])); - - else - CloudServer._fillJSON(null, pFiles); -}; - -/** - * Function fill JSON by file stats - * - * @param pStats - object, contain file stats. - * example {'1.txt': stat} - * - * @param pFiles - array of files of current directory - */ -CloudServer._fillJSON = function(pStats, pFiles){ - /* данные о файлах в формате JSON*/ - var lJSON = [], - lJSONFile = {}, - lHeader, /* заголовок ответа сервера */ - lList; - - lJSON[0] = { - path:DirPath, - size:'dir' - }; - - var fReturnFalse = function returnFalse(){return false;}; - - for(var i = 0; i < pFiles.length; i++){ - /* - *Переводим права доступа в 8-ричную систему - */ - var lName = pFiles[i], - - lMode = (pStats[lName].mode-0).toString(8), - lStats = pStats[lName], - lIsDir = lStats.isDirectory(); - - /* Если папка - выводим пиктограмму папки * - * В противоположном случае - файла */ - lJSONFile = { - 'name':pFiles[i], - 'size' : (lIsDir ? 'dir' : lStats.size), - 'uid' : lStats.uid, - 'mode' : lMode}; - - lJSON[i+1] = lJSONFile; - } - - /* если js недоступен - * или отключен выcылаем html-код - * и прописываем соответствующие заголовки - */ - if(CloudServer.NoJS){ - var lPanel = CloudFunc.buildFromJSON(lJSON); - lList = '
      ' + lPanel + '
    ' + - ''; - - /* пробуем достать данные из кэша - * с жатием или без, взависимости - * от настроек - */ - var lFileData = CloudServer.Cache.get(CloudServer.INDEX); - /* если их нет там - вычитываем из файла*/ - if(!lFileData) { - Fs.readFile(CloudServer.INDEX, - CloudServer.indexReaded(lList)); - }else { - var lReaded_f = CloudServer.indexReaded(lList); - lReaded_f(false, lFileData); - } - }else{ - DirPath = DirPath.substr(DirPath, DirPath.lastIndexOf('/') ); - - var lQuyery = CloudServer.Queries[DirPath]; - DirPath += '.json'; - CloudServer.Queries[DirPath] = lQuyery; - - /* в обычном режиме(когда js включен - * высылаем json-структуру файлов - * с соответствующими заголовками - */ - lList = JSON.stringify(lJSON); - lHeader = main.generateHeaders(DirPath, CloudServer.Gzip, lQuyery); - - /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ - if(CloudServer.Gzip){ - Zlib.gzip(lList,CloudServer.getGzipDataFunc(lHeader,CloudServer.INDEX)); - } - /* если не поддерживаеться - отсылаем данные без сжатия*/ - else - CloudServer.sendResponse(lHeader, lList, CloudServer.INDEX); - } -}; - -/** - *@param pList - */ -CloudServer.indexReaded = function(pList){ - return function(pError, pIndex){ - if(pError){ - return console.log(pError); - } - - var lSrv = CloudServer, - lIndexName = lSrv.INDEX; - - /* и сохраняем в кэш */ - lSrv.Cache.set(lIndexName, pIndex); - - pIndex = pIndex.toString(); - - - var lProccessed, - lIndexProccessing = lSrv.indexProcessing; - - lProccessed = Util.exec(lIndexProccessing, { - data : pIndex, - additional : pList - }); - - if(lProccessed) - pIndex = lProccessed; - /* - * если браузер поддерживает gzip-сжатие - * высылаем заголовок в зависимости от типа файла - */ - var lQuery = lSrv.Queries[lIndexName], - lHeader = main.generateHeaders(lIndexName, lSrv.Gzip, lQuery); - - /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ - if(lSrv.Gzip) { - Zlib.gzip(pIndex, - lSrv.getGzipDataFunc(lHeader, lIndexName)); - } - - /* если не поддерживаеться - отсылаем данные без сжатия*/ - else - lSrv.sendResponse(lHeader, pIndex, lIndexName); - }; -}; - -/** - * Функция генерирует функцию считывания файла - * таким образом, что бы у нас было - * имя считываемого файла - * @param pName - полное имя файла - */ -CloudServer.getReadFileFunc = function(pName){ -/* - * @pError - ошибка - * @pData - данные - * @pFromCache_o - прочитано с файла, - * или из одного из кешей - * Пример {cache: false, minify: true} - */ - var lReadFile = function(pError, pData, pFromCache_o){ - var lSrv = CloudServer; - if (!pError){ - console.log('file ' + pName + ' readed'); - - /* берём из кэша данные файла - * если их нет в кэше - - * сохраняем - */ - if(pFromCache_o && !pFromCache_o.cache && - lSrv.Cache.isAllowed) - lSrv.Cache.set(pName, pData); - - /* если кэш есть - * сохраняем его в переменную - * которая до этого будет пустая - * по скольку мы будем вызывать этот метод - * сами, ведь файл уже вычитан - */ - var lQuery = lSrv.Queries[pName], - lHeader = main.generateHeaders(pName, lSrv.Gzip, lQuery); - - /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ - if( lSrv.Gzip && !(pFromCache_o && pFromCache_o.cache) ) - /* сжимаем содержимое */ - Zlib.gzip(pData,lSrv.getGzipDataFunc(lHeader, pName)); - else - /* высылаем несжатые данные */ - lSrv.sendResponse(lHeader, pData, pName); - } - else{ - console.log(pError.path); - if(pError.path !== 'passwd.json'){ - console.log(pError); - - /* sending page not found */ - lSrv.Statuses[pName] = 404; - lSrv.sendResponse('file not found', pError.toString(), pName); - }else - lSrv.sendResponse('OK', 'passwd.json'); - } - }; - - return lReadFile; -}; - -/** - * Функция получает сжатые данные - * @param pHeader - заголовок файла - * @pName - */ -CloudServer.getGzipDataFunc = function(pHeader, pName){ - return function(error, pResult){ - if(!error){ - /* отправляем сжатые данные - * вместе с заголовком - * если установлена работа с кэшем - * сохраняем сжатые данные - */ - if(CloudServer.Cache.isAllowed){ - /* устанавливаем кєш */ - console.log(pName+' gziped'); - CloudServer.Cache.set(pName+'_gzip', pResult); - } - CloudServer.sendResponse(pHeader, pResult, pName); - } - else{ - console.log(error); - CloudServer.sendResponse(pHeader, error); - } - }; -}; -/** - * Функция высылает ответ серверу - * @param pHead - заголовок - * @param Data - данные - * @param pName - имя отсылаемого файла - */ -CloudServer.sendResponse = function(pHead, pData, pName){ - /* если у нас есть указатель на responce - * для соответствующего файла - - * высылаем его + /** + * Функция генерирует функцию считывания файла + * таким образом, что бы у нас было + * имя считываемого файла + * @param pName - полное имя файла */ - var lResponse = CloudServer.Responses[pName], - lStatus = CloudServer.Statuses[pName]; - - if(lResponse){ - lResponse.writeHead(lStatus, pHead); - lResponse.end(pData); + CloudServer.getReadFileFunc = function(pName){ + /* + * @pError - ошибка + * @pData - данные + * @pFromCache_o - прочитано с файла, + * или из одного из кешей + * Пример {cache: false, minify: true} + */ + var lReadFile = function(pError, pData, pFromCache_o){ + var lSrv = CloudServer; + if (!pError){ + console.log('file ' + pName + ' readed'); + + /* берём из кэша данные файла + * если их нет в кэше - + * сохраняем + */ + if(pFromCache_o && !pFromCache_o.cache && + lSrv.Cache.isAllowed) + lSrv.Cache.set(pName, pData); + + /* если кэш есть + * сохраняем его в переменную + * которая до этого будет пустая + * по скольку мы будем вызывать этот метод + * сами, ведь файл уже вычитан + */ + var lQuery = lSrv.Queries[pName], + lHeader = main.generateHeaders(pName, lSrv.Gzip, lQuery); + + /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ + if( lSrv.Gzip && !(pFromCache_o && pFromCache_o.cache) ) + /* сжимаем содержимое */ + Zlib.gzip(pData,lSrv.getGzipDataFunc(lHeader, pName)); + else + /* высылаем несжатые данные */ + lSrv.sendResponse(lHeader, pData, pName); + } + else{ + console.log(pError.path); + if(pError.path !== 'passwd.json'){ + console.log(pError); + + /* sending page not found */ + lSrv.Statuses[pName] = 404; + lSrv.sendResponse('file not found', pError.toString(), pName); + }else + lSrv.sendResponse('OK', 'passwd.json'); + } + }; - console.log(pName + ' sended'); - } -}; - -/** - * start server function - * @param pConfig - * @param pProcessing {index, appcache, rest} - */ -exports.start = function(pConfig, pProcessing){ - CloudServer.start(pConfig, pProcessing); -}; -exports.CloudServer = CloudServer; \ No newline at end of file + return lReadFile; + }; + + /** + * Функция получает сжатые данные + * @param pHeader - заголовок файла + * @pName + */ + CloudServer.getGzipDataFunc = function(pHeader, pName){ + return function(error, pResult){ + if(!error){ + /* отправляем сжатые данные + * вместе с заголовком + * если установлена работа с кэшем + * сохраняем сжатые данные + */ + if(CloudServer.Cache.isAllowed){ + /* устанавливаем кєш */ + console.log(pName+' gziped'); + CloudServer.Cache.set(pName+'_gzip', pResult); + } + CloudServer.sendResponse(pHeader, pResult, pName); + } + else{ + console.log(error); + CloudServer.sendResponse(pHeader, error); + } + }; + }; + /** + * Функция высылает ответ серверу + * @param pHead - заголовок + * @param Data - данные + * @param pName - имя отсылаемого файла + */ + CloudServer.sendResponse = function(pHead, pData, pName){ + /* если у нас есть указатель на responce + * для соответствующего файла - + * высылаем его + */ + var lResponse = CloudServer.Responses[pName], + lStatus = CloudServer.Statuses[pName]; + + if(lResponse){ + lResponse.writeHead(lStatus, pHead); + lResponse.end(pData); + + console.log(pName + ' sended'); + } + }; + + /** + * start server function + * @param pConfig + * @param pProcessing {index, appcache, rest} + */ + exports.start = function(pConfig, pProcessing){ + CloudServer.start(pConfig, pProcessing); + }; + + exports.CloudServer = CloudServer; +})(); \ No newline at end of file From 6a15b507d2f315608b5754d2695e020385706ae9 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 21 Nov 2012 07:57:31 -0500 Subject: [PATCH 194/281] refactored --- cloudcmd.js | 6 ++++-- lib/server/appcache.js | 2 +- lib/server/auth.js | 13 ++++++------- lib/server/cache.js | 2 +- lib/server/main.js | 2 +- lib/server/minify.js | 2 +- lib/server/rest.js | 2 +- lib/server/socket.js | 4 ++-- lib/server/update.js | 4 ++-- server.js | 8 +++++--- 10 files changed, 24 insertions(+), 21 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 14681c99..5d17b1cb 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -1,8 +1,10 @@ (function(){ "use strict"; - var DIR = process.cwd() + '/', - main = require(DIR + 'lib/server/main'), + global.cloudcmd = {}; + + var DIR = __dirname + '/', + main = require(DIR + 'lib/server/main'), LIBDIR = main.LIBDIR, SRVDIR = main.SRVDIR, diff --git a/lib/server/appcache.js b/lib/server/appcache.js index 1f02ce99..14451f1d 100644 --- a/lib/server/appcache.js +++ b/lib/server/appcache.js @@ -3,7 +3,7 @@ "use strict"; var DIR = process.cwd() + '/', - main = require(DIR + './lib/server/main'), + main = require(DIR + 'lib/server/main'), fs = main.fs, Util = main.util, diff --git a/lib/server/auth.js b/lib/server/auth.js index 025e9ee6..2c533ddd 100644 --- a/lib/server/auth.js +++ b/lib/server/auth.js @@ -2,15 +2,14 @@ (function(){ "use strict"; - var DIR = process.cwd() + '/', - main = require(DIR + 'lib/server/main'), + var DIR = process.cwd() + '/', + main = require(DIR + 'lib/server/main'), - https = main.https, - qs = main.querystring, + https = main.https, + qs = main.querystring, - Config = main.config, - - Util = main.util, + Config = main.config, + Util = main.util, GithubAuth = { host: "github.com", diff --git a/lib/server/cache.js b/lib/server/cache.js index 7bf5ffe2..94d100b6 100644 --- a/lib/server/cache.js +++ b/lib/server/cache.js @@ -2,7 +2,7 @@ "use strict"; var DIR = process.cwd() + '/', - main = require(DIR + 'lib/server/main.js'), + main = require(DIR + 'lib/server/main'), SRVDIR = main.SRVDIR; /* diff --git a/lib/server/main.js b/lib/server/main.js index fbf30bc5..788a1555 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -58,7 +58,7 @@ exports.update = srvrequire('update'), exports.minify = srvrequire('minify').Minify, exports.zlib = mrequire('zlib'); - + /** * function do safe require of needed module diff --git a/lib/server/minify.js b/lib/server/minify.js index 87ceca18..deac66a2 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -4,7 +4,7 @@ "use strict"; var DIR = process.cwd() + '/', - main = require(DIR + 'lib/server/main.js'), + main = require(DIR + 'lib/server/main'), SRVDIR = main.SRVDIR; exports.Minify = { diff --git a/lib/server/rest.js b/lib/server/rest.js index dd713770..1fcab0a8 100644 --- a/lib/server/rest.js +++ b/lib/server/rest.js @@ -4,7 +4,7 @@ "use strict"; var DIR = process.cwd() + '/', - main = require(DIR + 'lib/server/main'), + main = require(DIR + 'lib/server/main'), Util = main.util, APIURL = '/api/v1', OK = 200, diff --git a/lib/server/socket.js b/lib/server/socket.js index 1b574df7..fc59677c 100644 --- a/lib/server/socket.js +++ b/lib/server/socket.js @@ -3,8 +3,8 @@ (function(){ "use strict"; - var DIR = process.cwd() + '/', - main = require(DIR + 'lib/server/main'), + var DIR = process.cwd() + '/', + main = require(DIR + 'lib/server/main'), SRVDIR = main.SRVDIR, io = main.require('socket.io'), diff --git a/lib/server/update.js b/lib/server/update.js index 774b220e..0fc8fb49 100644 --- a/lib/server/update.js +++ b/lib/server/update.js @@ -3,8 +3,8 @@ (function(){ "use strict"; - var DIR = process.cwd() + '/', - main = require(DIR + 'lib/server/main'), + var DIR = process.cwd() + '/', + main = require(DIR + 'lib/server/main'), mainpackage = main.mainpackage, exec = main.child_process.exec; diff --git a/server.js b/server.js index 3153ce4a..b8f7e713 100644 --- a/server.js +++ b/server.js @@ -1,6 +1,6 @@ (function(){ "use strict"; - + /* Обьект содержащий все функции и переменные * серверной части Cloud Commander'а */ @@ -62,8 +62,10 @@ DirPath = '/', OK = 200, - DIR = process.cwd() + '/', - main = require(DIR + 'lib/server/main.js'), + + DIR = process.cwd() + '/', + + main = require(DIR + 'lib/server/main'), LIBDIR = main.LIBDIR, SRVDIR = main.SRVDIR, From e719ab48570414f75edd8a41e6c3f1ee84c6cce8 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 21 Nov 2012 08:00:47 -0500 Subject: [PATCH 195/281] minor changes --- lib/server/update.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/server/update.js b/lib/server/update.js index 0fc8fb49..2279885e 100644 --- a/lib/server/update.js +++ b/lib/server/update.js @@ -3,8 +3,8 @@ (function(){ "use strict"; - var DIR = process.cwd() + '/', - main = require(DIR + 'lib/server/main'), + var DIR = process.cwd() + '/', + main = require(DIR + 'lib/server/main'), mainpackage = main.mainpackage, exec = main.child_process.exec; From 4fd9ceb2c57e3da4d23570a952846b9c74070733 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 21 Nov 2012 08:23:29 -0500 Subject: [PATCH 196/281] refactored --- lib/server/appcache.js | 3 +-- lib/server/auth.js | 3 +-- lib/server/cache.js | 3 +-- lib/server/main.js | 6 ++++-- lib/server/minify.js | 3 +-- lib/server/rest.js | 3 +-- lib/server/socket.js | 3 +-- lib/server/update.js | 5 ++--- server.js | 6 +++--- 9 files changed, 15 insertions(+), 20 deletions(-) diff --git a/lib/server/appcache.js b/lib/server/appcache.js index 14451f1d..ef86fc11 100644 --- a/lib/server/appcache.js +++ b/lib/server/appcache.js @@ -2,8 +2,7 @@ (function(){ "use strict"; - var DIR = process.cwd() + '/', - main = require(DIR + 'lib/server/main'), + var main = global.cloudcmd.main, fs = main.fs, Util = main.util, diff --git a/lib/server/auth.js b/lib/server/auth.js index 2c533ddd..629cd629 100644 --- a/lib/server/auth.js +++ b/lib/server/auth.js @@ -2,8 +2,7 @@ (function(){ "use strict"; - var DIR = process.cwd() + '/', - main = require(DIR + 'lib/server/main'), + var main = global.cloudcmd.main, https = main.https, qs = main.querystring, diff --git a/lib/server/cache.js b/lib/server/cache.js index 94d100b6..054f3c66 100644 --- a/lib/server/cache.js +++ b/lib/server/cache.js @@ -1,8 +1,7 @@ (function(){ "use strict"; - var DIR = process.cwd() + '/', - main = require(DIR + 'lib/server/main'), + var main = global.cloudcmd.main, SRVDIR = main.SRVDIR; /* diff --git a/lib/server/main.js b/lib/server/main.js index 788a1555..21f53f18 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -16,7 +16,8 @@ '.appcache' : 'text/cache-manifest', '.mp3' : 'audio/mpeg' }; - + + global.cloudcmd = {}, /* Constants */ exports.DIR = DIR = process.cwd() + '/', exports.LIBDIR = LIBDIR = DIR + 'lib/', @@ -49,6 +50,7 @@ /* Additional Modules */ + global.cloudcmd.main = exports; exports.auth = srvrequire('auth').auth, exports.appcache = srvrequire('appcache'), exports.cache = srvrequire('cache').Cache, @@ -58,7 +60,7 @@ exports.update = srvrequire('update'), exports.minify = srvrequire('minify').Minify, exports.zlib = mrequire('zlib'); - + global.cloudcmd.main = exports; /** * function do safe require of needed module diff --git a/lib/server/minify.js b/lib/server/minify.js index deac66a2..e612e7db 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -3,8 +3,7 @@ (function(){ "use strict"; - var DIR = process.cwd() + '/', - main = require(DIR + 'lib/server/main'), + var main = global.cloudcmd.main, SRVDIR = main.SRVDIR; exports.Minify = { diff --git a/lib/server/rest.js b/lib/server/rest.js index 1fcab0a8..551abad7 100644 --- a/lib/server/rest.js +++ b/lib/server/rest.js @@ -3,8 +3,7 @@ (function(){ "use strict"; - var DIR = process.cwd() + '/', - main = require(DIR + 'lib/server/main'), + var main = global.cloudcmd.main, Util = main.util, APIURL = '/api/v1', OK = 200, diff --git a/lib/server/socket.js b/lib/server/socket.js index fc59677c..f20f5c14 100644 --- a/lib/server/socket.js +++ b/lib/server/socket.js @@ -3,8 +3,7 @@ (function(){ "use strict"; - var DIR = process.cwd() + '/', - main = require(DIR + 'lib/server/main'), + var main = global.cloudcmd.main, SRVDIR = main.SRVDIR, io = main.require('socket.io'), diff --git a/lib/server/update.js b/lib/server/update.js index 2279885e..c5f4c6f5 100644 --- a/lib/server/update.js +++ b/lib/server/update.js @@ -2,9 +2,8 @@ (function(){ "use strict"; - - var DIR = process.cwd() + '/', - main = require(DIR + 'lib/server/main'), + + var main = global.cloudcmd.main, mainpackage = main.mainpackage, exec = main.child_process.exec; diff --git a/server.js b/server.js index b8f7e713..1b004d95 100644 --- a/server.js +++ b/server.js @@ -606,11 +606,11 @@ var lProccessed, lIndexProccessing = lSrv.indexProcessing; - - lProccessed = Util.exec(lIndexProccessing, { + + lProccessed = Util.exec(lIndexProccessing, { data : pIndex, additional : pList - }); + }); if(lProccessed) pIndex = lProccessed; From c3cfbce39ff0e91ed353808831664b19edc6693c Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 22 Nov 2012 05:57:40 -0500 Subject: [PATCH 197/281] minor changes --- client.js | 29 ++++++++++++---------- lib/client/storage/_github.js | 16 ++++--------- lib/server/main.js | 21 +++++++++++++--- lib/server/rest.js | 45 ++++++++++++++++++++++++++++++----- 4 files changed, 78 insertions(+), 33 deletions(-) diff --git a/client.js b/client.js index 9047c6f5..49e793f8 100644 --- a/client.js +++ b/client.js @@ -758,22 +758,27 @@ CloudClient._createFileTable = function(pElem, pJSON){ * используеться при первом заходе в корень */ CloudClient._getJSONfromFileTable = function(){ - var lLeft = getById('left'); - var lPath = getByClass('path')[0].textContent; - var lFileTable = [{path:lPath,size:'dir'}]; - var lLI = lLeft.getElementsByTagName('li'); + var lLeft = getById('left'), + lPath = getByClass('path')[0].textContent, + + lFileTable = [{ + path:lPath, + size:'dir' + }], + + lLI = lLeft.getElementsByTagName('li'), - var j=1;/* счётчик реальных файлов */ - var i=1;/* счётчик элементов файлов в DOM */ + j = 1; /* счётчик реальных файлов */ + + /* счётчик элементов файлов в DOM */ /* Если путь отличный от корневного * второй элемент li - это ссылка на верхний * каталог '..' */ - i=2; /* пропускам Path и Header*/ - - for(; i Date: Thu, 22 Nov 2012 07:27:45 -0500 Subject: [PATCH 198/281] improved reading file with streams --- ChangeLog | 2 ++ config.json | 2 +- lib/server/main.js | 32 ++++++++++++++++++- lib/server/rest.js | 22 +++++-------- server.js | 77 ++++++++++++++++++++++++---------------------- 5 files changed, 82 insertions(+), 53 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1b1aaabf..02a72f5d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -112,6 +112,8 @@ set enveronment varibles "oauth_client_id" and * Changed funcyBox version to 2.1.3. +* Improved reading file with streams. + 2012.10.01, Version 0.1.7 diff --git a/config.json b/config.json index 8a7fcbb7..045e85e1 100644 --- a/config.json +++ b/config.json @@ -2,7 +2,7 @@ "cache" : {"allowed" : false}, "appcache" : false, "minification" : { - "js" : true, + "js" : false, "css" : true, "html" : true, "img" : true diff --git a/lib/server/main.js b/lib/server/main.js index 84785821..cc64f3e7 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -6,6 +6,7 @@ SRVDIR, Util, + OK = 200, Extensions = { '.css' : 'text/css', '.js' : 'text/javascript', @@ -28,6 +29,7 @@ /* Functions */ exports.generateHeaders = generateHeaders, + exports.sendFile = sendFile, exports.require = mrequire, exports.librequire = librequire, exports.srvrequire = srvrequire, @@ -75,7 +77,7 @@ * second initializing after all modules load, so global var is * totally filled of all information that should know all modules */ - global.cloudcmd.main = exports; + var main = global.cloudcmd.main = exports; /** * function do safe require of needed module @@ -152,5 +154,33 @@ return lRet; } + + /** + * send file to client thru pipe + * and gzip it if client support + * + * @param pName - имя файла + * @param pGzip - данные сжаты gzip'ом + */ + function sendFile(pParams){ + var lName = pParams.name, + lReq = pParams.request, + lRes = pParams.response, + + lEnc = lReq.headers['accept-encoding'] || '', + lGzip = lEnc.match(/\bgzip\b/), + + lReadStream = main.fs.createReadStream(lName, { + 'bufferSize': 4 * 1024 + }); + + lRes.writeHead(OK, generateHeaders(lName, true) ); + + + if (lGzip) + lReadStream = lReadStream.pipe( main.zlib.createGzip() ); + + lReadStream.pipe(lRes); + } })(); diff --git a/lib/server/rest.js b/lib/server/rest.js index 363ef5d8..d0aa89b4 100644 --- a/lib/server/rest.js +++ b/lib/server/rest.js @@ -31,6 +31,7 @@ command : lCommand, method : lMethod, body : pBody, + request : lReq, response : lRes }); @@ -57,7 +58,7 @@ /** * getting data on method and command * - * @param pParams {command, method, body, response} + * @param pParams {command, method, body, requrest, response} */ function getData(pParams){ var lResult, @@ -85,7 +86,7 @@ /** * process data on GET request * - * @param pParams {command, method, body, response} + * @param pParams {command, method, body, requrest, response} */ function onGET(pParams){ var lResult = {error: 'command not found'}, @@ -114,7 +115,7 @@ /** * process data on PUT request * - * @param pParams {command, method, body, response} + * @param pParams {command, method, body, requrest, response} */ function onPUT(pParams){ var lResult = {error: 'command not found'}, @@ -128,23 +129,16 @@ send(lRes, pTocken); }); lResult = false; + break; - /* Example: - * read=[lib/dom.js, lib/cloudfunc.js, client.js] - */ case 'read': console.log(lBody); var lFiles = lBody; - + if( Util.isString(lFiles) ){ - lRes.writeHead(OK, main.generateHeaders(lFiles, true) ); - - fs.createReadStream(lFiles, { - 'bufferSize': 4 * 1024 - }) - .pipe( zlib.createGzip() ) - .pipe(lRes); + pParams.name = lFiles; + main.sendFile(pParams); lResult = null; } diff --git a/server.js b/server.js index 1b004d95..b1fccefd 100644 --- a/server.js +++ b/server.js @@ -246,12 +246,13 @@ !Util.isContainStr(pathname, lNoJS_s) && !Util.strCmp(pathname, '/') && !Util.strCmp(lQuery, 'json') ) { - /* если имена файлов проекта - загружаем их*/ - /* убираем слеш и читаем файл с текущец директории*/ + + /* если имена файлов проекта - загружаем их * + * убираем слеш и читаем файл с текущец директории */ /* добавляем текующий каталог к пути */ var lName = '.' + pathname; - console.log('reading '+lName); + console.log('reading ' + lName); /* watching is file changed */ if(lConfig.appcache) @@ -283,20 +284,21 @@ * and in Minifys cache is files, so save it to * CloudServer cache */ - if(!lFileData && - lMinify._allowed){ - console.log('trying to read data from Minify.Cache'); - lFromCache_o.cache = false; - lFileData = CloudServer.Minify.Cache[ - Path.basename(lName)]; + if(!lFileData && lMinify._allowed){ + console.log('trying to read data from Minify.Cache'); + + lFromCache_o.cache = false; + + lFileData = CloudServer.Minify.Cache[ + Path.basename(lName)]; } var lReadFileFunc_f = CloudServer.getReadFileFunc(lName), /* если там что-то есть передаём данные в функцию readFile */ - lResult = true; + lResult = lFileData; - if(lFileData){ - /* if file readed not from cache - - * he readed from minified cache + if(lResult){ + /* if file readed not from cache - + * he readed from minified cache */ lFromCache_o.minify = !lFromCache_o.cache; @@ -311,33 +313,34 @@ /* if file not in one of caches * and minimizing setted then minimize it */ - else if(lName.indexOf('min') < 0 && - CloudServer.Minify){ - var lMin_o = lConfig.minification, + else if(lName.indexOf('min') < 0 && CloudServer.Minify){ + var lMin_o = lConfig.minification, - lCheck_f = function(pExt){ - return Util.checkExtension(lName,pExt); - }, - - isAllowd_b = (lCheck_f('js') && lMin_o.js) || - (lCheck_f('css') && lMin_o.css) || - (lCheck_f('html') && lMin_o.html); - - if(isAllowd_b){ - lResult = CloudServer.Minify.optimize(lName, { - cache: true, - callback: function(pFileData){ - lReadFileFunc_f(undefined, pFileData, false); - } - }); - } - else - lResult = false; - } else - lResult = false; + lCheck_f = function(pExt){ + return Util.checkExtension(lName,pExt); + }; + + lResult = (lCheck_f('js') && lMin_o.js) || + (lCheck_f('css') && lMin_o.css) || + (lCheck_f('html') && lMin_o.html); + if(lResult){ + lResult = CloudServer.Minify.optimize(lName, { + cache: true, + callback: function(pFileData){ + lReadFileFunc_f(undefined, pFileData, false); + } + }); + } + } + if(!lResult) - Fs.readFile(lName, lReadFileFunc_f); + main.sendFile({ + name: lName, + request: pReq, + response: pRes + }); + }else{/* если мы имеем дело с файловой системой*/ /* если путь не начинаеться с no-js - значит * js включен From 3e669b4f6f1f3b72da9f1de34834127e9b13e993 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 22 Nov 2012 07:57:27 -0500 Subject: [PATCH 199/281] minor changes --- lib/server/main.js | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/lib/server/main.js b/lib/server/main.js index cc64f3e7..34556c64 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -7,6 +7,7 @@ Util, OK = 200, + ERROR = 404, Extensions = { '.css' : 'text/css', '.js' : 'text/javascript', @@ -163,24 +164,37 @@ * @param pGzip - данные сжаты gzip'ом */ function sendFile(pParams){ - var lName = pParams.name, + var lRet = false, + lName = pParams.name, lReq = pParams.request, lRes = pParams.response, lEnc = lReq.headers['accept-encoding'] || '', lGzip = lEnc.match(/\bgzip\b/), - - lReadStream = main.fs.createReadStream(lName, { - 'bufferSize': 4 * 1024 - }); - - lRes.writeHead(OK, generateHeaders(lName, true) ); - + lReadStream, + lExists = main.fs.exists || main.path.exists; - if (lGzip) - lReadStream = lReadStream.pipe( main.zlib.createGzip() ); - - lReadStream.pipe(lRes); + lExists(lName, function(pExist){ + lRet = pExist; + if(pExist){ + lReadStream = main.fs.createReadStream(lName, { + 'bufferSize': 4 * 1024 + }); + lRes.writeHead(OK, generateHeaders(lName, lGzip) ); + + if (lGzip) + lReadStream = lReadStream.pipe( main.zlib.createGzip() ); + + lReadStream.pipe(lRes); + } + else{ + lRes.writeHead(OK, generateHeaders('error.json', lGzip) ); + var lJson = JSON.stringify({error: 'File not Found'}); + lRes.end(lJson); + } + }); + + return lRet; } })(); From b1c6c5e3edb2b8565e089336eaec52d8a39dd94f Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 22 Nov 2012 08:03:42 -0500 Subject: [PATCH 200/281] minor changes --- lib/server/main.js | 3 ++- server.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/server/main.js b/lib/server/main.js index 34556c64..284a5f5a 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -172,6 +172,7 @@ lEnc = lReq.headers['accept-encoding'] || '', lGzip = lEnc.match(/\bgzip\b/), lReadStream, + /* compitability with old versions of node */ lExists = main.fs.exists || main.path.exists; lExists(lName, function(pExist){ @@ -188,7 +189,7 @@ lReadStream.pipe(lRes); } else{ - lRes.writeHead(OK, generateHeaders('error.json', lGzip) ); + lRes.writeHead(ERROR, 'OK'); var lJson = JSON.stringify({error: 'File not Found'}); lRes.end(lJson); } diff --git a/server.js b/server.js index b1fccefd..82e64e5c 100644 --- a/server.js +++ b/server.js @@ -422,7 +422,7 @@ CloudServer._stated = function(pError, pStat){ if(pError){ CloudServer.Statuses[DirPath] = 404; - CloudServer.sendResponse('OK',pError.toString(), DirPath); + CloudServer.sendResponse('OK', pError.toString(), DirPath); return; } From 2ed4a37f70d21f9e9fd5efc130d9ca4d8b14edbe Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 22 Nov 2012 08:45:20 -0500 Subject: [PATCH 201/281] minor changes --- lib/server/main.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/server/main.js b/lib/server/main.js index 284a5f5a..08c43524 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -44,7 +44,10 @@ exports.https = require('https'), exports.path = require('path'), exports.url = require('url'), - exports.querystring = require('querystring'), + exports.querystring = require('querystring'), + + /* compitability with old versions of node */ + exports.fs.exists = exports.fs.exists || exports.path.exists; /* Needed Modules */ exports.util = Util = require(LIBDIR + 'util'), @@ -171,11 +174,9 @@ lEnc = lReq.headers['accept-encoding'] || '', lGzip = lEnc.match(/\bgzip\b/), - lReadStream, - /* compitability with old versions of node */ - lExists = main.fs.exists || main.path.exists; + lReadStream; - lExists(lName, function(pExist){ + main.fs.exists(lName, function(pExist){ lRet = pExist; if(pExist){ lReadStream = main.fs.createReadStream(lName, { @@ -189,8 +190,11 @@ lReadStream.pipe(lRes); } else{ + var lJson = JSON.stringify({ + error: 'File not Found' + }); + lRes.writeHead(ERROR, 'OK'); - var lJson = JSON.stringify({error: 'File not Found'}); lRes.end(lJson); } }); From d04e4f250d6709b5a43f1d88776ae1a16a8d3ace Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 22 Nov 2012 10:41:54 -0500 Subject: [PATCH 202/281] minor changes --- lib/server/rest.js | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/lib/server/rest.js b/lib/server/rest.js index d0aa89b4..96cefe63 100644 --- a/lib/server/rest.js +++ b/lib/server/rest.js @@ -36,7 +36,10 @@ }); if(lData) - send(lRes, lData); + send({ + response : lRes, + data : lData + }); }); } @@ -50,9 +53,18 @@ * @param pRes * @param pData */ - function send(pRes, pData){ - pRes.writeHead(OK, Header); - pRes.end( JSON.stringify(pData) ); + function send(pParams){ + var lRes = pParams.response, + lData = pParams.data, + lEndListener = pParams.endListener; + + lRes.writeHead(OK, Header); + lRes.end( JSON.stringify(lData) ); + + if(lEndListener) + lRes.on('end', function(){ + Util.exec(lEndListener); + }); } /** @@ -105,7 +117,13 @@ break; case 'kill': - process.exit(); + pParams.data = { + mesage: 'Cloud Commander was killed' + }; + + pParams.endListener = Util.retExec(process.exit); + + send(pParams); break; } @@ -126,10 +144,13 @@ switch(lCmd){ case 'auth': main.auth(lBody, function(pTocken){ - send(lRes, pTocken); + send({ + response: lRes, + data: pTocken + }); }); - lResult = false; + lResult = false; break; case 'read': From ebb873975cdf675cbd58e8bd7bada1ef19215855 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 22 Nov 2012 10:48:09 -0500 Subject: [PATCH 203/281] minor changes --- lib/server/rest.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/server/rest.js b/lib/server/rest.js index 96cefe63..9e37325e 100644 --- a/lib/server/rest.js +++ b/lib/server/rest.js @@ -5,8 +5,6 @@ var main = global.cloudcmd.main, Util = main.util, - fs = main.fs, - zlib = main.zlib, APIURL = '/api/v1', OK = 200, Header = main.generateHeaders('api.json', false); @@ -124,6 +122,8 @@ pParams.endListener = Util.retExec(process.exit); send(pParams); + + lResult = null; break; } From 5de6660f0f07cd6498fa3e032822c81288fce39c Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 22 Nov 2012 10:51:04 -0500 Subject: [PATCH 204/281] added --- lib/client/viewer/fancybox/CHANGELOG.md | 115 +++++++++++++ lib/client/viewer/fancybox/README.md | 217 ++++++++++++++++++++++++ 2 files changed, 332 insertions(+) create mode 100644 lib/client/viewer/fancybox/CHANGELOG.md create mode 100644 lib/client/viewer/fancybox/README.md diff --git a/lib/client/viewer/fancybox/CHANGELOG.md b/lib/client/viewer/fancybox/CHANGELOG.md new file mode 100644 index 00000000..07c987e2 --- /dev/null +++ b/lib/client/viewer/fancybox/CHANGELOG.md @@ -0,0 +1,115 @@ +fancyBox - Changelog +========= + +### Version 2.1.3 - October 23, 2012 + +* Fixed #426 - Broken IE7 +* Fixed #423 - Background flickering on iOS +* Fixed #418 - Automatically Grow/Shrink and Center +* Updated the script to work with jQuery 1.6 +* Media helper supports YouTube video series + +### Version 2.1.2 - October 15, 2012 + +* Fixed #414 - Don't allow nextClick if there is only one item +* Fixed #397 - Button helper 'Menu' not visible in IE7 +* Overlay can be opened/closed manually: +* $.fancybox.helpers.overlay.open(); +* $.fancybox.helpers.overlay.open({closeClick : false}); +* $.fancybox.helpers.overlay.close(); +* Optimized for Internet Explorer 10 (Windows 8) + +### Version 2.1.1 - October 01, 2012 + +* Fixed #357 - Converting values like 'auto' in getScalar() +* Fixed #358 - Updated overlay background image +* New "fancybox-href" and "fancybox-title" HTML5 data-attributes (#317) +* Improved helpers: +* - now they can have a property 'defaults' that contains default settings +* - updated vimeo and youtube parsers for media helper +* Content locking now can be turned off + +### Version 2.1.0 - August 20, 2012 + +* Fixed #103 - DOM element re-injection after closing +* Fixed #188 - navigation keys inside editable content +* New animation directions (see https://github.com/fancyapps/fancyBox/issues/233#issuecomment-5512453) +* New option "iframe" - it is now possible to separate scrolling for iframe and wrapping element; choose to preload +* New option "swf" - brings back functionality from fancyBox v1 +* Improved media helper - better support for vimeo and youtube; links are now configurable +* Rewritten overlay helper: +* - new option "showEarly" - toggles if should be open before of after content is loaded +* - Facebook-style (https://github.com/fancyapps/fancyBox/issues/24) and therefore uses image for background +* Option "padding" accepts array (e.g., padding: [15, 50, 10, 5]) +* One of dimensions (width or height) can now be set to "auto" (option "autoSize" needs to be "false") +* Updated callbacks: +* - "beforeClose" is now called only once +* - "afterLoad" receives current and previous object as arguments +* Method "$.fancybox.update();" recalculates content width/height +* Updated to work with jQuery v1.8 + +### Version 2.0.6 - April 16, 2012 + +* Fixed #188 - keystrokes in contenteditable +* Fixed #171 - non-images should not be preloaded +* Fixed #158 - 'closeClick: true' breaks gallery navigation +* New "media" helper - detects and displays various media types +* New option "groupAttr" - name of group selector attribute, default is "data-fancybox-group" +* New feature - selector expressions in URLs, see #170 +* Improved 'overlay' helper to use "position: fixed" +* Improved autoSize, fixed wrong height in some cases +* Improved centering and iframe scrolling for iOS +* Updated markup, new element '.fancybox-skin' is now used for styling + +### Version 2.0.5 - February 21, 2012 + +* Fixed #155 - easing for prev/next animations +* Fixed #153 - overriding "keys" options +* Fixed #147 - IE7 problem with #hash links +* Fixed #130 - changing dynamically data-fancybox-group +* Fixed #126 - obey minWidth/minHeight +* Fixed #118 - placement of loading icon and navigation arrows +* Fixed #101 - "index" option not working +* Fixed #94 - "orig" option not working +* Fixed #80 - does not work on IE6 +* Fixed #72 - can't set overlay opacity to 0 +* Fixed #63 - properly set gallery index +* New option "autoCenter" - toggles centering on window resize or scroll, disabled for mobile devices by default +* New option "autoResize" - toggles responsivity, disabled for mobile devices by default +* New option "preload" - number of images to preload +* New feature to target mobile/desktop browsers using CSS, see #108 +* Changed ajax option defaults to "{ dataType: 'html', headers: { 'X-fancyBox': true } }", see #150 and #128 +* Updated loading icon for IE7, IE8 +* Calculates height of the iframe if 'autoSize' is set to 'true' and the iframe is on the same domain as the main page + +### Version 2.0.4 - December 12, 2011 + +* Fixed #47 - fix overriding properties +* New option "position" to thumbnail and button helpers + + +### Version 2.0.3 - November 29, 2011 + +* Fixed #29 - broken elastic transitions + + +### Version 2.0.2 - November 28, 2011 + +* Fixed slideshow +* Fixed scrollbars issue when displayed a very tall image +* New option "nextClick" - navigate to next gallery item when user clicks the content +* New option "modal" - to disable navigation and closing +* Add 'metadata' plugin support +* Add ability to create groups using 'data-fancybox-group' attribute +* Updated manual usage to match earlier releases + + +### Version 2.0.1 - November 23, 2011 + +* Fixed keyboard events inside form elements +* Fixed manual usage + + +### Version 2.0.0 - November 21, 2011 + +First release - completely rewritten, many new features and updated graphics. \ No newline at end of file diff --git a/lib/client/viewer/fancybox/README.md b/lib/client/viewer/fancybox/README.md new file mode 100644 index 00000000..94348937 --- /dev/null +++ b/lib/client/viewer/fancybox/README.md @@ -0,0 +1,217 @@ +fancyBox +======== + +fancyBox is a tool that offers a nice and elegant way to add zooming functionality for images, html content and multi-media on your webpages. + +More information and examples: http://www.fancyapps.com/fancybox/ + +License: http://www.fancyapps.com/fancybox/#license + +Copyright (c) 2012 Janis Skarnelis - janis@fancyapps.com + + +How to use +---------- + +To get started, download the plugin, unzip it and copy files to your website/application directory. +Load files in the section of your HTML document. Make sure you also add the jQuery library. + + + + + + + +Create your links with a `title` if you want a title to be shown, and add a class: + + + +If you have a set of related items that you would like to group, +additionally include a group name in the `rel` (or `data-fancybox-group`) attribute: + + + + +Initialise the script like this: + + + +May also be passed an optional options object which will extend the default values. Example: + + + +Tip: Automatically group and apply fancyBox to all images: + + $("a[href$='.jpg'],a[href$='.jpeg'],a[href$='.png'],a[href$='.gif']").attr('rel', 'gallery').fancybox(); + +Script uses the `href` attribute of the matched elements to obtain the location of the content and to figure out content type you want to display. +You can specify type directly by adding classname (fancybox.image, fancybox.iframe, etc) or `data-fancybox-type` attribute: + + //Ajax: + Example + //or + Example + + //Iframe: + Example + + //Inline (will display an element with `id="example"`) + Example + + //SWF: + Example + + //Image: + Example + +Note, ajax requests are subject to the [same origin policy](http://en.wikipedia.org/wiki/Same_origin_policy). +If fancyBox will not be able to get content type, it will try to guess based on 'href' and will quit silently if would not succeed. +(this is different from previsous versions where 'ajax' was used as default type or an error message was displayed). + +Advanced +-------- + +### Helpers + +Helpers provide a simple mechanism to extend the capabilities of fancyBox. There are two built-in helpers - 'overlay' and 'title'. +You can disable them, set custom options or enable other helpers. Examples: + + //Disable title helper + $(".fancybox").fancybox({ + helpers: { + title: null + } + }); + + //Disable overlay helper + $(".fancybox").fancybox({ + helpers: { + overlay : null + } + }); + + //Change title position and overlay color + $(".fancybox").fancybox({ + helpers: { + title : { + type : 'inside' + }, + overlay : { + css : { + 'background' : 'rgba(255,255,255,0.5)' + } + } + } + }); + + //Enable thumbnail helper and set custom options + $(".fancybox").fancybox({ + helpers: { + thumbs : { + width: 50, + height: 50 + } + } + }); + + +### API + +Also available are event driven callback methods. The `this` keyword refers to the current or upcoming object (depends on callback method). Here is how you can change title: + + $(".fancybox").fancybox({ + beforeLoad : function() { + this.title = 'Image ' + (this.index + 1) + ' of ' + this.group.length + (this.title ? ' - ' + this.title : ''); + + /* + "this.element" refers to current element, so you can, for example, use the "alt" attribute of the image to store the title: + this.title = $(this.element).find('img').attr('alt'); + */ + } + }); + +It`s possible to open fancyBox programmatically in various ways: + + //HTML content: + $.fancybox( '

    Lorem Lipsum

    Lorem lipsum

    ', { + title : 'Custom Title' + }); + + //DOM element: + $.fancybox( $("#inline"), { + title : 'Custom Title' + }); + + //Custom object: + $.fancybox({ + href: 'example.jpg', + title : 'Custom Title' + }); + + //Array of objects: + $.fancybox([ + { + href: 'example1.jpg', + title : 'Custom Title 1' + }, + { + href: 'example2.jpg', + title : 'Custom Title 2' + } + ], { + padding: 0 + }); + +There are several methods that allow you to interact with and manipulate fancyBox, example: + + //Close fancybox: + $.fancybox.close(); + +There is a simply way to access wrapping elements using JS: + + $.fancybox.wrap + $.fancybox.skin + $.fancybox.outer + $.fancybox.inner + +You can override CSS to customize the look. For example, make navigation arrows always visible, +change width and move them outside of area (use this snippet after including fancybox.css): + + .fancybox-nav span { + visibility: visible; + } + + .fancybox-nav { + width: 80px; + } + + .fancybox-prev { + left: -80px; + } + + .fancybox-next { + right: -80px; + } + +In that case, you might want to increase space around box: + + $(".fancybox").fancybox({ + margin : [20, 60, 20, 60] + }); + + +Bug tracker +----------- + +Have a bug? Please create an issue on GitHub at https://github.com/fancyapps/fancyBox/issues \ No newline at end of file From de2cd5191511694ec95f9290cb06fca46b754f89 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 22 Nov 2012 10:55:40 -0500 Subject: [PATCH 205/281] minor changes --- lib/server/rest.js | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/lib/server/rest.js b/lib/server/rest.js index 9e37325e..b66d77f9 100644 --- a/lib/server/rest.js +++ b/lib/server/rest.js @@ -53,16 +53,10 @@ */ function send(pParams){ var lRes = pParams.response, - lData = pParams.data, - lEndListener = pParams.endListener; + lData = pParams.data; lRes.writeHead(OK, Header); lRes.end( JSON.stringify(lData) ); - - if(lEndListener) - lRes.on('end', function(){ - Util.exec(lEndListener); - }); } /** @@ -118,9 +112,6 @@ pParams.data = { mesage: 'Cloud Commander was killed' }; - - pParams.endListener = Util.retExec(process.exit); - send(pParams); lResult = null; From 61c220ca6691deaf2db860657368b58c120752c5 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 23 Nov 2012 06:27:50 -0500 Subject: [PATCH 206/281] Improved download speed of js and css files --- ChangeLog | 5 ++++ lib/client/dom.js | 44 ++++++++++++++++++++++++++++++ lib/client/editor/_codemirror.js | 26 +++++++++--------- lib/client/menu.js | 4 +-- lib/client/storage/_github.js | 19 ++++++------- lib/client/terminal.js | 6 ++--- lib/client/viewer.js | 3 ++- lib/util.js | 46 ++++++++++++++++++++++++++++++-- 8 files changed, 124 insertions(+), 29 deletions(-) diff --git a/ChangeLog b/ChangeLog index 02a72f5d..379260b1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -114,6 +114,11 @@ set enveronment varibles "oauth_client_id" and * Improved reading file with streams. +* Improved download speed of js and css files by +adding DOM.anyLoadInParallel function that is +loading files in parallel and then execute +callback function. + 2012.10.01, Version 0.1.7 diff --git a/lib/client/dom.js b/lib/client/dom.js index f2632656..5c13b219 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -269,6 +269,50 @@ var CloudCommander, Util, DOM, CloudFunc; } }; + /** + * improve callback of funcs so + * we pop number of function and + * if it's last we call pCallBack + * + * @param pParams_a + * @param pFunc - onload function + */ + DOM.anyLoadInParallel = function(pParams_a, pFunc){ + var lRet = Util.isArray(pParams_a), + done = [], + + doneFunc = function (pCallBack){ + Util.exec(pCallBack); + + if( !done.pop() ) + Util.exec(pFunc); + }; + + if(!lRet){ + pParams_a = [pParams_a]; + } + + for(var i = 0, n = pParams_a.length; i < n; i++){ + var lParam = pParams_a.pop(); + + if(lParam){ + done.push(i); + + if(Util.isString(lParam) ) + lParam = { src : lParam }; + + var lFunc = lParam.func; + lParam.func = Util.retExec(doneFunc, lFunc); + + DOM.anyload(lParam); + + lRet = true; + } + } + + return lRet; + }; + /** * Функция создаёт элемент и загружает файл с src. * diff --git a/lib/client/editor/_codemirror.js b/lib/client/editor/_codemirror.js index eaf11c55..4a501505 100644 --- a/lib/client/editor/_codemirror.js +++ b/lib/client/editor/_codemirror.js @@ -85,18 +85,20 @@ var CloudCommander, Util, DOM, CloudFunc, CodeMirror; console.time('codemirror load'); var lDir = cloudcmd.LIBDIRCLIENT + 'editor/codemirror/'; - DOM.anyLoadOnLoad([ - lDir + 'codemirror.css', - lDir + 'theme/night.css', - lDir + 'mode/javascript.js', - lDir + 'codemirror.js'], - - function(){ - console.timeEnd('codemirror load'); - CodeMirrorLoaded = true; - Util.exec(pCallBack); - } - ); + DOM.jsload(lDir + 'codemirror.js', function(){ + DOM.anyLoadInParallel([ + lDir + 'codemirror.css', + lDir + 'theme/night.css', + lDir + 'mode/javascript.js', + ], + + function(){ + console.timeEnd('codemirror load'); + CodeMirrorLoaded = true; + Util.exec(pCallBack); + }); + }); + } /** diff --git a/lib/client/menu.js b/lib/client/menu.js index 297ada83..50d7f9cf 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -105,10 +105,10 @@ var CloudCommander, Util, DOM, CloudFunc, $; */ function load(pCallBack){ console.time('menu load'); - + var lDir = Menu.dir; - DOM.anyLoadOnLoad([ + DOM.anyLoadInParallel([ lDir + 'contextMenu.js', lDir + 'contextMenu.css'], function(){ diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index 3a6e59a4..3eac075f 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -29,16 +29,17 @@ var CloudCommander, Util, DOM, $, Github, cb; console.time('github load'); var lDir = './lib/client/storage/github/'; - DOM.anyLoadOnLoad([ - lDir + 'github.js', - lDir + 'lib/base64.js', - lDir + 'lib/underscore.js'], - - function(){ - console.timeEnd('github load'); - DOM.Images.hideLoad(); + DOM.jsload(lDir + 'github.js', function(){ + DOM.anyLoadOnLoad([ + lDir + 'lib/base64.js', + lDir + 'lib/underscore.js'], - Util.exec(pCallBack); + function(){ + console.timeEnd('github load'); + DOM.Images.hideLoad(); + + Util.exec(pCallBack); + }); }); } diff --git a/lib/client/terminal.js b/lib/client/terminal.js index 5ce4b421..ff1d4c88 100644 --- a/lib/client/terminal.js +++ b/lib/client/terminal.js @@ -23,11 +23,11 @@ var CloudCommander, Util, DOM, $; console.time('terminal load'); var lDir = 'lib/client/terminal/jquery-terminal/jquery.'; - DOM.cssLoad(lDir + 'terminal.css'); - DOM.anyLoadOnLoad([ + DOM.anyLoadInParallel([ lDir + 'terminal.js', - lDir + 'mousewheel.js'], + lDir + 'mousewheel.js', + lDir + 'terminal.css'], function(){ console.timeEnd('terminal load'); diff --git a/lib/client/viewer.js b/lib/client/viewer.js index d15a5edf..9237d355 100644 --- a/lib/client/viewer.js +++ b/lib/client/viewer.js @@ -98,7 +98,8 @@ var CloudCommander, Util, DOM, CloudFunc, $; console.time('fancybox load'); var lDir = cloudcmd.LIBDIRCLIENT + 'viewer/fancybox/'; - DOM.anyLoadOnLoad([ + + DOM.anyLoadInParallel([ lDir + 'jquery.fancybox.css', lDir + 'jquery.fancybox.js'], diff --git a/lib/util.js b/lib/util.js index 8ca872c7..d7af7033 100644 --- a/lib/util.js +++ b/lib/util.js @@ -28,8 +28,7 @@ var Util, exports; * @param pName - получает имя файла * @param pExt - расширение */ - Util.checkExtension = function(pName, pExt) - { + Util.checkExtension = function(pName, pExt){ /* если длина имени больше * длинны расширения - * имеет смысл продолжать @@ -118,6 +117,49 @@ var Util, exports; Util.removeStr = function(pStr, pSubStr){ return pStr.replace(pSubStr,''); }; + + /** + * invoke a couple of functions in paralel + * + * @param {Array} pFuncs + * @param {function} pCallback + * + * Example: + * i >=0, pFuncs[i] = function(param, callback){} + */ + Util.paralelExec = function(pFuncs, pCallback){ + var done = []; + + /* add items to array done*/ + function addFunc(pNum){ + done.push(pNum); + } + + /* + * improve callback of funcs so + * we pop number of function and + * if it's last we call pCallBack + */ + function doneFunc(pParams){ + Util.exec(pParams.callback); + + var lNum = done.pop (pParams.number); + if(!lNum){ + Util.exec(pCallback); + } + } + + for(var i = 0, n = pFuncs.length; i < n; i++){ + addFunc(i); + + var lFunc = pFuncs[i].callback; + + pFuncs[i].callback = Util.retExec(doneFunc, { + number : i, + callback : lFunc + }); + } + }; /** * functions check is pVarible is array From 4d6d0050304cd267504f6ac21179adede5edf93a Mon Sep 17 00:00:00 2001 From: coderaiser Date: Sun, 25 Nov 2012 05:15:23 -0500 Subject: [PATCH 207/281] minor changes --- cloudcmd.js | 2 +- lib/client/storage/_github.js | 2 +- lib/client/storage/github/github.js | 37 +++++++++++++++++++++++++++-- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 5d17b1cb..a710a596 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -69,7 +69,7 @@ function appCacheProcessing(){ var lAppCache = srv.AppCache, - + lFiles = [ {'//themes.googleusercontent.com/static/fonts/droidsansmono/v4/ns-m2xQYezAtqh7ai59hJUYuTAAIFFn5GTWtryCmBQ4.woff' : './font/DroidSansMono.woff'}, {'//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js' : './lib/client/jquery.js'}]; diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index 3eac075f..44ea9997 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -73,7 +73,7 @@ var CloudCommander, Util, DOM, $, Github, cb; }; GithubStore.Login = function(pToken){ - cloudcmd.Storage.Github = GithubLocal = new Github({ + cloudcmd.Storage.Github = Github = GithubLocal = new Github({ token : pToken, auth : 'oauth' }); diff --git a/lib/client/storage/github/github.js b/lib/client/storage/github/github.js index 68b570ef..f01c44f3 100644 --- a/lib/client/storage/github/github.js +++ b/lib/client/storage/github/github.js @@ -81,8 +81,8 @@ this.show = function(username, cb) { var command = username ? "/users/"+username : "/user"; - - _request("GET", command, null, function(err, res) { + + _request("GET", "/users/"+username, null, function(err, res) { cb(err, res); }); }; @@ -113,6 +113,24 @@ cb(err, res); }); }; + + // Follow user + // ------- + + this.follow = function(username, cb) { + _request("PUT", "/user/following/"+username, null, function(err, res) { + cb(err, res); + }); + }; + + // Unfollow user + // ------- + + this.unfollow = function(username, cb) { + _request("DELETE", "/user/following/"+username, null, function(err, res) { + cb(err, res); + }); + }; }; @@ -419,6 +437,21 @@ }); }; + // Create the gist + // -------- + // { + // "description": "the description for this gist", + // "public": true, + // "files": { + // "file1.txt": { + // "content": "String file contents" + // } + // } + // } + + this.create = function(options, cb){ + _request("POST", "/gists", options, cb); + }; // Delete the gist // -------- From 2342988071eec0468af2ea8f6814d5cbf8575015 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 26 Nov 2012 07:28:02 -0500 Subject: [PATCH 208/281] added ability to load a couple scripts after one main is loaded in any position in anyLoadOnLoad function --- lib/client/dom.js | 28 ++++++++++++++++------------ lib/client/editor/_codemirror.js | 24 +++++++++++++----------- lib/client/storage/_github.js | 23 +++++++++++------------ 3 files changed, 40 insertions(+), 35 deletions(-) diff --git a/lib/client/dom.js b/lib/client/dom.js index 5c13b219..4ee89d94 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -91,7 +91,7 @@ var CloudCommander, Util, DOM, CloudFunc; return lRet_b; }; - /** + /** * safe add event listener * @param pType * @param pListener @@ -100,7 +100,7 @@ var CloudCommander, Util, DOM, CloudFunc; */ DOM.addListener = function(pType, pListener, pUseCapture, pElement){ return (pElement || document).addEventListener( - pType, + pType, pListener, pUseCapture || false ); @@ -251,16 +251,20 @@ var CloudCommander, Util, DOM, CloudFunc; */ DOM.anyLoadOnLoad = function(pParams_a, pFunc){ if( Util.isArray(pParams_a) ) { - var lParam = pParams_a.pop(); - - if(Util.isString(lParam) ) - lParam = { src : lParam }; - - - if(lParam && !lParam.func){ - lParam.func = function(){ + var lParam = pParams_a.pop(), + lFunc = function(){ DOM.anyLoadOnLoad(pParams_a, pFunc); }; + + if( Util.isString(lParam) ) + lParam = { src : lParam }; + else if( Util.isArray(lParam) ){ + + DOM.anyLoadInParallel(lParam, lFunc); + } + + if(lParam && !lParam.func){ + lParam.func = lFunc; DOM.anyload(lParam); @@ -292,7 +296,7 @@ var CloudCommander, Util, DOM, CloudFunc; pParams_a = [pParams_a]; } - for(var i = 0, n = pParams_a.length; i < n; i++){ + for(var i = 0, n = pParams_a.length; i < n; i++){ var lParam = pParams_a.pop(); if(lParam){ @@ -301,7 +305,7 @@ var CloudCommander, Util, DOM, CloudFunc; if(Util.isString(lParam) ) lParam = { src : lParam }; - var lFunc = lParam.func; + var lFunc = lParam.func; lParam.func = Util.retExec(doneFunc, lFunc); DOM.anyload(lParam); diff --git a/lib/client/editor/_codemirror.js b/lib/client/editor/_codemirror.js index 4a501505..07ccdf0f 100644 --- a/lib/client/editor/_codemirror.js +++ b/lib/client/editor/_codemirror.js @@ -85,19 +85,21 @@ var CloudCommander, Util, DOM, CloudFunc, CodeMirror; console.time('codemirror load'); var lDir = cloudcmd.LIBDIRCLIENT + 'editor/codemirror/'; - DOM.jsload(lDir + 'codemirror.js', function(){ - DOM.anyLoadInParallel([ - lDir + 'codemirror.css', - lDir + 'theme/night.css', - lDir + 'mode/javascript.js', + DOM.anyLoadOnLoad([ + [ + lDir + 'codemirror.css', + lDir + 'theme/night.css', + lDir + 'mode/javascript.js', ], - function(){ - console.timeEnd('codemirror load'); - CodeMirrorLoaded = true; - Util.exec(pCallBack); - }); - }); + lDir + 'codemirror.js' + ], + + function(){ + console.timeEnd('codemirror load'); + CodeMirrorLoaded = true; + Util.exec(pCallBack); + }); } diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index 44ea9997..03e4d6e4 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -29,18 +29,17 @@ var CloudCommander, Util, DOM, $, Github, cb; console.time('github load'); var lDir = './lib/client/storage/github/'; - DOM.jsload(lDir + 'github.js', function(){ - DOM.anyLoadOnLoad([ - lDir + 'lib/base64.js', - lDir + 'lib/underscore.js'], - - function(){ - console.timeEnd('github load'); - DOM.Images.hideLoad(); - - Util.exec(pCallBack); - }); - }); + DOM.anyLoadInParallel([ + lDir + 'github.js', + lDir + 'lib/base64.js', + lDir + 'lib/underscore.js'], + + function(){ + console.timeEnd('github load'); + DOM.Images.hideLoad(); + + Util.exec(pCallBack); + }); } function setConfig(pCallBack){ From 665d7bc8b668d8df2c88ff1786224575dd00beb1 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 26 Nov 2012 07:33:47 -0500 Subject: [PATCH 209/281] minor changes --- ChangeLog | 3 +++ lib/client/editor/_codemirror.js | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 379260b1..0e64de16 100644 --- a/ChangeLog +++ b/ChangeLog @@ -119,6 +119,9 @@ adding DOM.anyLoadInParallel function that is loading files in parallel and then execute callback function. +* Added ability to load a couple scripts after one main, +in any position in anyLoadOnLoad function. + 2012.10.01, Version 0.1.7 diff --git a/lib/client/editor/_codemirror.js b/lib/client/editor/_codemirror.js index 07ccdf0f..aa2d8215 100644 --- a/lib/client/editor/_codemirror.js +++ b/lib/client/editor/_codemirror.js @@ -85,7 +85,8 @@ var CloudCommander, Util, DOM, CloudFunc, CodeMirror; console.time('codemirror load'); var lDir = cloudcmd.LIBDIRCLIENT + 'editor/codemirror/'; - DOM.anyLoadOnLoad([ + DOM.anyLoadOnLoad( + [ [ lDir + 'codemirror.css', lDir + 'theme/night.css', From 252bbc4534e8ea54667e6c7614fc2475c998e5be Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 26 Nov 2012 10:39:15 -0500 Subject: [PATCH 210/281] minor changes --- lib/util.js | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/lib/util.js b/lib/util.js index d7af7033..eb2c2f7e 100644 --- a/lib/util.js +++ b/lib/util.js @@ -29,26 +29,29 @@ var Util, exports; * @param pExt - расширение */ Util.checkExtension = function(pName, pExt){ + var lRet = false, + lLength = pName.length; /* длина имени*/ /* если длина имени больше * длинны расширения - * имеет смысл продолжать */ - if (typeof pExt === 'string' && - pName.length > pExt.length) { - var lLength = pName.length; /* длина имени*/ - var lExtNum = pName.lastIndexOf(pExt); /* последнее вхождение расширения*/ - var lExtSub = lLength - lExtNum; /* длина расширения*/ + if (typeof pExt === 'string' && pName.length > pExt.length) { + var lExtNum = pName.lastIndexOf(pExt), /* последнее вхождение расширения*/ + lExtSub = lLength - lExtNum; /* длина расширения*/ - /* если pExt - расширение pName */ - return lExtSub === pExt.length; + /* если pExt - расширение pName */ + lRet = lExtSub === pExt.length; - }else if(typeof pExt === 'object' && - pExt.length){ - for(var i=0; i < pName.length; i++) - if(this.checkExtension(pName, pExt[i])) - return true; - }else - return false; + }else if(typeof pExt === 'object' && pExt.length){ + for(var i=0; i < pName.length; i++){ + lRet = Util.checkExtension(pName, pExt[i]); + + if(lRet) + break; + } + } + + return lRet; }; /* STRINGS */ From d64a58692fec99c7bb97e18b74e56a911e8d52ae Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 27 Nov 2012 05:05:04 -0500 Subject: [PATCH 211/281] Added chainable to Cache object in DOM --- ChangeLog | 2 ++ lib/client/dom.js | 66 +++++++++++++++++++++++++++++++--------- lib/client/ie.js | 76 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 129 insertions(+), 15 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0e64de16..67a6eb07 100644 --- a/ChangeLog +++ b/ChangeLog @@ -122,6 +122,8 @@ callback function. * Added ability to load a couple scripts after one main, in any position in anyLoadOnLoad function. +* Added chainable to Cache object in DOM. + 2012.10.01, Version 0.1.7 diff --git a/lib/client/dom.js b/lib/client/dom.js index 4ee89d94..50cb86a9 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -184,11 +184,35 @@ var CloudCommander, Util, DOM, CloudFunc; return ( CacheAllowed = Util.isObject( window.localStorage ) ); }; + + /** + * allow cache usage + */ + this.setAllowed = function(){ + var lRet = this; + CacheAllowed = true; + + return lRet; + }; + + /** + * dissalow cache usage + */ + this.UnSetAllowed = function(){ + var lRet = this; + CacheAllowed = false; + + return lRet; + }; + /** remove element */ this.remove = function(pItem){ - return CacheAllowed ? - localStorage.removeItem(pItem) : - (delete Data[pItem]); + var lRet = this; + + if(CacheAllowed) + localStorage.removeItem(pItem); + + return lRet; }; /** если доступен localStorage и @@ -196,28 +220,42 @@ var CloudCommander, Util, DOM, CloudFunc; * записываем данные в него */ this.set = function(pName, pData){ - var lRet; + var lRet = this; - if(pName && pData) - lRet = CacheAllowed ? - localStorage.setItem(pName,pData) : - Data[pName] = pData; + if(CacheAllowed && pName && pData) + localStorage.setItem(pName,pData); return lRet; }, /** Если доступен Cache принимаем из него данные*/ this.get = function(pName){ - return CacheAllowed ? - localStorage.getItem(pName) : - Data[pName]; + var lRet = this; + + if(CacheAllowed) + lRet = localStorage.getItem(pName); + + return lRet; }, + /* get all cache from local storage */ + this.getAll = function(){ + var lRet = null; + + if(CacheAllowed) + lRet = localStorage; + + return lRet; + }; + /** функция чистит весь кэш для всех каталогов*/ this.clear = function(){ - return CacheAllowed ? - localStorage.clear() : - (Data = {}); + var lRet = this; + + if(CacheAllowed) + localStorage.clear(); + + return lRet; }; }; diff --git a/lib/client/ie.js b/lib/client/ie.js index 010db487..708558fc 100644 --- a/lib/client/ie.js +++ b/lib/client/ie.js @@ -28,10 +28,17 @@ var Util, DOM, $; } /* setting function context (this) */ - DOM.bind = function(pFunction, pContext){ + Util.bind = function(pFunction, pContext){ return $.proxy(pFunction, pContext); }; + /* + * typeof callback === "function" should not be used, + * as older browsers may report objects to be a function, + * which they are not + */ + Util.isFunction = $.isFunction; + if (!document.addEventListener) /** * safe add event listener on ie @@ -141,4 +148,71 @@ var Util, DOM, $; if(!window.XMLHttpRequest) DOM.ajax = $.ajax; + + if(!window.localStorage){ + DOM.Cache = function(){ + /* приватный переключатель возможности работы с кэшем */ + var CacheAllowed, + Data = {}; + + /* функция проверяет возможно ли работать с кэшем каким-либо образом */ + this.isAllowed = function(){ + return CacheAllowed; + }; + + this.setAllowed = function(){ + CacheAllowed = true; + }; + + this.UnSetAllowed = function(){ + CacheAllowed = false; + }; + + /** remove element */ + this.remove = function(pItem){ + var lRet = this; + if(CacheAllowed) + delete Data[pItem]; + + return lRet; + }; + + /** если доступен localStorage и + * в нём есть нужная нам директория - + * записываем данные в него + */ + this.set = function(pName, pData){ + var lRet = this; + + if(CacheAllowed && pName && pData) + Data[pName] = pData; + + return lRet; + }, + + /** Если доступен Cache принимаем из него данные*/ + this.get = function(pName){ + var lRet = false; + + if(CacheAllowed) + lRet = Data[pName]; + + return lRet; + }, + + /** функция чистит весь кэш для всех каталогов*/ + this.clear = function(){ + var lRet = this; + + if(CacheAllowed) + Data = {}; + + return lRet; + }; + }; + + DOM.Cache = new DOM.Cache(); + } + + })(); \ No newline at end of file From 778fb8ebbb789a1f90870e1dd032cb10f03bcdbe Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 27 Nov 2012 06:06:03 -0500 Subject: [PATCH 212/281] refactored --- css/style.css | 10 +++--- lib/client/dom.js | 18 +++++++---- lib/client/editor/_codemirror.js | 52 +++++++++++++++----------------- lib/client/ie.js | 5 ++- lib/client/viewer.js | 24 ++++++--------- 5 files changed, 57 insertions(+), 52 deletions(-) diff --git a/css/style.css b/css/style.css index 864a0a76..d930bd50 100644 --- a/css/style.css +++ b/css/style.css @@ -181,10 +181,10 @@ body{ font-weight: bold; } #path{ - margin-left:1.5%; + margin-left:1.5%; } -#left{ - float:left; +.left, #left{ + float:left; } /* фон файла, на котором курсор*/ .current-file{ @@ -192,9 +192,11 @@ body{ } .selected-file{ color:white; + background-color: rgb(49, 123, 249); background-color: rgba(49, 123, 249, .40); } -#right{ + +.right, #right{ float:right; } .panel{ diff --git a/lib/client/dom.js b/lib/client/dom.js index 50cb86a9..e598b802 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -99,11 +99,15 @@ var CloudCommander, Util, DOM, CloudFunc; * @param pElement {document by default} */ DOM.addListener = function(pType, pListener, pUseCapture, pElement){ - return (pElement || document).addEventListener( + var lRet = this; + + (pElement || document).addEventListener( pType, pListener, pUseCapture || false ); + + return lRet; }; /** @@ -111,7 +115,7 @@ var CloudCommander, Util, DOM, CloudFunc; * @param pListener * @param pUseCapture */ - DOM.addKeyListener = function(pListener, pUseCapture){ + DOM.addKeyListener = function(pListener, pUseCapture){ return DOM.addListener('keydown', pListener, pUseCapture); }; @@ -288,6 +292,8 @@ var CloudCommander, Util, DOM, CloudFunc; * @param pFunc - onload function */ DOM.anyLoadOnLoad = function(pParams_a, pFunc){ + var lRet = this; + if( Util.isArray(pParams_a) ) { var lParam = pParams_a.pop(), lFunc = function(){ @@ -309,6 +315,8 @@ var CloudCommander, Util, DOM, CloudFunc; }else Util.exec(pFunc); } + + return lRet; }; /** @@ -320,7 +328,7 @@ var CloudCommander, Util, DOM, CloudFunc; * @param pFunc - onload function */ DOM.anyLoadInParallel = function(pParams_a, pFunc){ - var lRet = Util.isArray(pParams_a), + var lRet = this, done = [], doneFunc = function (pCallBack){ @@ -330,7 +338,7 @@ var CloudCommander, Util, DOM, CloudFunc; Util.exec(pFunc); }; - if(!lRet){ + if( !Util.isArray(pParams_a) ){ pParams_a = [pParams_a]; } @@ -347,8 +355,6 @@ var CloudCommander, Util, DOM, CloudFunc; lParam.func = Util.retExec(doneFunc, lFunc); DOM.anyload(lParam); - - lRet = true; } } diff --git a/lib/client/editor/_codemirror.js b/lib/client/editor/_codemirror.js index aa2d8215..2bd58096 100644 --- a/lib/client/editor/_codemirror.js +++ b/lib/client/editor/_codemirror.js @@ -21,22 +21,22 @@ var CloudCommander, Util, DOM, CloudFunc, CodeMirror; /* private functions */ function setCSS(){ - return DOM.cssSet({ + var lPosition = DOM.getPanel().id, + lRet = DOM.cssSet({ id : 'editor', inner : '.CodeMirror{' + 'font-family' + ': \'Droid Sans Mono\';' + 'font-size' + ': 15px;' + - /* codemirror v3 */ - //'height : ' + cloudcmd.HEIGHT + 'px' + '}' + '.CodeMirror-scroll{' + 'height' + ':' + cloudcmd.HEIGHT + 'px' + '}' + '#CodeMirrorEditor{' + - 'float' + ':' + DOM.getPanel().id + - // 'padding :20px 20px 20px 20px;' + + 'float' + ':' + lPosition + '}' }); + + return lRet; } /** @@ -83,27 +83,26 @@ var CloudCommander, Util, DOM, CloudFunc, CodeMirror; */ function load(pCallBack){ console.time('codemirror load'); - var lDir = cloudcmd.LIBDIRCLIENT + 'editor/codemirror/'; - - DOM.anyLoadOnLoad( - [ + var lDir = cloudcmd.LIBDIRCLIENT + 'editor/codemirror/', + lFiles = [ - lDir + 'codemirror.css', - lDir + 'theme/night.css', - lDir + 'mode/javascript.js', - ], - - lDir + 'codemirror.js' - ], - - function(){ - console.timeEnd('codemirror load'); - CodeMirrorLoaded = true; - Util.exec(pCallBack); - }); - - } + [ + lDir + 'codemirror.css', + lDir + 'theme/night.css', + lDir + 'mode/javascript.js', + ], + + lDir + 'codemirror.js' + ]; + DOM.anyLoadOnLoad(lFiles, function(){ + console.timeEnd('codemirror load'); + CodeMirrorLoaded = true; + Util.exec(pCallBack); + }); + + } + /** * function shows CodeMirror editor */ @@ -235,9 +234,8 @@ var CloudCommander, Util, DOM, CloudFunc, CodeMirror; }; /* добавляем обработчик клавишь */ - DOM.addKeyListener( lKeyListener ); - - DOM.setButtonKey('f4', lEditor); + DOM.addKeyListener( lKeyListener ) + .setButtonKey('f4', lEditor); }; cloudcmd.Editor.CodeMirror = CodeMirrorEditor; diff --git a/lib/client/ie.js b/lib/client/ie.js index 708558fc..45136ac7 100644 --- a/lib/client/ie.js +++ b/lib/client/ie.js @@ -46,13 +46,16 @@ var Util, DOM, $; * @param pListener */ DOM.addListener = function(pType, pListener){ - var lType = 'on' + pType, + var lRet = this, + lType = 'on' + pType, lFunc = document[lType]; document[lType] = function(){ Util.exec(lFunc); pListener(); }; + + return lRet; }; if(!document.getElementsByClassName){ diff --git a/lib/client/viewer.js b/lib/client/viewer.js index 9237d355..194e143b 100644 --- a/lib/client/viewer.js +++ b/lib/client/viewer.js @@ -96,19 +96,16 @@ var CloudCommander, Util, DOM, CloudFunc, $; */ FancyBox.load = function(pCallBack){ console.time('fancybox load'); - var lDir = cloudcmd.LIBDIRCLIENT + 'viewer/fancybox/'; + var lDir = cloudcmd.LIBDIRCLIENT + 'viewer/fancybox/', + lFiles = [ lDir + 'jquery.fancybox.css', + lDir + 'jquery.fancybox.js' ]; - DOM.anyLoadInParallel([ - lDir + 'jquery.fancybox.css', - lDir + 'jquery.fancybox.js'], - - function(){ - console.timeEnd('fancybox load'); - pCallBack(); - }); - - DOM.cssSet({id:'viewer', + DOM.anyLoadInParallel(lFiles, function(){ + console.timeEnd('fancybox load'); + pCallBack(); + }) + .cssSet({id:'viewer', inner : '#CloudViewer{' + 'font-size: 16px;' + 'white-space :pre' + @@ -215,9 +212,8 @@ var CloudCommander, Util, DOM, CloudFunc, $; }; /* добавляем обработчик клавишь */ - DOM.addKeyListener(lKeyListener); - - DOM.setButtonKey('f3', lView); + DOM.addKeyListener(lKeyListener) + .setButtonKey('f3', lView); }; cloudcmd.Viewer.FancyBox = FancyBox; From 8c4d0cc599b2a1ede822fd22d2851046dc366cff Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 28 Nov 2012 03:48:45 -0500 Subject: [PATCH 213/281] changed default ip to null so IP would be geted from config.json only if it setted up --- ChangeLog | 3 ++ client.js | 2 +- config.json | 2 +- lib/client/editor/_codemirror.js | 3 +- lib/client/menu.js | 21 +++++++------- lib/client/storage/_github.js | 23 +++++++-------- lib/client/terminal.js | 48 ++++++++++++++++---------------- 7 files changed, 53 insertions(+), 49 deletions(-) diff --git a/ChangeLog b/ChangeLog index 67a6eb07..1e5ba8fc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -124,6 +124,9 @@ in any position in anyLoadOnLoad function. * Added chainable to Cache object in DOM. +* Changed default ip to null so IP would be geted from +config.json only if it setted up. + 2012.10.01, Version 0.1.7 diff --git a/client.js b/client.js index 49e793f8..2d3cdd87 100644 --- a/client.js +++ b/client.js @@ -52,7 +52,7 @@ var CloudClient = { OLD_BROWSER : false, HOST : (function(){ - var lLocation = document.location; + var lLocation = document.location; return lLocation.protocol + '//' + lLocation.host; })() }; diff --git a/config.json b/config.json index 045e85e1..a5664801 100644 --- a/config.json +++ b/config.json @@ -14,6 +14,6 @@ "logs" : false, "socket" : true, "port" : 80, - "ip" : "127.0.0.1", + "ip" : null, "rest" : true } \ No newline at end of file diff --git a/lib/client/editor/_codemirror.js b/lib/client/editor/_codemirror.js index 2bd58096..3c16541e 100644 --- a/lib/client/editor/_codemirror.js +++ b/lib/client/editor/_codemirror.js @@ -99,8 +99,7 @@ var CloudCommander, Util, DOM, CloudFunc, CodeMirror; console.timeEnd('codemirror load'); CodeMirrorLoaded = true; Util.exec(pCallBack); - }); - + }); } /** diff --git a/lib/client/menu.js b/lib/client/menu.js index 50d7f9cf..8a79edff 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -106,15 +106,16 @@ var CloudCommander, Util, DOM, CloudFunc, $; function load(pCallBack){ console.time('menu load'); - var lDir = Menu.dir; + var lDir = Menu.dir, + lFiles = [ + lDir + 'contextMenu.js', + lDir + 'contextMenu.css' + ]; - DOM.anyLoadInParallel([ - lDir + 'contextMenu.js', - lDir + 'contextMenu.css'], - function(){ - console.timeEnd('menu load'); - Util.exec(pCallBack); - }); + DOM.anyLoadInParallel(lFiles, function(){ + console.timeEnd('menu load'); + Util.exec(pCallBack); + }); } function set(){ @@ -205,7 +206,7 @@ var CloudCommander, Util, DOM, CloudFunc, $; load ])); - var key_event = (function(pEvent){ + var key_event = function(pEvent){ /* если клавиши можно обрабатывать */ if( KeyBinding.get() ){ /* if shift + F10 pressed */ @@ -219,7 +220,7 @@ var CloudCommander, Util, DOM, CloudFunc, $; } else if (pEvent.keyCode === cloudcmd.KEY.ESC) KeyBinding.set(); - }); + }; /* добавляем обработчик клавишь */ DOM.addKeyListener( key_event ); diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index 03e4d6e4..798fb7b3 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -28,17 +28,18 @@ var CloudCommander, Util, DOM, $, Github, cb; function load(pCallBack){ console.time('github load'); - var lDir = './lib/client/storage/github/'; - DOM.anyLoadInParallel([ - lDir + 'github.js', - lDir + 'lib/base64.js', - lDir + 'lib/underscore.js'], - - function(){ - console.timeEnd('github load'); - DOM.Images.hideLoad(); - - Util.exec(pCallBack); + var lDir = './lib/client/storage/github/', + lFiles = [ + lDir + 'github.js', + lDir + 'lib/base64.js', + lDir + 'lib/underscore.js' + ]; + + DOM.anyLoadInParallel(lFiles, function(){ + console.timeEnd('github load'); + DOM.Images.hideLoad(); + + Util.exec(pCallBack); }); } diff --git a/lib/client/terminal.js b/lib/client/terminal.js index ff1d4c88..04a862d5 100644 --- a/lib/client/terminal.js +++ b/lib/client/terminal.js @@ -22,34 +22,34 @@ var CloudCommander, Util, DOM, $; function load(pCallBack){ console.time('terminal load'); - var lDir = 'lib/client/terminal/jquery-terminal/jquery.'; + var lDir = 'lib/client/terminal/jquery-terminal/jquery.', + lFiles = [ + lDir + 'terminal.js', + lDir + 'mousewheel.js', + lDir + 'terminal.css' + ]; - DOM.anyLoadInParallel([ - lDir + 'terminal.js', - lDir + 'mousewheel.js', - lDir + 'terminal.css'], + DOM.anyLoadInParallel(lFiles, function(){ + console.timeEnd('terminal load'); + init(); - function(){ - console.timeEnd('terminal load'); - init(); - - $(function($, undefined) { - Term = JqueryTerminal.Term = $('#terminal').terminal(function(command, term){ - term.echo(''); - cloudcmd.Socket.send(command); - }, { - greetings : '[[;#729FCF;]Cloud Commander Terminal]', - prompt : '[[;#729FCF;]cloudcmd> ]', - color : '#729FCF;' - }); + $(function($, undefined) { + Term = JqueryTerminal.Term = $('#terminal').terminal(function(command, term){ + term.echo(''); + cloudcmd.Socket.send(command); + }, { + greetings : '[[;#729FCF;]Cloud Commander Terminal]', + prompt : '[[;#729FCF;]cloudcmd> ]', + color : '#729FCF;' }); - /* removing resize function, no need for us */ - Term.resize = function(){}; - - $(window).unbind('resize'); - - Util.exec(pCallBack.callback || pCallBack); }); + /* removing resize function, no need for us */ + Term.resize = function(){}; + + $(window).unbind('resize'); + + Util.exec(pCallBack.callback || pCallBack); + }); } /** From 684015e8c80c1807173d8e15acd451111a4aa529 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 28 Nov 2012 08:34:12 -0500 Subject: [PATCH 214/281] fixed bug with starting node from other then projects dir --- ChangeLog | 2 ++ cloudcmd.js | 2 -- lib/server/main.js | 6 +++--- lib/server/minify.js | 28 +++++++++++++++++----------- lib/server/update.js | 16 ++++++++++++---- server.js | 14 +++++++------- 6 files changed, 41 insertions(+), 27 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1e5ba8fc..028ee029 100644 --- a/ChangeLog +++ b/ChangeLog @@ -127,6 +127,8 @@ in any position in anyLoadOnLoad function. * Changed default ip to null so IP would be geted from config.json only if it setted up. +* Fixed bug with starting node from other then projects dir. + 2012.10.01, Version 0.1.7 diff --git a/cloudcmd.js b/cloudcmd.js index a710a596..77dd3980 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -1,8 +1,6 @@ (function(){ "use strict"; - global.cloudcmd = {}; - var DIR = __dirname + '/', main = require(DIR + 'lib/server/main'), diff --git a/lib/server/main.js b/lib/server/main.js index 08c43524..dec323a9 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -23,7 +23,8 @@ global.cloudcmd = {}, /* Constants */ - exports.DIR = DIR = process.cwd() + '/', + /* current dir + 2 levels up */ + exports.DIR = DIR = __dirname + '/../../', exports.LIBDIR = LIBDIR = DIR + 'lib/', exports.SRVDIR = SRVDIR = LIBDIR + 'server/', exports.WIN32 = isWin32(); @@ -52,7 +53,7 @@ /* Needed Modules */ exports.util = Util = require(LIBDIR + 'util'), - exports.zlib = mrequire('zlib'), + exports.zlib = mrequire('zlib'), /* Main Information */ exports.config = rootrequire('config'); @@ -76,7 +77,6 @@ exports.socket = srvrequire('socket'), exports.update = srvrequire('update'), exports.minify = srvrequire('minify').Minify; - /* * second initializing after all modules load, so global var is * totally filled of all information that should know all modules diff --git a/lib/server/minify.js b/lib/server/minify.js index e612e7db..1b886192 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -4,11 +4,11 @@ "use strict"; var main = global.cloudcmd.main, - SRVDIR = main.SRVDIR; + DIR = main.DIR; exports.Minify = { /* pathes to directories */ - INDEX :'index.html', + INDEX : DIR + 'index.html', /* приватный переключатель минимизации */ _allowed :{ css : true, @@ -62,24 +62,30 @@ * in nodester) we can not write * minified versions */ - this.MinFolder = '/' + lMinify.MinFolder; + this.MinFolder = lMinify.MinFolder; - var lOptimizeParams = []; + var lOptimizeParams = [], + lStyleCSS = DIR + 'css/style.css', + lResetCSS = DIR + 'css/reset.css'; if (this._allowed.js) { - lOptimizeParams.push('client.js'); + lOptimizeParams.push(DIR + 'client.js'); } if (this._allowed.html) lOptimizeParams.push(this.INDEX); if (this._allowed.css) { - lOptimizeParams.push({ - './css/style.css' : this._allowed.img - }); + var lStyles = []; - lOptimizeParams.push({ - './css/reset.css': this._allowed.img - }); + lStyles[0] = {}; + lStyles[0][lStyleCSS] = this._allowed.img; + lStyles[1] = {}; + lStyles[1][lResetCSS] = this._allowed.img; + + lOptimizeParams.push(lStyles[0]); + lOptimizeParams.push(lStyles[1]); + + lOptimizeParams.push(); } if (lOptimizeParams.length) diff --git a/lib/server/update.js b/lib/server/update.js index c5f4c6f5..a1208d08 100644 --- a/lib/server/update.js +++ b/lib/server/update.js @@ -2,13 +2,21 @@ (function(){ "use strict"; - + var main = global.cloudcmd.main, mainpackage = main.mainpackage, - exec = main.child_process.exec; + exec = main.child_process.exec, + DIR = main.DIR; exports.get = function(){ - exec('git pull', pull); + var lPull = function(){ + exec('git pull', pull); + }; + + if( process.cwd === DIR ) + lPull(); + else + exec('cd ' + DIR, lPull); }; /** @@ -26,7 +34,7 @@ } else pStdout = 'Cloud Commander is up to date.'; - console.log( process.cwd() ); + console.log(DIR); if(mainpackage) pStdout = 'Version ' + mainpackage.version + '\n' + pStdout; } diff --git a/server.js b/server.js index 82e64e5c..8cb3202d 100644 --- a/server.js +++ b/server.js @@ -56,17 +56,15 @@ Server :{}, /* КОНСТАНТЫ */ - INDEX : 'index.html' + INDEX : __dirname + '/' + 'index.html' }, - DirPath = '/', OK = 200, - - DIR = process.cwd() + '/', - main = require(DIR + 'lib/server/main'), + main = global.cloudcmd.main, + DIR = main.Dir, LIBDIR = main.LIBDIR, SRVDIR = main.SRVDIR, @@ -402,7 +400,7 @@ * загружаем сжатый html-файл в дальнейшем */ CloudServer.INDEX = (CloudServer.Minify._allowed.html ? - '.' + CloudServer.Minify.MinFolder + 'index.min.html' + CloudServer.Minify.MinFolder + 'index.min.html' : CloudServer.INDEX); /* @@ -581,7 +579,9 @@ /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ if(CloudServer.Gzip){ - Zlib.gzip(lList,CloudServer.getGzipDataFunc(lHeader,CloudServer.INDEX)); + var lGzipCB = CloudServer.getGzipDataFunc(lHeader, CloudServer.INDEX); + + Zlib.gzip(lList, lGzipCB); } /* если не поддерживаеться - отсылаем данные без сжатия*/ else From 5fc2e9e819e50995ae1a090119f8c6f084988f8b Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 28 Nov 2012 08:41:03 -0500 Subject: [PATCH 215/281] minor changes --- config.json | 2 +- lib/server/minify.js | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/config.json b/config.json index a5664801..7d4eb55c 100644 --- a/config.json +++ b/config.json @@ -3,7 +3,7 @@ "appcache" : false, "minification" : { "js" : false, - "css" : true, + "css" : false, "html" : true, "img" : true }, diff --git a/lib/server/minify.js b/lib/server/minify.js index 1b886192..3ef7e2b1 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -84,8 +84,6 @@ lOptimizeParams.push(lStyles[0]); lOptimizeParams.push(lStyles[1]); - - lOptimizeParams.push(); } if (lOptimizeParams.length) From c668a8219609c45eaed02157ea78da53fd6d3f80 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 28 Nov 2012 10:57:21 -0500 Subject: [PATCH 216/281] fixed bug with path of all.min.css --- cloudcmd.js | 4 +++- config.json | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 77dd3980..4d780500 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -40,10 +40,12 @@ * минифицированый */ if(srv.Minify._allowed.css){ + var lPath = srv.Minify.MinFolder.replace(DIR, ''); + lReplace_s = ''; lData = Util.removeStr(lData, lReplace_s); - lData = lData.replace('/css/style.css', srv.Minify.MinFolder + 'all.min.css'); + lData = lData.replace('/css/style.css', lPath + 'all.min.css'); } lReplace_s = '
    '; diff --git a/config.json b/config.json index 7d4eb55c..76111b3e 100644 --- a/config.json +++ b/config.json @@ -2,8 +2,8 @@ "cache" : {"allowed" : false}, "appcache" : false, "minification" : { - "js" : false, - "css" : false, + "js" : true, + "css" : true, "html" : true, "img" : true }, From 045caf467ec7a146dd4f98599730bcfce40a07a1 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 29 Nov 2012 03:28:03 -0500 Subject: [PATCH 217/281] fixed bug with slashes on win32 --- ChangeLog | 2 ++ cloudcmd.js | 6 +++++- lib/server/main.js | 10 ++++++---- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/ChangeLog b/ChangeLog index 028ee029..a3eaada1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -129,6 +129,8 @@ config.json only if it setted up. * Fixed bug with starting node from other then projects dir. +* Fixed bug with slashes on win32. + 2012.10.01, Version 0.1.7 diff --git a/cloudcmd.js b/cloudcmd.js index 4d780500..7f45152d 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -16,7 +16,11 @@ Server = main.require(DIR + 'server'), srv = Server.CloudServer, Config = main.config; - + + /* reinit main dir os if we on + * Win32 should be backslashes */ + DIR = main.DIR; + readConfig(); Server.start(Config, { index : indexProcessing, diff --git a/lib/server/main.js b/lib/server/main.js index dec323a9..837ff263 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -5,7 +5,8 @@ LIBDIR, SRVDIR, Util, - + SLASH, + ISWIN32 = process.platform == 'win32', OK = 200, ERROR = 404, Extensions = { @@ -24,9 +25,10 @@ /* Constants */ /* current dir + 2 levels up */ - exports.DIR = DIR = __dirname + '/../../', - exports.LIBDIR = LIBDIR = DIR + 'lib/', - exports.SRVDIR = SRVDIR = LIBDIR + 'server/', + exports.SLASH = SLASH = ISWIN32 ? '\\' : '/', + exports.SRVDIR = SRVDIR = __dirname + SLASH, + exports.LIBDIR = LIBDIR = SRVDIR + '..' + SLASH, + exports.DIR = DIR = LIBDIR + '..' + SLASH, exports.WIN32 = isWin32(); /* Functions */ From 6d35bdc99dc07bc2bc212a389a06d1c14e91b4cf Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 29 Nov 2012 03:38:50 -0500 Subject: [PATCH 218/281] minor changes --- lib/server/main.js | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/lib/server/main.js b/lib/server/main.js index 837ff263..51c52a33 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -1,12 +1,18 @@ (function(){ "strict mode"; + /* Global var accessible from any loaded module */ + global.cloudcmd = {}; + var DIR, LIBDIR, SRVDIR, Util, + SLASH, - ISWIN32 = process.platform == 'win32', + ISWIN32, + path, + OK = 200, ERROR = 404, Extensions = { @@ -19,17 +25,26 @@ '.appcache' : 'text/cache-manifest', '.mp3' : 'audio/mpeg' }; - - /* Global var accessible from any loaded module */ - global.cloudcmd = {}, + + + /* Native Modules*/ + exports.crypto = require('crypto'), + exports.child_process = require('child_process'), + exports.fs = require('fs'), + exports.http = require('http'), + exports.https = require('https'), + exports.path = path = require('path'), + exports.url = require('url'), + exports.querystring = require('querystring'), /* Constants */ /* current dir + 2 levels up */ + exports.WIN32 = ISWIN32 = isWin32(); exports.SLASH = SLASH = ISWIN32 ? '\\' : '/', - exports.SRVDIR = SRVDIR = __dirname + SLASH, - exports.LIBDIR = LIBDIR = SRVDIR + '..' + SLASH, - exports.DIR = DIR = LIBDIR + '..' + SLASH, - exports.WIN32 = isWin32(); + + exports.SRVDIR = SRVDIR = __dirname + SLASH, + exports.LIBDIR = LIBDIR = path.normalize(SRVDIR + '../'), + exports.DIR = DIR = path.normalize(LIBDIR + '../'), /* Functions */ exports.generateHeaders = generateHeaders, @@ -39,16 +54,6 @@ exports.srvrequire = srvrequire, exports.rootrequire = rootrequire, - /* Native Modules*/ - exports.crypto = require('crypto'), - exports.child_process = require('child_process'), - exports.fs = require('fs'), - exports.http = require('http'), - exports.https = require('https'), - exports.path = require('path'), - exports.url = require('url'), - exports.querystring = require('querystring'), - /* compitability with old versions of node */ exports.fs.exists = exports.fs.exists || exports.path.exists; From 27879a08905d5dde1d4e610157e29c0426c5d2e9 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 29 Nov 2012 04:22:54 -0500 Subject: [PATCH 219/281] minor changes --- cloudcmd.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cloudcmd.js b/cloudcmd.js index 7f45152d..3c9c2603 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -39,7 +39,9 @@ var lReplace_s, lData = pData.data, lAdditional = pData.additional; - /* если выбрана опция минифизировать скрпиты + + /* + * если выбрана опция минимизировать скрипты * меняем в index.html обычные css на * минифицированый */ From 47d3edc744f545d970fa903df954be75d492e030 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 29 Nov 2012 07:34:39 -0500 Subject: [PATCH 220/281] added debug method to util --- cloudcmd.js | 4 ++-- config.json | 2 +- lib/client/dom.js | 8 +++---- lib/client/storage/_github.js | 1 + lib/util.js | 39 +++++++++++++++++++++++++++++++---- 5 files changed, 43 insertions(+), 11 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 3c9c2603..7c7707ad 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -1,6 +1,6 @@ (function(){ "use strict"; - + var DIR = __dirname + '/', main = require(DIR + 'lib/server/main'), @@ -46,7 +46,7 @@ * минифицированый */ if(srv.Minify._allowed.css){ - var lPath = srv.Minify.MinFolder.replace(DIR, ''); + var lPath = '/' + srv.Minify.MinFolder.replace(DIR, ''); lReplace_s = ''; diff --git a/config.json b/config.json index 76111b3e..a5664801 100644 --- a/config.json +++ b/config.json @@ -2,7 +2,7 @@ "cache" : {"allowed" : false}, "appcache" : false, "minification" : { - "js" : true, + "js" : false, "css" : true, "html" : true, "img" : true diff --git a/lib/client/dom.js b/lib/client/dom.js index e598b802..468c54e3 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -180,8 +180,7 @@ var CloudCommander, Util, DOM, CloudFunc; */ DOM.Cache = function(){ /* приватный переключатель возможности работы с кэшем */ - var CacheAllowed, - Data = {}; + var CacheAllowed; /* функция проверяет возможно ли работать с кэшем каким-либо образом */ this.isAllowed = function(){ @@ -265,6 +264,7 @@ var CloudCommander, Util, DOM, CloudFunc; DOM.Cache = new DOM.Cache(); + /** * Function gets id by src * @param pSrc @@ -721,14 +721,14 @@ var CloudCommander, Util, DOM, CloudFunc; */ DOM.getCurrentFile = function(){ var lCurrent = DOM.getByClass(getCurrentFile())[0]; - if(!lCurrent) + if(!lCurrent){ DOM.addCloudStatus({ code : -1, msg : 'Error: can not find ' + 'CurrentFile ' + 'in getCurrentFile' }); - + } return lCurrent; }; diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index 798fb7b3..ffdf9bdd 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -110,6 +110,7 @@ var CloudCommander, Util, DOM, $, Github, cb; }); } else + //window.open('welcome.html', 'welcome','width=300,height=200,menubar=yes,status=yes')"> window.location = 'https://github.com/login/oauth/authorize?client_id=' + CLIENT_ID + '&&scope=repo,user,gist'; diff --git a/lib/util.js b/lib/util.js index eb2c2f7e..7d5c16c2 100644 --- a/lib/util.js +++ b/lib/util.js @@ -6,9 +6,11 @@ var Util, exports; (function(){ "use strict"; - + Util = exports || {}; + var Scope = exports ? global : window; + /** setting function context * @param {function} pFunction * @param {object} pContext @@ -22,6 +24,16 @@ var Util, exports; return lRet; }; + Util.breakpoint = function(){ + var lRet = Util.tryCatch(function(){ + debugger; + }); + + Util.log(lRet); + + return lRet; + }; + /** * Функция ищет в имени файла расширение * и если находит возвращает true @@ -82,9 +94,11 @@ var Util, exports; * @param pArg */ Util.log = function(pArg){ - var lRet = pArg; - if(pArg) - console.log(pArg); + var lRet = pArg, + lConsole = Scope.console; + + if(lConsole && pArg) + lConsole.log(pArg); return lRet; }; @@ -285,6 +299,21 @@ var Util, exports; return lRet; }; + /** + * function execute param function in + * try...catch block and log result + * + * @param pCallBack + */ + Util.tryCatchDebug = function(pCallBack){ + var lRet = Util.tryCatch(pCallBack); + + if(lRet) + Util.debug(); + + return lRet; + }; + /** * function execute param function in * try...catch block and log result @@ -298,6 +327,7 @@ var Util, exports; return Util.log(lRet); }; + /** * function do save exec of function * @param pCallBack @@ -326,4 +356,5 @@ var Util, exports; return hours + ":" + minutes + ":" + seconds; }; + })(); \ No newline at end of file From 4db2f9f93e45a5fbd1a4b257bd2ec1b636ee2852 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 30 Nov 2012 05:25:01 -0500 Subject: [PATCH 221/281] fixed bug with editor close, when started from menu --- ChangeLog | 2 ++ lib/client/editor/_codemirror.js | 52 +++++++++++++++++++------------- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/ChangeLog b/ChangeLog index a3eaada1..51a8d62a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -131,6 +131,8 @@ config.json only if it setted up. * Fixed bug with slashes on win32. +* Fixed bug with editor close, when started from menu. + 2012.10.01, Version 0.1.7 diff --git a/lib/client/editor/_codemirror.js b/lib/client/editor/_codemirror.js index 3c16541e..63ed80c8 100644 --- a/lib/client/editor/_codemirror.js +++ b/lib/client/editor/_codemirror.js @@ -10,7 +10,14 @@ var CloudCommander, Util, DOM, CloudFunc, CodeMirror; CodeMirrorLoaded = false, /* indicator says CodeMirror still loads */ Loading = false, - ReadOnly = false; + ReadOnly = false, + + CallBacks = [ + hide, + initCodeMirror, + show, + load + ]; cloudcmd.Editor = { get : (function(){ @@ -70,8 +77,8 @@ var CloudCommander, Util, DOM, CloudFunc, CodeMirror; extraKeys: { //Сохранение 'Esc': function(){ - DOM.remove(lCSS, document.head); Util.exec(pData.callback); + DOM.remove(lCSS, document.head); } }, readOnly : ReadOnly @@ -105,7 +112,7 @@ var CloudCommander, Util, DOM, CloudFunc, CodeMirror; /** * function shows CodeMirror editor */ - CodeMirrorEditor.show = function(pCallBack){ + function show(pCallBack){ /* if CodeMirror function show already * called do not call it again @@ -180,7 +187,7 @@ var CloudCommander, Util, DOM, CloudFunc, CodeMirror; /** * function hides CodeMirror editor */ - CodeMirrorEditor.hide = function() { + function hide() { var lElem = CodeMirrorElement; KeyBinding.set(); @@ -188,27 +195,30 @@ var CloudCommander, Util, DOM, CloudFunc, CodeMirror; FM.removeChild(lElem); DOM.showPanel(); + } + + + /** + * function calls all CodeMirror editor functions + */ + CodeMirrorEditor.show = function(){ + DOM.Images.showLoad(); + Util.loadOnLoad( CallBacks ); }; + /** + * function hides CodeMirror editor + */ + CodeMirrorEditor.hide = hide; + /** * function bind keys */ cloudcmd.Editor.Keys = function(pReadOnly){ ReadOnly = pReadOnly; - var lShowCodemirror = [ - CodeMirrorEditor.hide, - initCodeMirror, - CodeMirrorEditor.show, - load - ], - lEditor = function(){ - DOM.Images.showLoad(); - Util.loadOnLoad( lShowCodemirror ); - }; - - lEditor(); - lShowCodemirror.pop(); + CodeMirrorEditor.show(); + CallBacks.pop(); var lKeyListener = function(pEvent){ /* если клавиши можно обрабатывать */ @@ -222,19 +232,19 @@ var CloudCommander, Util, DOM, CloudFunc, CodeMirror; { case lF4: ReadOnly = false; - lEditor(); + CodeMirrorEditor.show(); break; case lF3: ReadOnly = true; - lEditor(); + CodeMirrorEditor.show(); break; } } }; /* добавляем обработчик клавишь */ - DOM.addKeyListener( lKeyListener ) - .setButtonKey('f4', lEditor); + DOM.addKeyListener( lKeyListener ); + DOM.setButtonKey('f4', CodeMirrorEditor.show); }; cloudcmd.Editor.CodeMirror = CodeMirrorEditor; From c8345f531c8feffbd4993dd4b60b37962ab56b2b Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 30 Nov 2012 06:30:54 -0500 Subject: [PATCH 222/281] added description --- lib/server/appcache.js | 9 +++++++++ lib/server/main.js | 46 +++++++++++++++++++++++++----------------- lib/util.js | 26 ++++++++++++++++++------ 3 files changed, 56 insertions(+), 25 deletions(-) diff --git a/lib/server/appcache.js b/lib/server/appcache.js index ef86fc11..4a43225e 100644 --- a/lib/server/appcache.js +++ b/lib/server/appcache.js @@ -2,6 +2,15 @@ (function(){ "use strict"; + if(!global.cloudcmd) + return console.log( + '# appcache.js' + '\n' + + '# -----------' + '\n' + + '# Module is part of Cloud Commander,' + '\n' + + '# used for work with Aplication Cache.' + '\n' + + '# If you wont to see at work set appcache: true' + '\n' + + '# in config.json and start cloudcmd.js' + '\n'); + var main = global.cloudcmd.main, fs = main.fs, Util = main.util, diff --git a/lib/server/main.js b/lib/server/main.js index 51c52a33..bce4758a 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -11,7 +11,10 @@ SLASH, ISWIN32, + + fs, path, + zlib, OK = 200, ERROR = 404, @@ -30,7 +33,7 @@ /* Native Modules*/ exports.crypto = require('crypto'), exports.child_process = require('child_process'), - exports.fs = require('fs'), + exports.fs = fs = require('fs'), exports.http = require('http'), exports.https = require('https'), exports.path = path = require('path'), @@ -60,7 +63,7 @@ /* Needed Modules */ exports.util = Util = require(LIBDIR + 'util'), - exports.zlib = mrequire('zlib'), + exports.zlib = zlib = mrequire('zlib'), /* Main Information */ exports.config = rootrequire('config'); @@ -181,29 +184,34 @@ lEnc = lReq.headers['accept-encoding'] || '', lGzip = lEnc.match(/\bgzip\b/), - lReadStream; + lReadStream, - main.fs.exists(lName, function(pExist){ - lRet = pExist; - if(pExist){ - lReadStream = main.fs.createReadStream(lName, { - 'bufferSize': 4 * 1024 - }); - lRes.writeHead(OK, generateHeaders(lName, lGzip) ); - - if (lGzip) - lReadStream = lReadStream.pipe( main.zlib.createGzip() ); - - lReadStream.pipe(lRes); - } - else{ + lSendError = function(pError){ var lJson = JSON.stringify({ - error: 'File not Found' + error: pError }); lRes.writeHead(ERROR, 'OK'); lRes.end(lJson); - } + }; + + main.fs.exists(lName, function(pExist){ + lRet = pExist; + if(pExist) + Util.tryCatch(function(){ + lReadStream = fs.createReadStream(lName, { + 'bufferSize': 4 * 1024 + }); + lRes.writeHead(OK, generateHeaders(lName, lGzip) ); + + if (lGzip) + lReadStream = lReadStream.pipe( zlib.createGzip() ); + + lReadStream.pipe(lRes); + }, lSendError); + + else + lSendError('File not Found'); }); return lRet; diff --git a/lib/util.js b/lib/util.js index 7d5c16c2..bf844703 100644 --- a/lib/util.js +++ b/lib/util.js @@ -303,10 +303,10 @@ var Util, exports; * function execute param function in * try...catch block and log result * - * @param pCallBack + * @param pTryFunc */ - Util.tryCatchDebug = function(pCallBack){ - var lRet = Util.tryCatch(pCallBack); + Util.tryCatchDebug = function(pTryFunc){ + var lRet = Util.tryCatch(pTryFunc); if(lRet) Util.debug(); @@ -318,16 +318,30 @@ var Util, exports; * function execute param function in * try...catch block and log result * - * @param pCallBack + * @param pTryFunc */ - Util.tryCatchLog = function(pCallBack){ + Util.tryCatchLog = function(pTryFunc){ var lRet; - lRet = Util.tryCatch(pCallBack); + lRet = Util.tryCatch(pTryFunc); return Util.log(lRet); }; + /** + * function execute param function in + * try...catch block and log result + * + * @param pCallBack + */ + Util.tryCatchCall = function(pTryFunc, pCallBack){ + var lRet; + + lRet = Util.tryCatch(pTryFunc); + + return Util.exec(pCallBack, lRet); + }; + /** * function do save exec of function * @param pCallBack From bd121262deba1cd1d7ebe2a089ed268b83d2b4cc Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 30 Nov 2012 06:48:17 -0500 Subject: [PATCH 223/281] minor changes --- lib/util.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/util.js b/lib/util.js index bf844703..b5742814 100644 --- a/lib/util.js +++ b/lib/util.js @@ -339,7 +339,10 @@ var Util, exports; lRet = Util.tryCatch(pTryFunc); - return Util.exec(pCallBack, lRet); + if(lRet) + Util.exec(pCallBack, lRet); + + return lRet; }; /** From 53a47d35325af355aa845037b10ab543f2bfeddc Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 30 Nov 2012 09:50:47 -0500 Subject: [PATCH 224/281] added url change on folder changing --- ChangeLog | 2 + client.js | 95 +- index.html | 8 +- lib/client/dom.js | 2092 ++++++++++++++++---------------- lib/client/google_analytics.js | 2 +- lib/server/appcache.js | 2 +- 6 files changed, 1113 insertions(+), 1088 deletions(-) diff --git a/ChangeLog b/ChangeLog index 51a8d62a..737cdb50 100644 --- a/ChangeLog +++ b/ChangeLog @@ -133,6 +133,8 @@ config.json only if it setted up. * Fixed bug with editor close, when started from menu. +* Added url change on folder changing. + 2012.10.01, Version 0.1.7 diff --git a/client.js b/client.js index 2d3cdd87..6cf7764e 100644 --- a/client.js +++ b/client.js @@ -113,7 +113,7 @@ CloudClient.GoogleAnalytics = function(){ document.onmousemove = function(){ setTimeout(function(){ - DOM.jsload('lib/client/google_analytics.js'); + DOM.jsload(cloudcmd.LIBDIRCLIENT + 'google_analytics.js'); },5000); Util.exec(lFunc); @@ -505,7 +505,6 @@ CloudClient._changeLinks = function(pPanelID){ * что js отключен */ lNoJS_s = CloudFunc.NOJS, - lFS_s = CloudFunc.FS, /* right mouse click function varible */ lOnContextMenu_f = function(pEvent){ @@ -576,14 +575,9 @@ CloudClient._changeLinks = function(pPanelID){ /* убираем адрес хоста*/ var link = a[i].href.replace(lUrl,''); - /* убираем значения, которые говорят, * - * об отсутствии js */ - if(link.indexOf(lNoJS_s) === lFS_s.length){ - link = link.replace(lNoJS_s,''); - } /* ставим загрузку гифа на клик*/ if(i === lREFRESHICON){ - a[i].onclick = CloudClient._loadDir(link,true); + a[i].onclick = CloudClient._loadDir(link, true); a[i].parentElement.onclick = a[i].onclick; } @@ -631,17 +625,31 @@ CloudClient._changeLinks = function(pPanelID){ * @param path - каталог для чтения * @param pNeedRefresh - необходимость обновить данные о каталоге */ -CloudClient._ajaxLoad = function(path, pNeedRefresh){ +CloudClient._ajaxLoad = function(pFullPath, pNeedRefresh){ /* Отображаем красивые пути */ + /* added supporting of russian language */ - var lPath = decodeURI(path), - lFS_s = CloudFunc.FS; + pFullPath = decodeURI(pFullPath); + + var lPath = pFullPath, + lFSPath, + lFS_s = CloudFunc.FS, + lNoJS_s = CloudFunc.NOJS; + /* + * убираем значения, которые, + * говорят об отсутствии js + */ + if(lPath.indexOf(lNoJS_s) === lFS_s.length){ + lPath = lFSPath = Util.removeStr(lPath, lNoJS_s); + } if(lPath.indexOf(lFS_s) === 0){ lPath = lPath.replace(lFS_s,''); - if(lPath === '') lPath = '/'; + if(lPath === '/') + pFullPath = '/'; } + console.log ('reading dir: "' + lPath + '";'); /* если доступен localStorage и @@ -674,42 +682,43 @@ CloudClient._ajaxLoad = function(path, pNeedRefresh){ CloudClient._createFileTable(lPanel, lJSON); CloudClient._changeLinks(lPanel); + DOM.setHistory(lPath, 'Cloud Commander', pFullPath); + return; } } - /* ######################## */ - Util.tryCatchLog(function(){ - DOM.ajax({ - url: path, - error: DOM.Images.showError, - - success:function(data, textStatus, jqXHR){ - /* если такой папки (или файла) нет - * прячем загрузку и показываем ошибку - */ - if(!jqXHR.responseText.indexOf('Error:')) - return DOM.showError(jqXHR); + DOM.ajax({ + url: lFSPath, + error: DOM.Images.showError, + + success:function(data, textStatus, jqXHR){ + /* если такой папки (или файла) нет + * прячем загрузку и показываем ошибку + */ + if(!jqXHR.responseText.indexOf('Error:')) + return DOM.showError(jqXHR); - CloudClient._createFileTable(lPanel, data); - CloudClient._changeLinks(lPanel); - - /* Сохраняем структуру каталогов в localStorage, - * если он поддерживаеться браузером - */ - /* переводим таблицу файлов в строку, для - * сохранения в localStorage - */ - var lJSON_s = JSON.stringify(data); - console.log(lJSON_s.length); - - /* если размер данных не очень бошьой - * сохраняем их в кэше - */ - if(lJSON_s.length<50000) - DOM.Cache.set(lPath,lJSON_s); - } - }); + CloudClient._createFileTable(lPanel, data); + CloudClient._changeLinks(lPanel); + + /* Сохраняем структуру каталогов в localStorage, + * если он поддерживаеться браузером + */ + /* переводим таблицу файлов в строку, для + * сохранения в localStorage + */ + var lJSON_s = JSON.stringify(data); + console.log(lJSON_s.length); + + /* если размер данных не очень бошьой + * сохраняем их в кэше + */ + if(lJSON_s.length < 50000 ) + DOM.Cache.set(lPath, lJSON_s); + + DOM.setHistory(lPath, 'Cloud Commander', pFullPath); + } }); }; diff --git a/index.html b/index.html index 1416705a..b0a2f6bb 100644 --- a/index.html +++ b/index.html @@ -40,11 +40,11 @@
    - - - + + + \ No newline at end of file diff --git a/lib/client/dom.js b/lib/client/dom.js index 468c54e3..0b2adae2 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -1,1040 +1,1054 @@ -var CloudCommander, Util, DOM, CloudFunc; - -(function(){ - "use strict"; - - DOM = {}; - - /* PRIVATE */ - - function getCurrentFile(){ - return CloudCommander.CURRENT_FILE; - } - - /** - * private function thet unset currentfile - */ - function UnSetCurrentFile(pCurrentFile){ - var lRet_b = DOM.isCurrentFile(pCurrentFile); - - if(!pCurrentFile) - DOM.addCloudStatus({ - code : -1, - msg : 'Error pCurrentFile in' + - 'unSetCurrentFile' + - 'could not be none' - }); - - if(lRet_b) - DOM.removeClass(pCurrentFile, getCurrentFile()); - - return lRet_b; - } - - /* private members */ - var XMLHTTP, - LoadingImage, - ErrorImage, - - /* Обьект, который содержит - * функции для отображения - * картинок - */ - Images_o = { - /* Функция создаёт картинку загрузки*/ - loading : function(){ - var lE = DOM.getById('loading-image'); - if (!lE) - lE = DOM.anyload({ - name : 'span', - className : 'icon loading', - id : 'loading-image', - not_append : true - }); - - LoadingImage = lE; - - return lE; - }, - - /* Функция создаёт картинку ошибки загрузки*/ - error : function(){ - var lE = DOM.getById('error-image'); - if (!lE) - lE = DOM.anyload({ - name : 'span', - className : 'icon error', - id : 'error-image', - not_append : true - }); - - return lE; - } - }; - - /** - * add class to current element - * @param pElement - * @param pClass - */ - DOM.addClass = function(pElement, pClass){ - var lRet_b = true; - - var lClassList = pElement.classList; - if(lClassList){ - if( !lClassList.contains(pClass) ) - lClassList.add(pClass); - else - lRet_b = false; - } - - return lRet_b; - }; - - /** - * safe add event listener - * @param pType - * @param pListener - * @param pUseCapture - * @param pElement {document by default} - */ - DOM.addListener = function(pType, pListener, pUseCapture, pElement){ - var lRet = this; - - (pElement || document).addEventListener( - pType, - pListener, - pUseCapture || false - ); - - return lRet; - }; - - /** - * safe add event keydown listener - * @param pListener - * @param pUseCapture - */ - DOM.addKeyListener = function(pListener, pUseCapture){ - return DOM.addListener('keydown', pListener, pUseCapture); - }; - - /** - * load file countent thrue ajax - */ - DOM.ajax = function(pParams){ - var lType = pParams.type || 'GET', - lData = pParams.data, - lSuccess_f = pParams.success; - - if(!XMLHTTP) - XMLHTTP = new XMLHttpRequest(); - - XMLHTTP.open(lType, pParams.url, true); - XMLHTTP.send(lData); - - if( !Util.isFunction(lSuccess_f) ){ - console.log('error in DOM.ajax onSuccess:', pParams); - console.log(pParams); - } - - XMLHTTP.onreadystatechange = function(pEvent){ - if (XMLHTTP.readyState === 4 /* Complete */){ - var lJqXHR = pEvent.target, - lType = XMLHTTP.getResponseHeader('content-type'); - - if (XMLHTTP.status === 200 /* OK */){ - var lData = lJqXHR.response; - - /* If it's json - parse it as json */ - if(lType && Util.isContainStr(lType, 'application/json') ){ - var lResult = Util.tryCatch(function(){ - lData = JSON.parse(lJqXHR.response); - }); - - if( Util.log(lResult) ) - lData = lJqXHR.response; - } - - lSuccess_f(lData, lJqXHR.statusText, lJqXHR); - } - else/* file not found or connection lost */{ - /* if html given or something like it - * getBack just status of result - */ - if(lType && - lType.indexOf('text/plain') !== 0){ - lJqXHR.responseText = lJqXHR.statusText; - } - Util.exec(pParams.error, lJqXHR); - } - } - }; - }; - - /** - * Обьект для работы с кэшем - * в него будут включены функции для - * работы с LocalStorage, webdb, - * indexed db etc. - */ - DOM.Cache = function(){ - /* приватный переключатель возможности работы с кэшем */ - var CacheAllowed; - - /* функция проверяет возможно ли работать с кэшем каким-либо образом */ - this.isAllowed = function(){ - return ( CacheAllowed = Util.isObject( window.localStorage ) ); - }; - - - /** - * allow cache usage - */ - this.setAllowed = function(){ - var lRet = this; - CacheAllowed = true; - - return lRet; - }; - - /** - * dissalow cache usage - */ - this.UnSetAllowed = function(){ - var lRet = this; - CacheAllowed = false; - - return lRet; - }; - - /** remove element */ - this.remove = function(pItem){ - var lRet = this; - - if(CacheAllowed) - localStorage.removeItem(pItem); - - return lRet; - }; - - /** если доступен localStorage и - * в нём есть нужная нам директория - - * записываем данные в него - */ - this.set = function(pName, pData){ - var lRet = this; - - if(CacheAllowed && pName && pData) - localStorage.setItem(pName,pData); - - return lRet; - }, - - /** Если доступен Cache принимаем из него данные*/ - this.get = function(pName){ - var lRet = this; - - if(CacheAllowed) - lRet = localStorage.getItem(pName); - - return lRet; - }, - - /* get all cache from local storage */ - this.getAll = function(){ - var lRet = null; - - if(CacheAllowed) - lRet = localStorage; - - return lRet; - }; - - /** функция чистит весь кэш для всех каталогов*/ - this.clear = function(){ - var lRet = this; - - if(CacheAllowed) - localStorage.clear(); - - return lRet; - }; - }; - - DOM.Cache = new DOM.Cache(); - - - /** - * Function gets id by src - * @param pSrc - * - * Example: http://domain.com/1.js -> 1_js - */ - DOM.getIdBySrc = function(pSrc){ - var lID = pSrc.replace(pSrc.substr(pSrc, - pSrc.lastIndexOf('/')+1), - ''); - - /* убираем точки */ - while(lID.indexOf('.') > 0) - lID = lID.replace('.','_'); - - return lID; - }, - - - /** - * create elements and load them to DOM-tree - * one-by-one - * - * @param pParams_a - * @param pFunc - onload function - */ - DOM.anyLoadOnLoad = function(pParams_a, pFunc){ - var lRet = this; - - if( Util.isArray(pParams_a) ) { - var lParam = pParams_a.pop(), - lFunc = function(){ - DOM.anyLoadOnLoad(pParams_a, pFunc); - }; - - if( Util.isString(lParam) ) - lParam = { src : lParam }; - else if( Util.isArray(lParam) ){ - - DOM.anyLoadInParallel(lParam, lFunc); - } - - if(lParam && !lParam.func){ - lParam.func = lFunc; - - DOM.anyload(lParam); - - }else - Util.exec(pFunc); - } - - return lRet; - }; - - /** - * improve callback of funcs so - * we pop number of function and - * if it's last we call pCallBack - * - * @param pParams_a - * @param pFunc - onload function - */ - DOM.anyLoadInParallel = function(pParams_a, pFunc){ - var lRet = this, - done = [], - - doneFunc = function (pCallBack){ - Util.exec(pCallBack); - - if( !done.pop() ) - Util.exec(pFunc); - }; - - if( !Util.isArray(pParams_a) ){ - pParams_a = [pParams_a]; - } - - for(var i = 0, n = pParams_a.length; i < n; i++){ - var lParam = pParams_a.pop(); - - if(lParam){ - done.push(i); - - if(Util.isString(lParam) ) - lParam = { src : lParam }; - - var lFunc = lParam.func; - lParam.func = Util.retExec(doneFunc, lFunc); - - DOM.anyload(lParam); - } - } - - return lRet; - }; - - /** - * Функция создаёт элемент и загружает файл с 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 - * } - */ - DOM.anyload = function(pParams_o){ - - if( !pParams_o ) return; - - /* if a couple of params was - * processing every of params - * and quit - */ - if( Util.isArray(pParams_o) ){ - var lElements_a = []; - for(var i = 0, n = pParams_o.length; i < n ; i++) - lElements_a[i] = DOM.anyload(pParams_o[i]); - - return lElements_a; - } - - var lName = pParams_o.name, - 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, - lInner = pParams_o.inner, - lNotAppend = pParams_o.not_append; - - if ( Util.isObject(lFunc) ){ - lOnError = lFunc.onerror; - lFunc = lFunc.onload; - } - /* убираем путь к файлу, оставляя только название файла */ - if(!lID && lSrc) - lID = DOM.getIdBySrc(lSrc); - - var element = DOM.getById(lID); - - /* если скрипт еще не загружен */ - if(!element){ - 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'}; - } - } - element = document.createElement(lName); - - if(lID) - element.id = lID; - - if(lClass) - element.className = lClass; - - /* if working with external css - * using href in any other case - * using src - */ - if(lName === 'link'){ - element.href = lSrc; - element.rel = 'stylesheet'; - }else - element.src = lSrc; - - /* - * if passed arguments function - * then it's onload by default - * - * if object - then onload and onerror - */ - - if( Util.isFunction(lFunc) ) - element.onload = lFunc; - - /* if element (js/css) will not loaded - * it would be removed from DOM tree - * and error image would be shown - */ - element.onerror = (function(){ - (pParams_o.parent || document.body) - .removeChild(element); - - DOM.Images.showError({ - responseText: 'file ' + - lSrc + - ' could not be loaded', - status : 404 - }); - - Util.exec(lOnError); - }); - - if(pParams_o.style){ - element.style.cssText = pParams_o.style; - } - - if(lAsync || lAsync === undefined) - element.async = true; - - if(!lNotAppend) - (lParent || document.body).appendChild(element); - - if(lInner){ - element.innerHTML = lInner; - } - } - /* если js-файл уже загружен - * запускаем функцию onload - */ - else - Util.exec(lFunc); - - return element; - }, - - /** - * Функция загружает js-файл - * - * @param pSrc - * @param pFunc - */ - DOM.jsload = function(pSrc, pFunc){ - if(pSrc instanceof Array){ - for(var i=0; i < pSrc.length; i++) - pSrc[i].name = 'script'; - - return DOM.anyload(pSrc); - } - - return DOM.anyload({ - name : 'script', - src : pSrc, - func : pFunc - }); - }, - - /** - * Функция создаёт елемент style и записывает туда стили - * @param pParams_o - структура параметров, заполняеться таким - * образом: {src: ' ',func: '', id: '', element: '', inner: ''} - * все параметры опциональны - */ - DOM.cssSet = function(pParams_o){ - pParams_o.name = 'style'; - pParams_o.parent = pParams_o.parent || document.head; - - return DOM.anyload(pParams_o); - }, - - /** - * Function loads external css files - * @pParams_o - структура параметров, заполняеться таким - * образом: {src: ' ',func: '', id: '', element: '', inner: ''} - * все параметры опциональны - */ - DOM.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 DOM.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 DOM.anyload(pParams_o); - }; - - /** - * load jquery from google cdn or local copy - * @param pCallBack - */ - DOM.jqueryLoad = function(pCallBack){ - /* загружаем jquery: */ - DOM.jsload('//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js',{ - onload: Util.retExec(pCallBack), - - onerror: function(){ - DOM.jsload('lib/client/jquery.js'); - - /* - * if could not load jquery from google server - * maybe we offline, load font from local - * directory - */ - DOM.cssSet({id:'local-droids-font', - element : document.head, - inner : '@font-face {font-family: "Droid Sans Mono";' + - 'font-style: normal;font-weight: normal;' + - 'src: local("Droid Sans Mono"), local("DroidSansMono"),'+ - ' url("font/DroidSansMono.woff") format("woff");}' - }); - } - }); - }; - - /** - * load socket.io - * @param pCallBack - */ - DOM.socketLoad = function(pCallBack){ - DOM.jsload('lib/client/socket.js', pCallBack); - }; - - /* DOM */ - - /** - * Function search element by tag - * @param pTag - className - * @param pElement - element - */ - DOM.getByTag = function(pTag, pElement){ - return (pElement || document).getElementsByTagName(pTag); - }; - - /** - * Function search element by id - * @param Id - className - * @param pElement - element - */ - DOM.getById = function(pId, pElement){ - return (pElement || document).getElementById(pId); - }; - - /** - * Function search element by class name - * @param pClass - className - * @param pElement - element - */ - DOM.getByClass = function(pClass, pElement){ - return (pElement || document).getElementsByClassName(pClass); - }; - - - DOM.Images = { - /** - * Function shows loading spinner - * pPosition = {top: true}; - */ - showLoad : function(pPosition){ - var lRet_b = true; - - LoadingImage = Images_o.loading(); - ErrorImage = Images_o.error(); - - DOM.hide(ErrorImage); - - var lCurrent; - if(pPosition){ - if(pPosition.top){ - lCurrent = DOM.getRefreshButton(); - if(lCurrent) - lCurrent = lCurrent.parentElement; - else - lRet_b = false; - } - } - else{ - lCurrent = DOM.getCurrentFile(); - lCurrent = lCurrent.firstChild.nextSibling; - } - - /* show loading icon - * if it not showed - * and if error was not - * heppen - */ - if(lRet_b){ - var lParent = LoadingImage.parentElement; - if(!lParent || - (lParent && lParent !== lCurrent)) - lCurrent.appendChild(LoadingImage); - - DOM.show(LoadingImage); /* показываем загрузку*/ - } - - return lRet_b; - }, - - /** - * hide load image - */ - hideLoad : function(){ - LoadingImage = Images_o.loading(); - DOM.hide(LoadingImage); - }, - - /** - * show error image (usualy after error on ajax request) - */ - showError : function(jqXHR, textStatus, errorThrown){ - LoadingImage = Images_o.loading(); - - ErrorImage = Images_o.error(); - - var lText; - if(jqXHR.status === 404) - lText = jqXHR.responseText; - else - lText = jqXHR.statusText; - - /* если файла не существует*/ - if(!lText.indexOf('Error: ENOENT, ')) - lText = lText.replace('Error: ENOENT, n','N'); - - /* если не хватает прав для чтения файла*/ - else if(!lText.indexOf('Error: EACCES,')) - lText = lText.replace('Error: EACCES, p','P'); - - DOM.show(ErrorImage); - ErrorImage.title = lText; - - var lParent = LoadingImage.parentElement; - if(lParent) - lParent.appendChild(ErrorImage); - - DOM.hide(LoadingImage); - - console.log(lText); - } - }; - - /** - * unified way to get current file - */ - DOM.getCurrentFile = function(){ - var lCurrent = DOM.getByClass(getCurrentFile())[0]; - if(!lCurrent){ - DOM.addCloudStatus({ - code : -1, - msg : 'Error: can not find ' + - 'CurrentFile ' + - 'in getCurrentFile' - }); - } - return lCurrent; - }; - - - /** - * unified way to get RefreshButton - */ - DOM.getRefreshButton = function(){ - var lPanel = DOM.getPanel(), - lRefresh = DOM.getByClass(CloudFunc.REFRESHICON, lPanel); - - if (lRefresh.length) - lRefresh = lRefresh[0]; - else { - DOM.addCloudStatus({ - code : -3, - msg : 'Error Refresh icon not found' - }); - lRefresh = false; - } - - return lRefresh; - }; - - /** - * unified way to set current file - */ - DOM.setCurrentFile = function(pCurrentFile){ - var lRet_b = true; - - if(!pCurrentFile){ - DOM.addCloudStatus({ - code : -1, - msg : 'Error pCurrentFile in' + - 'setCurrentFile' + - 'could not be none' - }); - - lRet_b = false; - } - var lCurrentFileWas = DOM.getCurrentFile(); - - if (pCurrentFile.className === 'path') - pCurrentFile = pCurrentFile.nextSibling; - - if (pCurrentFile.className === 'fm_header') - pCurrentFile = pCurrentFile.nextSibling; - - if(lCurrentFileWas) - UnSetCurrentFile(lCurrentFileWas); - - DOM.addClass(pCurrentFile, getCurrentFile()); - - /* scrolling to current file */ - DOM.scrollIntoViewIfNeeded(pCurrentFile); - - return lRet_b; - }; - - /** - * set onclick handler on buttons f1-f10 - * @param pKey - 'f1'-'f10' - */ - DOM.setButtonKey = function(pKey, pFunc){ - return CloudCommander.KeysPanel[pKey].onclick = pFunc; - }; - - /** - * current file check - * - * @param pCurrentFile - */ - DOM.isCurrentFile = function(pCurrentFile){ - if(!pCurrentFile) - DOM.addCloudStatus({ - code : -1, - msg : 'Error pCurrentFile in' + - 'isCurrentFile' + - 'could not be none' - }); - - var lCurrentFileClass = pCurrentFile.className, - lIsCurrent = lCurrentFileClass.indexOf(getCurrentFile()) >= 0; - - return lIsCurrent; - }; - - - /** - * get link from current (or param) file - * - * @param pCurrentFile - current file by default - */ - DOM.getCurrentLink = function(pCurrentFile){ - var lLink = DOM.getByTag('a', - pCurrentFile || DOM.getCurrentFile()), - - lRet = lLink.length > 0 ? lLink[0] : -1; - - if(!lRet) - DOM.addCloudStatus({ - code : -1, - msg : 'Error current element do not contain links' - }); - - return lRet; - }; - - /** - * get name from current (or param) file - * - * @param pCurrentFile - */ - DOM.getCurrentName = function(pCurrentFile){ - var lLink = DOM.getCurrentLink( - pCurrentFile || DOM.getCurrentFile()); - - if(!lLink) - DOM.addCloudStatus({ - code : -1, - msg : 'Error current element do not contain links' - }); - else lLink = lLink.textContent; - - return lLink; - }; - - /** function getting FM - * @param pPanel_o = {active: true} - */ - DOM.getFM = function(){ - return DOM.getPanel().parentElement; - }; - - /** function getting panel active, or passive - * @param pPanel_o = {active: true} - */ - DOM.getPanel = function(pActive){ - var lPanel = DOM.getCurrentFile().parentElement; - - /* if {active : false} getting passive panel */ - if(pActive && !pActive.active){ - var lId = lPanel.id === 'left' ? 'right' : 'left'; - lPanel = DOM.getById(lId); - } - - /* if two panels showed - * then always work with passive - * panel - */ - if(window.innerWidth < CloudCommander.MIN_ONE_PANEL_WIDTH) - lPanel = DOM.getById('left'); - - - if(!lPanel) - console.log('Error can not find Active Panel'); - - return lPanel; - }; - - DOM.show = function(pElement){ - DOM.removeClass(pElement, 'hidden'); - }; - - /** - * shows panel right or left (or active) - */ - DOM.showPanel = function(pActive){ - var lRet = true, - lPanel = DOM.getPanel(pActive); - - if(lPanel) - DOM.show(lPanel); - else - lRet = false; - - return lRet; - }; - - /** - * hides panel right or left (or active) - */ - DOM.hidePanel = function(pActive){ - var lRet = false, - lPanel = DOM.getPanel(pActive); - - if(lPanel) - lRet = DOM.hide(lPanel); - - return lRet; - }; - - /** - * add class=hidden to element - * - * @param pElement - */ - DOM.hide = function(pElement){ - return DOM.addClass(pElement, 'hidden'); - }; - - /** - * remove child of element - * @param pChild - * @param pElement - */ - DOM.remove = function(pChild, pElement){ - return (pElement || document.body).removeChild(pChild); - }; - - /** - * remove class pClass from element pElement - * @param pElement - * @param pClass - */ - DOM.removeClass = function(pElement, pClass){ - var lRet_b = true, - lClassList = pElement.classList; - - if(pElement && lClassList) - lClassList.remove(pClass); - - else - lRet_b = false; - - return lRet_b; - }; - - /** - * remove current file from file table - * @pCurrent - */ - DOM.removeCurrent = function(pCurrent){ - var lParent = pCurrent.parentElement; - - if(!pCurrent) - pCurrent = DOM.getCurrentFile(); - var lName = DOM.getCurrentName(pCurrent); - - if(pCurrent && lParent){ - if(lName !== '..'){ - var lNext = pCurrent.nextSibling; - var lPrevious = pCurrent.previousSibling; - if(lNext) - DOM.setCurrentFile(lNext); - else if(lPrevious) - DOM.setCurrentFile(lPrevious); - - lParent.removeChild(pCurrent); - } - else - DOM.addCloudStatus({ - code : -1, - msg : 'Could not remove parrent dir' - }); - } - else - DOM.addCloudStatus({ - code : -1, - msg : 'Current file (or parent of current) could not be empty' - }); - - return pCurrent; - }; - - /** - * unified way to scrollIntoViewIfNeeded - * (native suporte by webkit only) - * @param pElement - */ - DOM.scrollIntoViewIfNeeded = function(pElement){ - var lRet = true; - - if(pElement && pElement.scrollIntoViewIfNeeded) - pElement.scrollIntoViewIfNeeded(); - else - lRet = false; - - return lRet; - }; - - /** - * function gets time - */ - DOM.getTime = function(){ - var date = new Date(), - hours = date.getHours(), - minutes = date.getMinutes(), - seconds = date.getSeconds(); - - minutes = minutes < 10 ? '0' + minutes : minutes; - seconds = seconds < 10 ? '0' + seconds : seconds; - - return hours + ":" + minutes + ":" + seconds; - }; - - /** - * array of all statuses of opertattions - */ - DOM.CloudStatus = []; - - /** - * adds status of operation - * @param pStatus - */ - DOM.addCloudStatus = function(pStatus){ - DOM.CloudStatus[DOM.CloudStatus.length] = pStatus; - }; +var CloudCommander, Util, DOM, CloudFunc; + +(function(){ + "use strict"; + + DOM = {}; + + /* PRIVATE */ + + function getCurrentFile(){ + return CloudCommander.CURRENT_FILE; + } + + /** + * private function thet unset currentfile + */ + function UnSetCurrentFile(pCurrentFile){ + var lRet_b = DOM.isCurrentFile(pCurrentFile); + + if(!pCurrentFile) + DOM.addCloudStatus({ + code : -1, + msg : 'Error pCurrentFile in' + + 'unSetCurrentFile' + + 'could not be none' + }); + + if(lRet_b) + DOM.removeClass(pCurrentFile, getCurrentFile()); + + return lRet_b; + } + + /* private members */ + var XMLHTTP, + LoadingImage, + ErrorImage, + + /* Обьект, который содержит + * функции для отображения + * картинок + */ + Images_o = { + /* Функция создаёт картинку загрузки*/ + loading : function(){ + var lE = DOM.getById('loading-image'); + if (!lE) + lE = DOM.anyload({ + name : 'span', + className : 'icon loading', + id : 'loading-image', + not_append : true + }); + + LoadingImage = lE; + + return lE; + }, + + /* Функция создаёт картинку ошибки загрузки*/ + error : function(){ + var lE = DOM.getById('error-image'); + if (!lE) + lE = DOM.anyload({ + name : 'span', + className : 'icon error', + id : 'error-image', + not_append : true + }); + + return lE; + } + }; + + /** + * add class to current element + * @param pElement + * @param pClass + */ + DOM.addClass = function(pElement, pClass){ + var lRet_b = true; + + var lClassList = pElement.classList; + if(lClassList){ + if( !lClassList.contains(pClass) ) + lClassList.add(pClass); + else + lRet_b = false; + } + + return lRet_b; + }; + + /** + * safe add event listener + * @param pType + * @param pListener + * @param pUseCapture + * @param pElement {document by default} + */ + DOM.addListener = function(pType, pListener, pUseCapture, pElement){ + var lRet = this; + + (pElement || document).addEventListener( + pType, + pListener, + pUseCapture || false + ); + + return lRet; + }; + + /** + * safe add event keydown listener + * @param pListener + * @param pUseCapture + */ + DOM.addKeyListener = function(pListener, pUseCapture){ + return DOM.addListener('keydown', pListener, pUseCapture); + }; + + /** + * load file countent thrue ajax + */ + DOM.ajax = function(pParams){ + var lType = pParams.type || 'GET', + lData = pParams.data, + lSuccess_f = pParams.success; + + if(!XMLHTTP) + XMLHTTP = new XMLHttpRequest(); + + XMLHTTP.open(lType, pParams.url, true); + XMLHTTP.send(lData); + + if( !Util.isFunction(lSuccess_f) ){ + console.log('error in DOM.ajax onSuccess:', pParams); + console.log(pParams); + } + + XMLHTTP.onreadystatechange = function(pEvent){ + if (XMLHTTP.readyState === 4 /* Complete */){ + var lJqXHR = pEvent.target, + lType = XMLHTTP.getResponseHeader('content-type'); + + if (XMLHTTP.status === 200 /* OK */){ + var lData = lJqXHR.response; + + /* If it's json - parse it as json */ + if(lType && Util.isContainStr(lType, 'application/json') ){ + var lResult = Util.tryCatch(function(){ + lData = JSON.parse(lJqXHR.response); + }); + + if( Util.log(lResult) ) + lData = lJqXHR.response; + } + + lSuccess_f(lData, lJqXHR.statusText, lJqXHR); + } + else/* file not found or connection lost */{ + /* if html given or something like it + * getBack just status of result + */ + if(lType && + lType.indexOf('text/plain') !== 0){ + lJqXHR.responseText = lJqXHR.statusText; + } + Util.exec(pParams.error, lJqXHR); + } + } + }; + }; + + /** + * Обьект для работы с кэшем + * в него будут включены функции для + * работы с LocalStorage, webdb, + * indexed db etc. + */ + DOM.Cache = function(){ + /* приватный переключатель возможности работы с кэшем */ + var CacheAllowed; + + /* функция проверяет возможно ли работать с кэшем каким-либо образом */ + this.isAllowed = function(){ + return ( CacheAllowed = Util.isObject( window.localStorage ) ); + }; + + + /** + * allow cache usage + */ + this.setAllowed = function(){ + var lRet = this; + CacheAllowed = true; + + return lRet; + }; + + /** + * dissalow cache usage + */ + this.UnSetAllowed = function(){ + var lRet = this; + CacheAllowed = false; + + return lRet; + }; + + /** remove element */ + this.remove = function(pItem){ + var lRet = this; + + if(CacheAllowed) + localStorage.removeItem(pItem); + + return lRet; + }; + + /** если доступен localStorage и + * в нём есть нужная нам директория - + * записываем данные в него + */ + this.set = function(pName, pData){ + var lRet = this; + + if(CacheAllowed && pName && pData) + localStorage.setItem(pName,pData); + + return lRet; + }, + + /** Если доступен Cache принимаем из него данные*/ + this.get = function(pName){ + var lRet = this; + + if(CacheAllowed) + lRet = localStorage.getItem(pName); + + return lRet; + }, + + /* get all cache from local storage */ + this.getAll = function(){ + var lRet = null; + + if(CacheAllowed) + lRet = localStorage; + + return lRet; + }; + + /** функция чистит весь кэш для всех каталогов*/ + this.clear = function(){ + var lRet = this; + + if(CacheAllowed) + localStorage.clear(); + + return lRet; + }; + }; + + DOM.Cache = new DOM.Cache(); + + + /** + * Function gets id by src + * @param pSrc + * + * Example: http://domain.com/1.js -> 1_js + */ + DOM.getIdBySrc = function(pSrc){ + var lID = pSrc.replace(pSrc.substr(pSrc, + pSrc.lastIndexOf('/')+1), + ''); + + /* убираем точки */ + while(lID.indexOf('.') > 0) + lID = lID.replace('.','_'); + + return lID; + }, + + + /** + * create elements and load them to DOM-tree + * one-by-one + * + * @param pParams_a + * @param pFunc - onload function + */ + DOM.anyLoadOnLoad = function(pParams_a, pFunc){ + var lRet = this; + + if( Util.isArray(pParams_a) ) { + var lParam = pParams_a.pop(), + lFunc = function(){ + DOM.anyLoadOnLoad(pParams_a, pFunc); + }; + + if( Util.isString(lParam) ) + lParam = { src : lParam }; + else if( Util.isArray(lParam) ){ + + DOM.anyLoadInParallel(lParam, lFunc); + } + + if(lParam && !lParam.func){ + lParam.func = lFunc; + + DOM.anyload(lParam); + + }else + Util.exec(pFunc); + } + + return lRet; + }; + + /** + * improve callback of funcs so + * we pop number of function and + * if it's last we call pCallBack + * + * @param pParams_a + * @param pFunc - onload function + */ + DOM.anyLoadInParallel = function(pParams_a, pFunc){ + var lRet = this, + done = [], + + doneFunc = function (pCallBack){ + Util.exec(pCallBack); + + if( !done.pop() ) + Util.exec(pFunc); + }; + + if( !Util.isArray(pParams_a) ){ + pParams_a = [pParams_a]; + } + + for(var i = 0, n = pParams_a.length; i < n; i++){ + var lParam = pParams_a.pop(); + + if(lParam){ + done.push(i); + + if(Util.isString(lParam) ) + lParam = { src : lParam }; + + var lFunc = lParam.func; + lParam.func = Util.retExec(doneFunc, lFunc); + + DOM.anyload(lParam); + } + } + + return lRet; + }; + + /** + * Функция создаёт элемент и загружает файл с 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 + * } + */ + DOM.anyload = function(pParams_o){ + + if( !pParams_o ) return; + + /* if a couple of params was + * processing every of params + * and quit + */ + if( Util.isArray(pParams_o) ){ + var lElements_a = []; + for(var i = 0, n = pParams_o.length; i < n ; i++) + lElements_a[i] = DOM.anyload(pParams_o[i]); + + return lElements_a; + } + + var lName = pParams_o.name, + 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, + lInner = pParams_o.inner, + lNotAppend = pParams_o.not_append; + + if ( Util.isObject(lFunc) ){ + lOnError = lFunc.onerror; + lFunc = lFunc.onload; + } + /* убираем путь к файлу, оставляя только название файла */ + if(!lID && lSrc) + lID = DOM.getIdBySrc(lSrc); + + var element = DOM.getById(lID); + + /* если скрипт еще не загружен */ + if(!element){ + 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'}; + } + } + element = document.createElement(lName); + + if(lID) + element.id = lID; + + if(lClass) + element.className = lClass; + + /* if working with external css + * using href in any other case + * using src + */ + if(lName === 'link'){ + element.href = lSrc; + element.rel = 'stylesheet'; + }else + element.src = lSrc; + + /* + * if passed arguments function + * then it's onload by default + * + * if object - then onload and onerror + */ + + if( Util.isFunction(lFunc) ) + element.onload = lFunc; + + /* if element (js/css) will not loaded + * it would be removed from DOM tree + * and error image would be shown + */ + element.onerror = (function(){ + (pParams_o.parent || document.body) + .removeChild(element); + + DOM.Images.showError({ + responseText: 'file ' + + lSrc + + ' could not be loaded', + status : 404 + }); + + Util.exec(lOnError); + }); + + if(pParams_o.style){ + element.style.cssText = pParams_o.style; + } + + if(lAsync || lAsync === undefined) + element.async = true; + + if(!lNotAppend) + (lParent || document.body).appendChild(element); + + if(lInner){ + element.innerHTML = lInner; + } + } + /* если js-файл уже загружен + * запускаем функцию onload + */ + else + Util.exec(lFunc); + + return element; + }, + + /** + * Функция загружает js-файл + * + * @param pSrc + * @param pFunc + */ + DOM.jsload = function(pSrc, pFunc){ + if(pSrc instanceof Array){ + for(var i=0; i < pSrc.length; i++) + pSrc[i].name = 'script'; + + return DOM.anyload(pSrc); + } + + return DOM.anyload({ + name : 'script', + src : pSrc, + func : pFunc + }); + }, + + /** + * Функция создаёт елемент style и записывает туда стили + * @param pParams_o - структура параметров, заполняеться таким + * образом: {src: ' ',func: '', id: '', element: '', inner: ''} + * все параметры опциональны + */ + DOM.cssSet = function(pParams_o){ + pParams_o.name = 'style'; + pParams_o.parent = pParams_o.parent || document.head; + + return DOM.anyload(pParams_o); + }, + + /** + * Function loads external css files + * @pParams_o - структура параметров, заполняеться таким + * образом: {src: ' ',func: '', id: '', element: '', inner: ''} + * все параметры опциональны + */ + DOM.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 DOM.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 DOM.anyload(pParams_o); + }; + + /** + * load jquery from google cdn or local copy + * @param pCallBack + */ + DOM.jqueryLoad = function(pCallBack){ + /* загружаем jquery: */ + DOM.jsload('//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js',{ + onload: Util.retExec(pCallBack), + + onerror: function(){ + DOM.jsload('lib/client/jquery.js'); + + /* + * if could not load jquery from google server + * maybe we offline, load font from local + * directory + */ + DOM.cssSet({id:'local-droids-font', + element : document.head, + inner : '@font-face {font-family: "Droid Sans Mono";' + + 'font-style: normal;font-weight: normal;' + + 'src: local("Droid Sans Mono"), local("DroidSansMono"),'+ + ' url("font/DroidSansMono.woff") format("woff");}' + }); + } + }); + }; + + /** + * load socket.io + * @param pCallBack + */ + DOM.socketLoad = function(pCallBack){ + DOM.jsload('lib/client/socket.js', pCallBack); + }; + + /* DOM */ + + /** + * Function search element by tag + * @param pTag - className + * @param pElement - element + */ + DOM.getByTag = function(pTag, pElement){ + return (pElement || document).getElementsByTagName(pTag); + }; + + /** + * Function search element by id + * @param Id - className + * @param pElement - element + */ + DOM.getById = function(pId, pElement){ + return (pElement || document).getElementById(pId); + }; + + /** + * Function search element by class name + * @param pClass - className + * @param pElement - element + */ + DOM.getByClass = function(pClass, pElement){ + return (pElement || document).getElementsByClassName(pClass); + }; + + + DOM.Images = { + /** + * Function shows loading spinner + * pPosition = {top: true}; + */ + showLoad : function(pPosition){ + var lRet_b = true; + + LoadingImage = Images_o.loading(); + ErrorImage = Images_o.error(); + + DOM.hide(ErrorImage); + + var lCurrent; + if(pPosition){ + if(pPosition.top){ + lCurrent = DOM.getRefreshButton(); + if(lCurrent) + lCurrent = lCurrent.parentElement; + else + lRet_b = false; + } + } + else{ + lCurrent = DOM.getCurrentFile(); + lCurrent = lCurrent.firstChild.nextSibling; + } + + /* show loading icon + * if it not showed + * and if error was not + * heppen + */ + if(lRet_b){ + var lParent = LoadingImage.parentElement; + if(!lParent || + (lParent && lParent !== lCurrent)) + lCurrent.appendChild(LoadingImage); + + DOM.show(LoadingImage); /* показываем загрузку*/ + } + + return lRet_b; + }, + + /** + * hide load image + */ + hideLoad : function(){ + LoadingImage = Images_o.loading(); + DOM.hide(LoadingImage); + }, + + /** + * show error image (usualy after error on ajax request) + */ + showError : function(jqXHR, textStatus, errorThrown){ + LoadingImage = Images_o.loading(); + + ErrorImage = Images_o.error(); + + var lText; + if(jqXHR.status === 404) + lText = jqXHR.responseText; + else + lText = jqXHR.statusText; + + /* если файла не существует*/ + if(!lText.indexOf('Error: ENOENT, ')) + lText = lText.replace('Error: ENOENT, n','N'); + + /* если не хватает прав для чтения файла*/ + else if(!lText.indexOf('Error: EACCES,')) + lText = lText.replace('Error: EACCES, p','P'); + + DOM.show(ErrorImage); + ErrorImage.title = lText; + + var lParent = LoadingImage.parentElement; + if(lParent) + lParent.appendChild(ErrorImage); + + DOM.hide(LoadingImage); + + console.log(lText); + } + }; + + /** + * unified way to get current file + */ + DOM.getCurrentFile = function(){ + var lCurrent = DOM.getByClass(getCurrentFile())[0]; + if(!lCurrent){ + DOM.addCloudStatus({ + code : -1, + msg : 'Error: can not find ' + + 'CurrentFile ' + + 'in getCurrentFile' + }); + } + return lCurrent; + }; + + + /** + * unified way to get RefreshButton + */ + DOM.getRefreshButton = function(){ + var lPanel = DOM.getPanel(), + lRefresh = DOM.getByClass(CloudFunc.REFRESHICON, lPanel); + + if (lRefresh.length) + lRefresh = lRefresh[0]; + else { + DOM.addCloudStatus({ + code : -3, + msg : 'Error Refresh icon not found' + }); + lRefresh = false; + } + + return lRefresh; + }; + + /** + * unified way to set current file + */ + DOM.setCurrentFile = function(pCurrentFile){ + var lRet_b = true; + + if(!pCurrentFile){ + DOM.addCloudStatus({ + code : -1, + msg : 'Error pCurrentFile in' + + 'setCurrentFile' + + 'could not be none' + }); + + lRet_b = false; + } + var lCurrentFileWas = DOM.getCurrentFile(); + + if (pCurrentFile.className === 'path') + pCurrentFile = pCurrentFile.nextSibling; + + if (pCurrentFile.className === 'fm_header') + pCurrentFile = pCurrentFile.nextSibling; + + if(lCurrentFileWas) + UnSetCurrentFile(lCurrentFileWas); + + DOM.addClass(pCurrentFile, getCurrentFile()); + + /* scrolling to current file */ + DOM.scrollIntoViewIfNeeded(pCurrentFile); + + return lRet_b; + }; + + /** + * setting history wrapper + */ + DOM.setHistory = function(pData, pTitle, pUrl){ + var lRet = true; + + if(window.history) + history.pushState(pData, pTitle, pUrl); + else + lRet = false; + + return lRet; + }; + + /** + * set onclick handler on buttons f1-f10 + * @param pKey - 'f1'-'f10' + */ + DOM.setButtonKey = function(pKey, pFunc){ + return CloudCommander.KeysPanel[pKey].onclick = pFunc; + }; + + /** + * current file check + * + * @param pCurrentFile + */ + DOM.isCurrentFile = function(pCurrentFile){ + if(!pCurrentFile) + DOM.addCloudStatus({ + code : -1, + msg : 'Error pCurrentFile in' + + 'isCurrentFile' + + 'could not be none' + }); + + var lCurrentFileClass = pCurrentFile.className, + lIsCurrent = lCurrentFileClass.indexOf(getCurrentFile()) >= 0; + + return lIsCurrent; + }; + + + /** + * get link from current (or param) file + * + * @param pCurrentFile - current file by default + */ + DOM.getCurrentLink = function(pCurrentFile){ + var lLink = DOM.getByTag('a', + pCurrentFile || DOM.getCurrentFile()), + + lRet = lLink.length > 0 ? lLink[0] : -1; + + if(!lRet) + DOM.addCloudStatus({ + code : -1, + msg : 'Error current element do not contain links' + }); + + return lRet; + }; + + /** + * get name from current (or param) file + * + * @param pCurrentFile + */ + DOM.getCurrentName = function(pCurrentFile){ + var lLink = DOM.getCurrentLink( + pCurrentFile || DOM.getCurrentFile()); + + if(!lLink) + DOM.addCloudStatus({ + code : -1, + msg : 'Error current element do not contain links' + }); + else lLink = lLink.textContent; + + return lLink; + }; + + /** function getting FM + * @param pPanel_o = {active: true} + */ + DOM.getFM = function(){ + return DOM.getPanel().parentElement; + }; + + /** function getting panel active, or passive + * @param pPanel_o = {active: true} + */ + DOM.getPanel = function(pActive){ + var lPanel = DOM.getCurrentFile().parentElement; + + /* if {active : false} getting passive panel */ + if(pActive && !pActive.active){ + var lId = lPanel.id === 'left' ? 'right' : 'left'; + lPanel = DOM.getById(lId); + } + + /* if two panels showed + * then always work with passive + * panel + */ + if(window.innerWidth < CloudCommander.MIN_ONE_PANEL_WIDTH) + lPanel = DOM.getById('left'); + + + if(!lPanel) + console.log('Error can not find Active Panel'); + + return lPanel; + }; + + DOM.show = function(pElement){ + DOM.removeClass(pElement, 'hidden'); + }; + + /** + * shows panel right or left (or active) + */ + DOM.showPanel = function(pActive){ + var lRet = true, + lPanel = DOM.getPanel(pActive); + + if(lPanel) + DOM.show(lPanel); + else + lRet = false; + + return lRet; + }; + + /** + * hides panel right or left (or active) + */ + DOM.hidePanel = function(pActive){ + var lRet = false, + lPanel = DOM.getPanel(pActive); + + if(lPanel) + lRet = DOM.hide(lPanel); + + return lRet; + }; + + /** + * add class=hidden to element + * + * @param pElement + */ + DOM.hide = function(pElement){ + return DOM.addClass(pElement, 'hidden'); + }; + + /** + * remove child of element + * @param pChild + * @param pElement + */ + DOM.remove = function(pChild, pElement){ + return (pElement || document.body).removeChild(pChild); + }; + + /** + * remove class pClass from element pElement + * @param pElement + * @param pClass + */ + DOM.removeClass = function(pElement, pClass){ + var lRet_b = true, + lClassList = pElement.classList; + + if(pElement && lClassList) + lClassList.remove(pClass); + + else + lRet_b = false; + + return lRet_b; + }; + + /** + * remove current file from file table + * @pCurrent + */ + DOM.removeCurrent = function(pCurrent){ + var lParent = pCurrent.parentElement; + + if(!pCurrent) + pCurrent = DOM.getCurrentFile(); + var lName = DOM.getCurrentName(pCurrent); + + if(pCurrent && lParent){ + if(lName !== '..'){ + var lNext = pCurrent.nextSibling; + var lPrevious = pCurrent.previousSibling; + if(lNext) + DOM.setCurrentFile(lNext); + else if(lPrevious) + DOM.setCurrentFile(lPrevious); + + lParent.removeChild(pCurrent); + } + else + DOM.addCloudStatus({ + code : -1, + msg : 'Could not remove parrent dir' + }); + } + else + DOM.addCloudStatus({ + code : -1, + msg : 'Current file (or parent of current) could not be empty' + }); + + return pCurrent; + }; + + /** + * unified way to scrollIntoViewIfNeeded + * (native suporte by webkit only) + * @param pElement + */ + DOM.scrollIntoViewIfNeeded = function(pElement){ + var lRet = true; + + if(pElement && pElement.scrollIntoViewIfNeeded) + pElement.scrollIntoViewIfNeeded(); + else + lRet = false; + + return lRet; + }; + + /** + * function gets time + */ + DOM.getTime = function(){ + var date = new Date(), + hours = date.getHours(), + minutes = date.getMinutes(), + seconds = date.getSeconds(); + + minutes = minutes < 10 ? '0' + minutes : minutes; + seconds = seconds < 10 ? '0' + seconds : seconds; + + return hours + ":" + minutes + ":" + seconds; + }; + + /** + * array of all statuses of opertattions + */ + DOM.CloudStatus = []; + + /** + * adds status of operation + * @param pStatus + */ + DOM.addCloudStatus = function(pStatus){ + DOM.CloudStatus[DOM.CloudStatus.length] = pStatus; + }; })(); \ No newline at end of file diff --git a/lib/client/google_analytics.js b/lib/client/google_analytics.js index 82396855..9e07312e 100644 --- a/lib/client/google_analytics.js +++ b/lib/client/google_analytics.js @@ -1,4 +1,4 @@ -var CloudCommander, DOM, _gaq; +var DOM, _gaq; (function(){ "use strict"; diff --git a/lib/server/appcache.js b/lib/server/appcache.js index 4a43225e..290a04a7 100644 --- a/lib/server/appcache.js +++ b/lib/server/appcache.js @@ -113,7 +113,7 @@ } - function onWatch (pFileName){ + function onWatch (){ return function(pEvent, pFileName){ console.log(pEvent); console.log('file ' + pFileName + ' is changed'); From 02f9e2b64bd777c259efc5ea81bebe0c974e8d9d Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 30 Nov 2012 10:18:55 -0500 Subject: [PATCH 225/281] fixed bug with traveling in directories with Javascript dissabled --- ChangeLog | 3 +++ server.js | 30 ++++++++++++++++-------------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/ChangeLog b/ChangeLog index 737cdb50..9f97685f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -135,6 +135,9 @@ config.json only if it setted up. * Added url change on folder changing. +* Fixed bug with traveling in directories with +Javascript dissabled. + 2012.10.01, Version 0.1.7 diff --git a/server.js b/server.js index 8cb3202d..00cedb47 100644 --- a/server.js +++ b/server.js @@ -349,29 +349,31 @@ * длиннее */ - if(pathname.indexOf(lNoJS_s) !== lFS_s.length && pathname !== '/'){ - CloudServer.NoJS = false; - - }else - pathname = Util.removeStr(pathname, lNoJS_s); + if(pathname.indexOf(lNoJS_s) !== lFS_s.length && pathname !== '/') + CloudServer.NoJS = false; + else{ + CloudServer.NoJS = true; + pathname = Util.removeStr(pathname, lNoJS_s); + } /* убираем индекс файловой системы */ if(pathname.indexOf(lFS_s) === 0){ pathname = Util.removeStr(pathname, lFS_s); - /* if query json setted up - * load json data, no-js false. - */ - - if(lQuery === 'json') - CloudServer.NoJS = false; - + /* если посетитель только зашел на сайт * no-js будет пустым, как и fs. * Если в пути нету fs - посетитель только зашел на сайт * загружаем его полностью. */ - }else - CloudServer.NoJS = true; + } + + /* if query json setted up + * load json data, no-js false. + */ + + if(lQuery === 'json') + CloudServer.NoJS = false; + /* если в итоге путь пустой * делаем его корневым From 8903671fdb6cee487465f3a23e0dc7fb52870ab0 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 30 Nov 2012 10:23:24 -0500 Subject: [PATCH 226/281] moved to lib directory --- ChangeLog | 2 ++ cloudcmd.js | 2 +- index.html | 2 +- client.js => lib/client.js | 0 server.js => lib/server.js | 0 5 files changed, 4 insertions(+), 2 deletions(-) rename client.js => lib/client.js (100%) rename server.js => lib/server.js (100%) diff --git a/ChangeLog b/ChangeLog index 9f97685f..adbfd31b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -138,6 +138,8 @@ config.json only if it setted up. * Fixed bug with traveling in directories with Javascript dissabled. +* client.js and server.js moved to lib directory. + 2012.10.01, Version 0.1.7 diff --git a/cloudcmd.js b/cloudcmd.js index 7c7707ad..a5a96dd6 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -13,7 +13,7 @@ Util = main.util, update = main.update, - Server = main.require(DIR + 'server'), + Server = main.require(LIBDIR + 'server'), srv = Server.CloudServer, Config = main.config; diff --git a/index.html b/index.html index b0a2f6bb..e6e42b00 100644 --- a/index.html +++ b/index.html @@ -42,7 +42,7 @@
    - + diff --git a/client.js b/lib/client.js similarity index 100% rename from client.js rename to lib/client.js diff --git a/server.js b/lib/server.js similarity index 100% rename from server.js rename to lib/server.js From e69fe2f8d96c30596b9ec5f7a97440e50ba317bc Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 30 Nov 2012 17:28:38 +0200 Subject: [PATCH 227/281] Update cloudcmd.bat --- cloudcmd.bat | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cloudcmd.bat b/cloudcmd.bat index cff0a18f..4c94262d 100644 --- a/cloudcmd.bat +++ b/cloudcmd.bat @@ -1,7 +1,7 @@ :: ------------------------------------- -:: Changing charset back to 866, becouse -:: all win32 default commands, thet work -:: thrue cmd.exe showing out in unicode +:: Changing charset back to 866, because +:: all win32 default commands, that work +:: thru cmd.exe showing out in unicode :: charset. So Cloud Commander changes :: 866 charset to Unicode 65001 sometime :: when it's neaded. From d08d05d6d75079f8f51784b90b979974404c593f Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 3 Dec 2012 05:21:45 -0500 Subject: [PATCH 228/281] improved work with browsers history api --- ChangeLog | 2 ++ cloudcmd.js | 2 +- lib/client.js | 48 +++++++++++++++++++++++++++-------------------- lib/client/dom.js | 24 +++++++++++++++++++++++- lib/cloudfunc.js | 16 +++++++++------- 5 files changed, 63 insertions(+), 29 deletions(-) diff --git a/ChangeLog b/ChangeLog index adbfd31b..caa2fc9b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -140,6 +140,8 @@ Javascript dissabled. * client.js and server.js moved to lib directory. +* Improved work with browsers history api. + 2012.10.01, Version 0.1.7 diff --git a/cloudcmd.js b/cloudcmd.js index a5a96dd6..efe5ab2f 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -59,7 +59,7 @@ /* меняем title */ lData = lData.replace('Cloud Commander', - '' + CloudFunc.setTitle() + ''); + '' + CloudFunc.getTitle() + ''); if(!srv.Config.appcache) lData = Util.removeStr(lData, ' manifest="/cloudcmd.appcache"'); diff --git a/lib/client.js b/lib/client.js index 6cf7764e..342397be 100644 --- a/lib/client.js +++ b/lib/client.js @@ -3,7 +3,7 @@ * клиентский и серверный */ -var Util, DOM, CloudCommander = (function(){ +var Util, DOM, CloudFunc, CloudCommander = (function(){ "use strict"; /* Клиентский обьект, содержащий функциональную часть*/ @@ -62,7 +62,7 @@ var CloudClient = { var cloudcmd = CloudClient, /* глобальные переменные */ -CloudFunc, $, KeyBinding, + $, KeyBinding, /* short names used all the time functions */ getByClass, getById; @@ -149,7 +149,7 @@ CloudClient._loadDir = function(pLink,pNeedRefresh){ lHref = lHref.replace(lSubstr+'/',''); /* загружаем содержимое каталога */ - CloudClient._ajaxLoad(pLink, pNeedRefresh); + CloudClient._ajaxLoad(pLink, { refresh: pNeedRefresh }); /* получаем все элементы выделенной папки*/ /* при этом, если мы нажали обновить @@ -413,16 +413,18 @@ function baseInit(pCallBack){ Util.exec(lFunc); }; } - /* меняем title - * если js включен - имена папок отображать необязательно... - * а может и обязательно при переходе, можно будет это сделать - */ - var lTitle = DOM.getByTag('title'); - if(lTitle.length > 0) - lTitle[0].textContent = 'Cloud Commander'; - + /* загружаем общие функции для клиента и сервера */ DOM.jsload(cloudcmd.LIBDIR + 'cloudfunc.js',function(){ + DOM.addListener("popstate", function(pEvent) { + var lPath = pEvent.state; + + if(lPath) + cloudcmd._ajaxLoad(lPath, {nohistory: true}); + + return true; + }); + /* берём из обьекта window общий с сервером функционал */ CloudFunc = window.CloudFunc; @@ -623,16 +625,19 @@ CloudClient._changeLinks = function(pPanelID){ * Функция загружает json-данные о Файловой Системе * через ajax-запрос. * @param path - каталог для чтения - * @param pNeedRefresh - необходимость обновить данные о каталоге + * @param pOptions + * { refresh, nohistory } - необходимость обновить данные о каталоге */ -CloudClient._ajaxLoad = function(pFullPath, pNeedRefresh){ - /* Отображаем красивые пути */ - +CloudClient._ajaxLoad = function(pFullPath, pOptions){ + if(!pOptions) + pOptions = {}; + /* Отображаем красивые пути */ /* added supporting of russian language */ pFullPath = decodeURI(pFullPath); var lPath = pFullPath, - lFSPath, + lFSPath = pFullPath, + lFS_s = CloudFunc.FS, lNoJS_s = CloudFunc.NOJS; /* @@ -652,6 +657,12 @@ CloudClient._ajaxLoad = function(pFullPath, pNeedRefresh){ console.log ('reading dir: "' + lPath + '";'); + + if(!pOptions.nohistory) + DOM.setHistory(pFullPath, null, pFullPath); + + DOM.setTitle( CloudFunc.getTitle(lPath) ); + /* если доступен localStorage и * в нём есть нужная нам директория - * читаем данные с него и @@ -666,7 +677,7 @@ CloudClient._ajaxLoad = function(pFullPath, pNeedRefresh){ var lPanel = DOM.getPanel().id, lError; - if(pNeedRefresh === undefined && lPanel){ + if(!pOptions.refresh && lPanel){ var lJSON = DOM.Cache.get(lPath); if (lJSON){ @@ -682,7 +693,6 @@ CloudClient._ajaxLoad = function(pFullPath, pNeedRefresh){ CloudClient._createFileTable(lPanel, lJSON); CloudClient._changeLinks(lPanel); - DOM.setHistory(lPath, 'Cloud Commander', pFullPath); return; } @@ -716,8 +726,6 @@ CloudClient._ajaxLoad = function(pFullPath, pNeedRefresh){ */ if(lJSON_s.length < 50000 ) DOM.Cache.set(lPath, lJSON_s); - - DOM.setHistory(lPath, 'Cloud Commander', pFullPath); } }); }; diff --git a/lib/client/dom.js b/lib/client/dom.js index 0b2adae2..9f884d85 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -33,6 +33,7 @@ var CloudCommander, Util, DOM, CloudFunc; /* private members */ var XMLHTTP, + Title, LoadingImage, ErrorImage, @@ -101,7 +102,7 @@ var CloudCommander, Util, DOM, CloudFunc; DOM.addListener = function(pType, pListener, pUseCapture, pElement){ var lRet = this; - (pElement || document).addEventListener( + (pElement || window).addEventListener( pType, pListener, pUseCapture || false @@ -810,6 +811,27 @@ var CloudCommander, Util, DOM, CloudFunc; return CloudCommander.KeysPanel[pKey].onclick = pFunc; }; + /** + * set title with pName + * create title element + * if it absent + * @param pName + */ + + DOM.setTitle = function(pName){ + if(!Title) + Title = DOM.getByTag('title')[0] || + DOM.anyload({ + name:'title', + parentElement: document.head, + innerHTML: pName + }); + if(Title) + Title.textContent = pName; + + return Title; + }; + /** * current file check * diff --git a/lib/cloudfunc.js b/lib/cloudfunc.js index 6daf20e7..8c9454eb 100644 --- a/lib/cloudfunc.js +++ b/lib/cloudfunc.js @@ -44,13 +44,15 @@ var CloudFunc, exports; else return pPath; }; - /* Функция возвращает заголовок веб страницы */ - CloudFunc.setTitle = function(){ - - return CloudFunc.Path==='' ? CloudFunc.NAME: - CloudFunc.Path + - ' - ' + - CloudFunc.NAME; + /** Функция возвращает заголовок веб страницы + * @pPath + */ + CloudFunc.getTitle = function(pPath){ + if(!CloudFunc.Path) + CloudFunc.Path = '/'; + + return CloudFunc.NAME + ' - ' + (pPath || CloudFunc.Path); + }; /** * Функция переводит права из цыфрового вида в символьный From ba23f18b01f7fc890a54a91d0d0453844c4b8567 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 3 Dec 2012 07:57:25 -0500 Subject: [PATCH 229/281] fixed bug with setting path of index.html --- ChangeLog | 1 + config.json | 2 +- lib/server.js | 17 +++++++++-------- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/ChangeLog b/ChangeLog index caa2fc9b..7876290a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -142,6 +142,7 @@ Javascript dissabled. * Improved work with browsers history api. +* Fixed bug with setting path of index.html. 2012.10.01, Version 0.1.7 diff --git a/config.json b/config.json index a5664801..f08472da 100644 --- a/config.json +++ b/config.json @@ -4,7 +4,7 @@ "minification" : { "js" : false, "css" : true, - "html" : true, + "html" : false, "img" : true }, "oauth_client_id" : "891c251b925e4e967fa9", diff --git a/lib/server.js b/lib/server.js index 00cedb47..1e03aad2 100644 --- a/lib/server.js +++ b/lib/server.js @@ -1,10 +1,13 @@ (function(){ "use strict"; - /* Обьект содержащий все функции и переменные + var main = global.cloudcmd.main, + + /* + * Обьект содержащий все функции и переменные * серверной части Cloud Commander'а - */ - var CloudServer = { + */ + CloudServer = { /* base configuration */ Config : { server : true, @@ -50,20 +53,18 @@ * Поддержка браузером JS */ NoJS : true, /* Поддержка gzip-сжатия браузером */ - Gzip : undefined, + Gzip : false, /* server varible */ - Server :{}, + Server : {}, /* КОНСТАНТЫ */ - INDEX : __dirname + '/' + 'index.html' + INDEX : main.DIR + '/' + 'index.html' }, DirPath = '/', OK = 200, - main = global.cloudcmd.main, - DIR = main.Dir, LIBDIR = main.LIBDIR, SRVDIR = main.SRVDIR, From 1afd2970b6e0bcb8ee622667f92307beb6b842d4 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 3 Dec 2012 08:09:22 -0500 Subject: [PATCH 230/281] added dropbox module --- ChangeLog | 2 ++ lib/client/storage/_dropbox.js | 53 ++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 lib/client/storage/_dropbox.js diff --git a/ChangeLog b/ChangeLog index 7876290a..a585cd97 100644 --- a/ChangeLog +++ b/ChangeLog @@ -144,6 +144,8 @@ Javascript dissabled. * Fixed bug with setting path of index.html. +* Added dropbox module. + 2012.10.01, Version 0.1.7 * Changed name of menu files, fixed npm and jitsu diff --git a/lib/client/storage/_dropbox.js b/lib/client/storage/_dropbox.js new file mode 100644 index 00000000..337937a7 --- /dev/null +++ b/lib/client/storage/_dropbox.js @@ -0,0 +1,53 @@ +var CloudCommander, DOM, Dropbox; //o7d6llji052vijk +/* module for work with github */ + +(function(){ + "use strict"; + + var cloudcmd = CloudCommander, + CLIENT_ID, + DropBoxStore = {}, + options = { + linkType: "preview", + // "preview" (default) is a preview link to the document for sharing, + // "directLink" is an expiring link to the contents of the file for downloading + success: function(files) { + console.log("Here's the file link:" + files[0].link); + }, + cancel: function() { + console.log('Chose something'); + } + }; + + cloudcmd.Storage = {}; + + /* PRIVATE FUNCTIONS */ + + /** + * function loads dropbox.js + */ + function load(){ + console.time('dropbox load'); + + var lElement = DOM.anyload({ + src : 'https://www.dropbox.com/static/api/1/dropbox.js', + not_append : true, + id : 'dropboxjs', + func : DropBoxStore.choose + + }); + + lElement.setAttribute('data-app-key', 'o7d6llji052vijk'); + document.body.appendChild(lElement); + } + + DropBoxStore.choose = function(){ + Dropbox.choose(options); + }; + + cloudcmd.Storage.Keys = function(){ + load(); + }; + + cloudcmd.Storage.DropBoxStore = DropBoxStore; +})(); From 28749bff536ecce1e1b7513f648fc17735fc0ee5 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 3 Dec 2012 08:44:39 -0500 Subject: [PATCH 231/281] renamed config options ouath_client_id and oauth_client_secre to github_id and github_secret. Added dropbox_id option. --- README.md | 7 ++++--- config.json | 5 +++-- lib/client/storage/_github.js | 10 +++++----- lib/server/auth.js | 9 +++++---- lib/server/rest.js | 4 ++-- 5 files changed, 19 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 3790bbe8..70563367 100644 --- a/README.md +++ b/README.md @@ -81,8 +81,9 @@ All main configuration could be done thrue config.json. "html" : true, "img" : false }, - "oauth_client_id" : "891c251b925e4e967fa9", /* github app id */ - "oauth_client_secret" : "afe9bed1e810c5dc44c4c2a953fc6efb1e5b0545", /* github app secret */ + "github_id" : "891c251b925e4e967fa9", /* github app id */ + "github_secret" : "afe9bed1e810c5dc44c4c2a953fc6efb1e5b0545", /* github app secret */ + "dropbox_id" : "o7d6llji052vijk", "show_keys_panel" : true, /* show classic panel with buttons of keys */ "server" : true, /* server mode or testing mode */ "logs" : false, /* logs or console ouput */ @@ -97,7 +98,7 @@ Authorization Thru openID Cloud Commander could authorize clients on GitHub. All things that should be done is must be added **id** and **secret** of application from github settings page and added to **config.json** or env varibles with names: -*oauth_client_id* and *oauth_client_secret* that is more secure way. +*github_id* and *github_secret* that is more secure way. Starting diff --git a/config.json b/config.json index f08472da..205537c8 100644 --- a/config.json +++ b/config.json @@ -7,8 +7,9 @@ "html" : false, "img" : true }, - "oauth_client_id" : "891c251b925e4e967fa9", - "oauth_client_secret" : "afe9bed1e810c5dc44c4c2a953fc6efb1e5b0545", + "github_id" : "891c251b925e4e967fa9", + "github_secret" : "afe9bed1e810c5dc44c4c2a953fc6efb1e5b0545", + "dropbox_id" : "o7d6llji052vijk", "show_keys_panel" : true, "server" : true, "logs" : false, diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index ffdf9bdd..3f79e549 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -8,8 +8,8 @@ var CloudCommander, Util, DOM, $, Github, cb; APIURL = '/api/v1', AuthURL = APIURL + '/auth', - ClientIdURL = APIURL + '/client_id', - CLIENT_ID, + GitHubIdURL = APIURL + '/github_id', + GitHub_ID, Cache = DOM.Cache, GithubLocal, User, @@ -46,9 +46,9 @@ var CloudCommander, Util, DOM, $, Github, cb; function setConfig(pCallBack){ DOM.ajax({ - url : ClientIdURL, + url : GitHubIdURL, success : function(pData){ - CLIENT_ID = pData; + GitHub_ID = pData; Util.exec(pCallBack); } }); @@ -113,7 +113,7 @@ var CloudCommander, Util, DOM, $, Github, cb; //window.open('welcome.html', 'welcome','width=300,height=200,menubar=yes,status=yes')"> window.location = 'https://github.com/login/oauth/authorize?client_id=' + - CLIENT_ID + '&&scope=repo,user,gist'; + GitHub_ID + '&&scope=repo,user,gist'; } } diff --git a/lib/server/auth.js b/lib/server/auth.js index 629cd629..53a8e25c 100644 --- a/lib/server/auth.js +++ b/lib/server/auth.js @@ -36,18 +36,19 @@ }; function authenticate(pCode, pCallBack) { - var lId = Config.oauth_client_id, - lSecret = Config.oauth_client_secret, + var lId = Config.github_id, + lSecret = Config.github_secret, lEnv = process.env, - lClientId = lEnv.oauth_client_id || lId, - lClientSecret = lEnv.oauth_client_secret || lSecret; + lClientId = lEnv.github_id || lId, + lClientSecret = lEnv.github_secret || lSecret; var data = qs.stringify({ client_id : lClientId, client_secret : lClientSecret, code : pCode }); + console.log(data); GithubAuth.headers = { 'content-length': data.length }; diff --git a/lib/server/rest.js b/lib/server/rest.js index b66d77f9..866c4205 100644 --- a/lib/server/rest.js +++ b/lib/server/rest.js @@ -101,11 +101,11 @@ lResult = {info: 'Cloud Commander API v1'}; break; - case 'client_id': + case 'github_id': var lEnv = process.env, lConfig = main.config; - lResult = lEnv.oauth_client_id || lConfig.oauth_client_id; + lResult = lEnv.github_id || lConfig.github_id; break; case 'kill': From 6fe79d912a3e0ee988485b256bc73c680caf4068 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 3 Dec 2012 08:45:02 -0500 Subject: [PATCH 232/281] minor changes --- ChangeLog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ChangeLog b/ChangeLog index a585cd97..3dbbb74a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -146,6 +146,11 @@ Javascript dissabled. * Added dropbox module. +* Renamed config options ouath_client_id and +oauth_client_secre to github_id and github_secret. +Added dropbox_id option. + + 2012.10.01, Version 0.1.7 * Changed name of menu files, fixed npm and jitsu From 49763d1d06b08ba62ecc630c8d44e13426aa2975 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 3 Dec 2012 08:57:35 -0500 Subject: [PATCH 233/281] fixed bug in github show function --- ChangeLog | 2 ++ lib/client/storage/github/github.js | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 3dbbb74a..10678c8b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -150,6 +150,8 @@ Javascript dissabled. oauth_client_secre to github_id and github_secret. Added dropbox_id option. +* Fixed bug in github show function. + 2012.10.01, Version 0.1.7 diff --git a/lib/client/storage/github/github.js b/lib/client/storage/github/github.js index f01c44f3..e7ba98d0 100644 --- a/lib/client/storage/github/github.js +++ b/lib/client/storage/github/github.js @@ -82,7 +82,7 @@ this.show = function(username, cb) { var command = username ? "/users/"+username : "/user"; - _request("GET", "/users/"+username, null, function(err, res) { + _request("GET", command, null, function(err, res) { cb(err, res); }); }; From ea8b13cd952ac1f147cbbf1f4b2fd8ba506d7df2 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 3 Dec 2012 09:21:44 -0500 Subject: [PATCH 234/281] changed chose type to direct links --- lib/client/storage/_dropbox.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/client/storage/_dropbox.js b/lib/client/storage/_dropbox.js index 337937a7..5fb32ded 100644 --- a/lib/client/storage/_dropbox.js +++ b/lib/client/storage/_dropbox.js @@ -8,9 +8,7 @@ var CloudCommander, DOM, Dropbox; //o7d6llji052vijk CLIENT_ID, DropBoxStore = {}, options = { - linkType: "preview", - // "preview" (default) is a preview link to the document for sharing, - // "directLink" is an expiring link to the contents of the file for downloading + linkType: "direct", success: function(files) { console.log("Here's the file link:" + files[0].link); }, From 555912574f4084263682bb0dbb7e0f56f8f5b3b2 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 3 Dec 2012 09:22:09 -0500 Subject: [PATCH 235/281] minor changes --- lib/client/storage/_dropbox.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/client/storage/_dropbox.js b/lib/client/storage/_dropbox.js index 5fb32ded..3d4f1490 100644 --- a/lib/client/storage/_dropbox.js +++ b/lib/client/storage/_dropbox.js @@ -6,8 +6,8 @@ var CloudCommander, DOM, Dropbox; //o7d6llji052vijk var cloudcmd = CloudCommander, CLIENT_ID, - DropBoxStore = {}, - options = { + DropBoxStore = {}, + options = { linkType: "direct", success: function(files) { console.log("Here's the file link:" + files[0].link); From d23e26530c7524e5fc8598e9188e0f590aa472e8 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 4 Dec 2012 03:07:11 -0500 Subject: [PATCH 236/281] added logo --- img/logo/cloudcmd.cdr | Bin 0 -> 242514 bytes img/logo/cloudcmd.png | Bin 0 -> 84078 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 img/logo/cloudcmd.cdr create mode 100644 img/logo/cloudcmd.png diff --git a/img/logo/cloudcmd.cdr b/img/logo/cloudcmd.cdr new file mode 100644 index 0000000000000000000000000000000000000000..764bb74c44d441bd5f2d31978ea655824872ee47 GIT binary patch literal 242514 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

    PFY`kfqIW&V+$-oL`iooH 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

    }Y3yI7RUH3GUhQ@Uvs34DqT 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 Hk_-$0^BS=8 literal 0 HcmV?d00001 diff --git a/img/logo/cloudcmd.png b/img/logo/cloudcmd.png new file mode 100644 index 0000000000000000000000000000000000000000..8e4d1d3b07fdda90034cbe132474077bf814faf7 GIT binary patch literal 84078 zcmV)nK%KvdP)X50_ulP;Tg+o`kd1({MFa#vM4Y%#5fwpD zR74c{tAMB=Lj^_Ldm$*OAP$zwRz&vR>+Tpg-}onK)23O2)1MT&!c;{$)MqtX{O|s?GCGs%@qt59uKDZx39$L|F=($Y zIUZ8~FSzlqO_e1H^1U7pjw2SGE&xS&#Y(z`sxXvMtSA=_-)`J{8Y?K@>mxA3B1zWw z<{^b*#Y*Vv-?tpd>C~V~B08Oq;&j)lUHibuM<1)yu+b&p^?JeOa*+@f6@`j*6xl9a zu>lSrI|29IcCF1*f|o*3n2IVKH&#~s=j-+y%SQ^3kcK2s03xf|`1O_hRCFnF+cWRO zhyS~SD6CGR1O)~8(6v(s_;cNcGx>S>ke8PSZnqoq^Yb7h;}m!h0t*TXz>PwFKKeah zxu$Ekp0H`_u88L?;tErhg%uTj-QW=|`VD^gH(dM(yMvA@s<8GWX9MhbQ@bB43k$wy z)~E9!`)n3;yM7>y8aV=dUaV{%q^GAtK2i`?3@);GiYFzc9MarvLtn zX#OIqFjiS8)a-fpa86kY%E*INg;^EL`rBqh-c|Pzo}>5Bcp7Ygf@KCG;`SC{SFnCn)GAI`4>l#FAgVR2K1SwytD#$W)A$3Tk*?|*%~ z3q(78F!9a4HP-%f$k(b#Wi1+NsxX0ykA`q6j)*A?B^1XMg+lDaUv?a!q>Y6wj#`ma z@59H}kNXn78b81gA4Vp{pwznFVDcFeg`ouCeBngLB&JpP1J{J?>!rw$7_>N5jp5^aW-SCbI@b^sAzd)!TpW{vPk-dvsJ|VP6bQ!)I&v>S3S-gbjE;GZC`-+l%pr?K7@KgB%)P%C z+&c4nB@RXjJAYaS3Gui%dP#A_XsdEbOG6Q+@scvip_7e^RzwJ$Irp@!iP4sil;*a} zZzK?=p=gFjxGtSZO>N#$zm z%ApDZeP2QfB)@}ljhpb67d3~JEj~U{xiT?7tPo|Q1u*{3S48Ej)oO8U->!A4(oCEd zF2X2(MaPv(|B)K0Y$8EblyWRV#?h^iQmG-zzpN|Chn0hs;#HOBRfEjzD*>Y{UqxC* zof;s!jkO|Q)J2G**NZBmVekC7?CC4(;=VYqsx(9G8Lq;##&yTZ^ULQ85N0?=f6BGJ z96Wn7Op5SXqdml0~Q^eXB0DaA0y%^=xpR0@wMSvH#eK~zLB zD7cX>4E=Eig)zMh&ZzQLXEu6%!QD7Ay{AE~o>3y5>Gk3Axf@T>hvy>ONXF`vn77Ei zUG{Baq0BkZ%~2@)@9X7cA1iO<)vcjz?`Kt%dDZk##rPP* zR?V=cER-Ivk>C7PL^Ph^-)}iGy>|l)2x8cp6vj53qbk?=t>)8Tw8&-Ja-ugvA3$v# z%}6x8O`idsr=xSB%aAc}EZc*Qy(GiPAV`b>s5G<1kt0KAW z)#1>3s1U(bTg^$(Gf0=7!?kQngHmW`a8a^`}h~hxgDq7hPZWW-Z~vTe<^tq zqZ8D0#U`aK$jLl$6(}gn*A&L|jK-u4oqZtJ<1@SsiV{76+?xdM*ot6Ls5A6c$ShF0 zr%Cyj>9s!XjXNa$#Sn#pf;OT=bWqmmg%kODMn&mR;?!RhaOvRh6`}Lmw|;B&=?#}) z5ap>&Cm%(aX03Z`wQ}V$=+gO!;V{#i^l=H~-=q=>j_-q*3f0sE`bdnjwpHFZMGoZ` zs#u4tH+|?g0sTcvDk}JNiPI4%I#t06!(hro!NV&oHjhyj|2Ek3#sqR4W`)kws>CYS z-=t4{4L(bBnu-ylP+Z9xGOZc_dR#hpT2)3?rNp6Yj4Dj#6-J6=p1?F<6UshBwrPe0 zgHASqW}dN52a6Uy9l|1Mipw6ISYpyDeU^LTa4%Iy1yo_wVt3_o?ZH5M9E8_`@%K>C z>94r+#wyE4R_QPmNdY>sw3_sN^A-2D#JVB6ViiV$>$xl5J@?eoyF_o6KNn6jBouU7 zDU?NLpy{wfr;;}sCk7S)lM|a>CQDz+&;Asl>@Gv%$h|(zj*4V6Bp6gsCD00iD%6^| z!$T(%mqQJj!aPjyTK76wA;YI3!NE2~B7{zV#hoW^wz!9bPUe^naWE(h8)vu=sG!P( zcGvHHa^+XReOr;Z+Kk3zNF<|B9-WHy&Lu1ebXqCYqL9sXT zs!wb;2r2CyR$=&7kB>QEmFZ>81u;7L%<|}zWhP~Uc<8Z-hs3A|8=Y8=kGWz#n@?6@ z`KrQl)a^Ovsr_DeN>v#G5UM{*g#u&W1%XahDVoB;s8F7`O?((4I!QKi&P)dVo2I5P zd>KX6BhZ~(S>9cRT3!&_N8$AUq+7q=MUF^`wa-!ikMhSL(V&S3_6y4CEG>vde zT>UyeYw)|bFXx@#KX8dRvVX;G}-s51*2wT(xH&KE^Crs>h1eDU}R<}OZtf0jb z7fCwuhE%6WY4>kO39C{%QoopQ|zPyj@Oo9(S2fq&n!V0DsH` zF~aiHfMFYlLad6o=)I$tzY0=7_HKz0+_FKNEKrnn1{8!DiX#@kA9B@-fEsQr$|xaX zWnrb^?~}yftv;y(<~Y(*iBLBsa@mT!sHFod2P-NAg-lQsrlE@+u^7;oZ`I_Wn&weP zm4y|D6&B5GqfvtH2h|8h%6laOd5nlueWlw^a(?y~peP0_C`%0nbn)XW0v;-$YMG*q zRT^!7QpK?fi(NO?m*ZAo~YF`f56wzOZ!ISx@!8is>jIw-l*Vn~&R^O7eT!4ve@ z3iX2~MjS1kv;x2nF(C|8WvPKSxty{)cL7TLA~g*-I6Uzw4<8u*T|`?G)h2cLRPuRO zfS7x!!dOM+sMkeyxRZtA@WZ0Hz$=UfC$l9AbDTScTNAW*Q|1CSq|;ES2nw2UO}1an zFs+=}TAig3zJ{mDq2>ea+Oi#m81{fVMsaQ?L2Lt_7E7%`4;Bhzf(&M*QH5b*-p(Y! z#{`kWw2o0VZ9I+SGUwg?#<@mp*tyDaB#~+bK{IdY{Qxt{sNqitW^_40TpW3&vEr{tQiRboP$dQB&_JcgJ#s1M0aFVxO5~Lp2*R+i$`UD!^p-}# z*Rn+Td^1WE3gv8$)g=J~s|*rX_D63|Gze8tL(S8P4d5qAX- zA3hB*?X3H868tr12t2wd4!&Eu&G*plop6&X=&HUjr6^_*pxz;_#sBUi4Ns+ui zdj{Mx_#v|Y`<4T^T?d^SREfh%ORrMpkb2Q;_5inAae_Z{<}{o-bqZo*W68_`ct!zy9?u@|z{8J043Mt`2n)eWWl-9nXq;Bat)2Mhqi-fzT64Vbo`c($UQ z{|!)P4QO=Z1i2{5KavjcSO)}AQL$&Z7z^Lpil&K4jV*RW;Tf3Q9NN+^>U4Ij0qX$Csv>pHR_5--q3&z`( zm$x464U1KOa3_OEEZG0it@>}%sTJn@u`8bzZ;@Len}&Ozo}4!1naO#_4;_G^*K~+c zeS-p;5h|ii?`11i*+7e5FoSqSLd^^}7Z35@R%Zt&Q1|ma;^M6wi)U9!tdI^LuHKV- zTk9GLY9E~2AIc)>@E$wp?Vroj+Ao*^ya$hELVEcmL!@-30rtLvyj;jvo!xO!#2&V| zD2n}@{Lid{i#S5%*?TXmRTgyLAWOqHT;FY$t77w*DXW4RynEZ!Wl*!xwIIn(&GB89 zza{&amQNo37t(9Dg|Z1TkeCpy%?dz^A?pGydMQ?Epq0)WD|cl*(5@DKTar4Zf>2?R zazl=}^NYW?lE_^WhY z`fW$XW1Z_@gn6mEUSTLJn(~IoXHYqo)O=G%c1}5}UpDXT&IURY09Mo=J61u{Yi~Eq z2*4L_9$A45B_ICIj~UYk^(X^e7kF68;%1L?X*H67A80~Vb#YT9cnt~^6-ByL=9xeb zYTFpmVwg!kbl>SCrx@VN7#cH?-s@y@}-7aJGbjlcf{-^||x2}zaJ`6py`tMC+^ zz$hHYb^e92NPGN;t;Z(!tdIGNeo`|lOKsZ`v^0Bj!x`$SDsozH_3E#k(donZc-7s_ zpT@HQP-iPXA#6ewW%(e>a~N)Z{6#Vg0Dj}!GV(S5?1W}r;I8IZpx>0^k7q2H183^6 zsJ!*f#fLw>?+2qWwHG-?pSa&Ml!&%`>glr;31j!?bM`hI^kHN_tui}Xz>7M?| zR0$G!+fEe~SV0niHkCr%0i9vTX^-~YaGliwz@SVPl~LuY;M3HRouG>%OP@taQ2ht$ zsLmRIB2>E2o6hv=36fP8IayUEZS<1a03^^Rb;PK|`&enoQ4$=_^%^rMNQcQQR!}-< z`m9h1N>L=^xlr&r*|r_v9s{NiDrhHl$$kn2{6tArmP1RV?z%^v*=dnb8qq#wh5#m0 zSi$Fr0UW-Qx^9S#sqY-Lm>nU12_A zT|~k`U1UDc7hk2@tuj&;8JtI5i&=-F|@0A zEKsTL6J_B`S!8LR{=pb=G~!4)rrV)1&~!+qxQzD(I)@MFW6d;WQMAjU_8joY3Iz=` z(RQdbrXylb=|WVhmuSPI@E?Qci)Zg9UtanplJQLxu0y&B4K}C*PMJI^`63>mL%JmZ zfEflwm8FesN}79bzXM}derq=3)Z-#>M)!+`2s)$tGl$e$49Y5CL(Hb1a?V}SJr)=# zN=I}%REpAISMk`PQi;pH;$iegl*Rg6lR>L#^PjyFDeH4qS%u|<&Z3ev5CP2bSn{7u z5%=eW?yuru#i_caQ2#_a1Ujes#CCV?eRAb{5TJzWfNqCMGaA4$9(o+C1G+zRh|L~t z?$E9X>d8;MD3HSLLkiPp|5#5Gm}95Ooo< z_(f#6Qz8IZ_Gv}Gb?+%z9~w>+#&-VEqKJ8m9b!#{AZ*kxv%nP{a^@o~;W(VzS30QO zxBs78!RVQx6+_lNn4+9dlc)O2R}9ASoG(u3R?g;F>?)l~FA!&R>c)b%WbGn;ssrk* z37{*CcM~Sl9ptCgeD?6*kIF|bYeg$oMLhAZPcdib0OxMUcK&IC2T3pr(@+IQ_uz!6 z6T6>6N{cyo{}(gd$?1iYqaqe5_bjCRJRPsYLtXA@$DggoK2?~=W1l)d5?|`{SvQdR zAx^ix>G_Oq50@$A{1lovP9M#!^4fKGg6{Q-5k`xkwX(3N3gaD#D6&}hG~!2HX7y*I zP$26BAx?Xw&~jA>qPi@z{?xQ6(zzf=A+`O;%9s?q?=Cee4iQ=Yb%6WjTAeNH}M6);*0jkEpXboz_6}p-%W`;o~R} zFIRWjO3xg8@T(G|IQ50%v}46!1z~0Js0!`#z51asU-g$}U4Lm&WWDprS88DVr=3}c zA4dt)95qjMd-%mOt#2w)cbF>EdarV1XM6zqByE_;sj?_f(e|-vtX3%A`5qNz{2UNu zj27q$GbjrC5kk3N}qvlJdh#*@^4F83SgnWgX3_!nA1& zKq-Ea8FIw=NV6!j^UP;`r$tWYS;@c78j6CIlR0&3!82&%;*2wM0u;}D-bs_4nN2i> z4CPzaM4(fzSdRXSgjYUQT7jBLI;|dyDAp^VRTS%J&Ic>^-{Z{VS$}EP%%K$$RT%3p z%}-;H%lvLTt~8P!`Sh(8s5TngC>MF-^B(zj{?e?n0F12*JAY}`8=qAc4Xl#)k62c zFLB=YB}wYmsx$10o2N{#StSK#zcAnu^fERVcbd*8DfW_BlB9l}`rBLg8ePZXa1jTO zP6H;9;jCxQ<-xdD7yS!j+AI|dxuC&iQAKEVY~?Cd7gW6DhHefSgUJOgyN}e|7nDK! z%BcLsUrvOo!zaO#6}x=*-q?-~d?1uxQYKRN<=3n!LqFfvS}i||N8Ue z6P|pe1}MET8sGv28oH-JbaZsDgv7)fVxnWJHf`QKzIB^6N%iVCAQOE!91arZ{j5v| z_3Ws7N_-W4UV%#hD6MnjtMz;Fw+9tS`uk)ViXGm-x)w#b9K>7ikAL>SWld^WK7Y*Z zJ>Z#l7a4$=pPLJt*8K%Lw{9u7YW2Ev6a=!mJ$LS$GKgB1$uS*{$~L6%{^sqwz~k|t z;3iuDO=g=!t;R@#bUK!cO#@+@0&m%l4v|jwgJn7%Rcwf31YgT{>u>aC;O` z^r&zUzCTc2Ii`R?0CKyPEl!U|jfZY=5P>T?u^g01yqcut%(?7vR9YlbqQS*^k7_-L zu?dNjXDr!xFTRF2KdOMy3@#Q>0_I``|GvJ~eb8c8V51&Ccx(XNIO$8$|I0Grgpa=$ zJ`>OD>-zIi=-sxSA)Y;lPC}y_#+PX330F!axGX?L`t)Zm-+2TZ)cH&oB6TJp`(Or? z@i(IhBuT-&-)HZ+3Wh#%JKQt*ebc}=`V5{epZjnHJoWl~SpD88?R>8hvjIlTrmfB? z$Trh|yg;PHfs2`Ak$&SKplZ^o$Id&Sdo6+WE2Uz`PV8@rf2i?h3bv)Iw?Ws|Mt0$4 z(%M+)blpH0IPqSMF_k#&({umhZ$nJ91LkKqAUetkF;Nbf`(+=pALWuIw0#ui`!O)y z#(y1b+Mq@2ubzMVi>fSBi&*;9@uTqAt$lZ%Ja(iJn7AO zLHs+r>NYEEXBO)=&i+RCt$_KL<5K8KMvqv_y|qukCXO-f(nI(0Oci9(1q?>Xqj(d^ z9u4UD94Mnm$l=H-bFx}=yi!(BWsIE6tO)k@WkO1MnDxUSwbAnoQ^LCQ^Wf&T^@^U#n8HrscEAio)-PKE4clF5h|g3l zGW#DqISM*?p8q*;wqVabaG$H24GNlm#n0f-)=%?rc*N z53nXfASkniJ1cRqg9~)SogMNOGwi!9JM)-15vllN3cfV^0~34L{h41Oz0MWr#pC`c z8I+mwWMhy|!-~&`!ZnXR4yozYEYtJ-j0sSyMmK2H@fI?LAz>~Dp2|>0U{a?HLr9Yy z_%|;MeBljkIz-#_M!>!l#r@{0UBo81?{YGc9R(R3eGyW%S*+k<=l%vASaGq*X>amX zp|&px00Z1{FjWOQacswC_<8#CP_^Dw>i0xiU~%#oTUb(lt6IMs{Qdn#bnib1Y&`gs zZMkxt$fSIdTJ`0-bvui)l}v3h^ezjK8VcNjvvfH2rBH9`!Sv-jGjXmT>QI|bo}~dT zSSBm(WeKS9PFaf#yuW_V?^m#MM6o4MQ3_A@VEmoc8Uo9g4_ynDYP1K3Gui|K8miRk zs!l}cW!%jqGXI_yF*#2Gr=I7_vwDo}C|R|$ux&-Af0A`O%b`|}c>hSnikf>?Fx7ad z_GKWzobvn5jFsPhocZM|PuBo_0j8i|9W`> zRIb^P0LbTK3Z6(k1VFMj+a(5&M8V`^7>vv*=T#oeTuUNQkMY!^c#1xarHIW1Dsw64 zGi%pWz$5s&AUdY!tbUI)Fk4=8OA-p1W3H%!7sjLGc4c;UJ3A>Ba0yz6njzm}=%n%N zv|m{46mDuUamR=Dtb{7HI|G?=LZb9z28&YVDO;b9IVZZF&I9CA0Y%P9#_P~EZajM+ zv8}wYY4>7ifA45*&@i#0nHjq*^9thM>2HSPLIUQqOSYeQwp(4i>LOM*2AHJ!m)N!} zg9evij1<3L3x%$(gruqkXES%8{XbFI21aLbI@6cQ1tIOS-iB6_l2NW&G_``j_&f7k z=NCVRoo~+6#%KxcC=NUlWugdFkNE{k`di)g1euMMPq$r5voP#S_P1bw`M;p!_W%GO z07*naROFA_j?BKN$JA2?cH{Np^VQ4Tsx2l2m@T-twGj-X|1Wvwv&{JF4Gi}OmvQC9 zx~B&m&OWdWe~l%rM1Gwb;k^NtNRntrjQWcXAPSTNyMKi-@61zLg&=7&XSifK&6}jz zCN9+wr==Q)Mc;f5OTPFP>a^%*Sv}(EemAZC23mJ(ZCRa*@~ampvp(oGB2XCP;Phgl zC*b!jo_FV)Ka70q_D*Hbo;>;ldzlr$gy7<*8&EW5rpFERSH1I)jhC~zn5>HoQ!8L@Tm1zL`gj%48QWxOIP5vCj7(ss@RNa=u}Qs_ z%)2VH^K?m2uj_SC|H|vM{NXspU*8ehUj3AT$r_J(L0cSvO`{caDCYvRGm9FScfR}E z-LLg&@HN`~i;YtRFn(%g7F4VsldJY?>yBcDYOOdBC?pu#6=c}(djP;>&x!5@_1gTJ zm7!se8=(%2gyT6L1)Qn^9g+?fdapKRAoHU1>-6i5=M+HR_rWjP?0g?Tbq~bHRfRg9 z_O{h|u=SoX|q&}8g~I{SqP5LPGQ0F&k+(C3<3<__mIK974oFplncsv3S0ehMzO zr<@qrYzrsY3>%eK#K3gMK!GPX#?+N(tonI2<$p6D>&s8D`Dr@#1IXOE24d^B(#~6_ zOKfLG5|~(c+bS4f_W5wb>+^4E{1ce{+zsI3J2dG;BC&Sk(F?2hxY&VdlPZ8qpUdoHx2i+HxVh5>YItzbYEhePC9j0`9$P)vth~^H z3C#830Mqh!mNkRW2Y+vPkX+3y#;Xdcmg-$qQUur?icLc1l}r_ z7%~)4hW#kud;lh^+;D(N;wHypa26LZL2z%dE%Qo!hQuRqVM3y_myiWS=(-r9G-$`s zZ{Blt#<*3Bl+&~no6#(czy=`a129eLvU{#1>Kh7-HP2UIGS~B6S9sq|!Nq-O(}%y} z{p9hN_dER5%6T+$yPjV@0s6IOVA8lE1x)fh%jcJv7h!sKaRC!RP%U!%cKQ&2%P{E` z3odqYCR3>NLc1IGKDqJ>C1SoWM4SMcIre-oz|`X|9$-d_Ap(lqCS8REOqwp^Zy_~+ z_*v8p;NmAZ#pm(INdw(gpV;me1h7xor24c!{7JC{158%w#Q{t<&On~$m)WcUV)I-q zg$7IG{)68E$8i#DdEWMg+1b)KG~4Hqc$B;%f(oer;TZJ3u+VfT<)X zTwwZpW^nAO`Vim zt8H1hz{C=mHh{zuC|ed6FzN52c>O)sHMTAGceH8%5|1H%*%|^QmBCoX;wO;iCyyp` z&R6(sBwi|iteNV2q$?BP<5*aBBU{;!T%`(TiQ!jz? zuGyfiGy?h(Qug4&!c>!@^%ZbrF7*NI@+;b6Kfo=?f)f4{0@_mbKIhRJr#zoq`?3VP zG?YrwiU^qKonb=2AfhX>uz@ZSNLIZA#;-9T?;ioka3o}o4bVGWDacs2e16Rz*5fCt z!sm}X0?hZQ<9)}!l6}cle5Y}#6q6uO;gtX}pmMT4n>Xd^N5-Fwa!O z2Y#I-Z5g0)^T*u}^T%nUGTaGiV-60Rf8MTjc|J<|%xjN3&m4ao^eT@96dhsE%a67uG6;8vnTM{poU>Up7@lf4h;V;HI#IhidYiX z&&x~wVe&9Au5HM#xy4q0G-zE95nI`f6~QWR{7lRIYDgkriB@|P1qU)JN2`EiLsWL{ z%bz)Z<16vu=I3IODpR^IbmX1I)a2yVPu_m6d7qLnT~x71%$_w+;gjJ{g0ea(+Y!#P z5DPB$n63KQPExlb`)hgHqKPX};Ma_! z$I0$86^0$Y=e@GtnDcglX(Dl(m-y1=kDDj?e7^z%0x&$Kok1UUO( zk(6e}M#Dl`jRH5dI7nAp#H z?0q!OpeBJUma2jW8~($1d$TXsALv9tlLh-$z~Y}J0u{eK3ssgFJA|o5H4!Ywg7;cAih}!|oin;8-#oJw`<#XBiB0XJri3XQQxGL2k0^r%P zo*iQa0xWtCKnyT;YbU`nmmRQ#>o0Rbi?h&|Aui0wi(2e}hs9!XPdldKjrm|$%QVu3 z*|++XY{y>yn2QHrk%7ocE+}yM6Gq7aDVt$JfWSain9L>my+p56Te^UH0MfEq%cJWy zuB+hx%2HwFqGGOK<*L3z&opk`H(|&VZxwe^&Pyo;mq4z&V}d8SLLIq!qi$pcS-f^n zsRS>j;9}(F`(N`#MJGVnv>Mtux5o>)d2VcfwMX{NisDP%`6m}0xR@3r7QA1+P46cU z%Z?~;IHLg1^^EOMHei4?Xwnu>a#lL(A_%#l!DZ3!MMK9*wJ-ZAy>15;Y@+^5CE(RC z?jANFu2J1Ozhq~h?FPl%#6Mr;f&-Ue?tHq9zyFcZ+ixH85MD4zb37O}Y^usosQSy! zgo9faLo8&%tHXOh-Kyna$m|`En|2+k8s~iWOP5>wv~G)TTZ01iQZEIXgGmWI^+~b-(W2d}9hC>&SHK3Gt zuAxvx7X{h2eaCE{GiG2LXwkc$7S|U`R#Zo)b8hd`ZO6{7TbhFMsYNOUnJ(uoxR@b2 z22fR~T;=z7e_YuDgU91D0*V#MCuzT7IhJwO_IY@5^%ETHVR>)ClpmoEd{Pk^CZ;^9o!XTWO%G+2E2XFMqP|9*?>N zBI{;sni5Do$4;ahs9+`}b)Qea>(Jiau-r}`{s#U9wTB9Bm++^!^D_alR=I7;U$2)dxgId*VLWOe4aP)WvB&DSNjiRY0 zPHD-I^9Ee1ZN&r2IqdmY_jl~oF9E-Jh0=_0VHpem;^n|sf7@)>{P~!G(tyFcY4JDE zuYZ4Xj9+}E+LfVsjs7Y?%%E7Yd>J&lq_GJ+c#GNS=#eAf^?H?n&f_ilU5$@kzjOO` z=sTbhR7gvKbLVow<#L6i&LWZX0$dER3`3niT)pQMW}yORDMa&}o?tlU!8d%)UoidN zHUXt${5}2P?jgfq-P$$eg;Oxlz=PuW@#C;*^>47gOxubf**Jt8k`|cx9fZ3@aAM$f^NLcdCXHcU?4JAE3FX2UOi+wd|5MZB* zXTRQUs`e5S6Jf);bvo;*zl*`T2Nv}|jPPmSZ+yB;vaNq@&n<<{9c8=mc7(-2Qw z`{BWud*`K;Sa31GV%^DHvsUgp?qTgHW;`0{1K?hnwt^T?W8MtDSH4^gGlumDC|%PZ zz6Bnf`mqKoJ3EhFbMybchEJdAZ^~aJoFGcFlt^$1Y%Bt-c71Nz;qm$mY~^>FMW4|I z6M-)#y-~nvoApo^SVwI<3ND)t+FAb_4VyM^6GcU5a6F7aRyp7VoJUgPz{RartGV^3 z*KO42hQ}wAE%0%GVZfqPi+*e_?s+(_9)lkTIQfZfy3Xa~!RCe!DFDhM9}Gd+G14SpOqw;$x$M+Idvyy4H5f)g9co8R%`Us2F6R z16foZ_jBQ`7j03#Z$&?U!rW&1R$8udl?C@c@xQK@cDhp5E;6yOvi;)h6uhJbZciYBdywLcybM*?qhnHFk2fyKcM^7bK}na_a9he_GR$jKU*=#!vn;^=JNWi z!6RZla$Y^eFIwj3&E{m)kMrQttMt`K6fldcTKddm`wu|vs+g^0>}rL!n8jqmTuL97 z1c_ynp>oZ-8~^vok9BZdl%*X(bgOv48hkYGGrwD21MTJv?cILH@xuq70OtEAT)-aJ zLe@&IT>Y;C(mK>H;@L;$BC)IXc-Eu$V0q)))iCrr`=0uL=f4dty6NEZ`gtZ!+n&w; zK-)flVHKy9vN+p}P(pR`W8w=-;HS<`1<)#A6* z=hkt;>X5G*Jxuoo6`n@I>C+Pd@59x5&JOL_x8KQma12khiWH^)C+k zy^avNRSdtg*ITgO^hJLyA3vzyj$Qw5Y)+F5YE>C7z={=I3~TW-47%=`v$(mNE6sYf z$QZIVz$51v^?12^*|;mzYXXpA4=yYoud&*)t7^9{S_3YpgLFR-cJi=HrSIbm^TV%6 z{5Dm5uzh-VtT*QEZ|ZE{uzL54_uo_tbbvv)04r|h5*hM1l=JxPDP21@Rf{&I7p%dD z&klMYUys^ol7rkx_GMW;N5i*pc;An3>+`QOov3CI{4?)Qh>miQV`^h*eOuVpV(cyg zGNv_5#b?v94Yp6uAM4d_*1Fu-cNZLc{;ocTX$!&uSV;jFJCw%N;)eTvb+g~+>})PB zGOx|32$3Daz7S;~pJL=GAoQtSAWT5NRj%F>ew*{12EMbIhv8i2AxKQA3T0BO)1G(I zpGB%Y(ywJ*HNN`tQ*ixrZy54J=v)rHTZIRGAgq^O?~>TQLmoXifA%Zs>~En0tk}S1 zXUJcA+sGKV*9;&E29|wf8{o0`Vo^%FS*i2KKLk;7MFfs2%1I4SlBtLj07`}vkdTy4 zfQ7;4^D)()N2v~FQXN`Xjs3>Iu2r{&t`)B}c!KTI3zYS)y8Xe#Ro{QP^Z0?ib=ccN z0a$S|mx#3pz(k#u+P=?*xg@Y1JCBcZPoIQuMh}8&^?PW{lbqa40|sR$jj4P_)*`X{ zXeWdZv~ktUy#zpnr4(b`>5#Rad9TV?4hOwP1_Mho`Qc|E3a3vm7}k60lcnWG_h@!c zZuZ%?foZe`3$T&^E`z-lla%%a`wdU#n92kg4tRWPam$aN!_Fl?LTaVP8knlqy9#_% zXDUG{aWjC@;^l!x*^IrGyG8=to}#vv&)COB&+Sf#hZvTnS-UC3ZWXGFaK2^Gh4=m;d3q(o+xC|}DE1!M0KHDSj5ipgZgv!>yJGx~Zocd=kq*QFAf=LI1W>c{M z@iEn4kX56W0QESKs`2${qv57kJ_6(TG=1OHNos1m1P;DXSns9ZcAR{ybDb1mK!y9k z0_|um7H|oC4mEJaSFQ;jzC_yD5?(Rzu%y;+0lU9n0$4AS#DR%3(kZ}-s%m`q-4~(% z@Qyy41{RLw5qPzlsxFB99&i|9wsTK~^u3=1x@W{1$c9sM}fSxOok5B$S0JZ5!*K zdcX((_I50?A7_(bz!whdjsJQ3v2j<{EeA%s%dh=a+%}hKF#^r8mAjDLx3VQnRi?V7 zRb6pajT={g1?{`OKtA#ChP7Q|#pmb4v{j#;4iF!s7fw$>vR+JL*@3xdvOWMKd)Zcx zL4(U;hGTZWhvaZJ@%g;iM`{B){vFhHV?akbzd#fPSsFVE0>I*XAg3t9Z^8+Li??db z@ns9gf+>cz7(j%XY#J;6SWmg0xj#(S8~NqxS10zUheylOQ4})hKod$p;If&bs7iwo zGq)x;?I_zNj+9XK@Zaep4xL!L6pvfk1N!Q*6lfJZTy3Mc&x;iyv>iCs%RYS!;>wg) zt1&YZVc_oCv=CxqWf(hsE~Hhc1Ws9r*C8puCiS-`4@TU1BOE=R1x-5)HpF3IKEtt> zfawSU)e(CshTj=gNbWwqFo zE2-R=<}WXKiayleuo#a|Phh==rdF;Guo{Ce=h%K&`$TU%Xbm^%$a>hs(UnW8)kp?% zY&Q}Bg{-W71YATkc0z{r#_!vU}tAaQB=AaCPhm@|=uZA4v-VjHEmnGnb^sEvZ0q zIwY-%mem?@%1`Uax$Rs2gGF<{hVoULiK|ARF%M#WFezuiXDUnqvR=4fGOX@(L`9GE zOd|AS>yn^!Cj?dKfqbafz`T#20DSGv3RbP+70hBhMmGsVoJcfkUyMe45gAz$vj&z|nKG zik2vIY2c&aQsV42d^|b+WyV}+c=uQcwtEE_9q`}@hV_UF0%Sb^u4_a9J_@`>y>9(` z^TLm@n>t-)Psm;xxU4*#QRwRS3K)ZbFb6!db3yU2UKvzv=3*77j3hAt0RXKEl+{jE z9e6sMVbOpIl9nEaYWPcPzn5N(DsQIs{dd6+@6Loe{T~o?1y~nUdQR6zXsoB?I&jvr z{f^79UYo&BCn0cAjL_BG9fm~{)r zd%1l(@bMrHhV=~K#A z(HxI{zdmK99*<_Ev(J%~*kmSN1$;h@mGpCXPiHQ}bJSL7<12c@p;KqT$xIvIua!%6 zYbr1hIO}1t8xzJeS1{jk>s>wY&uxo7Y5_VKQT8%|i>4WjtmTwq;-7P~nY3dcE^}7S z1x3YruC#Q>rxWk0m(UJ7LwY2i0c-$z)#eZ>Uee<*01{LuR!L`C1-wdJNr?%Nk*|Z7 z9Z$uxG4T2^=_4@K!@~8I+7d)C-*KA{8k2^=MXSaF&}%-eiN%A<05DgZYp-z|T4To2 zBr!MJ0l;6T3WN377fgk132Fwb#$@UpY`&OkQBy9%d@=_j9WMpX|P91pOV?;ppY_c;Gz6Rk6a!E>jnEAw`R}VpWF5QyWfEsTtuAAGfWeK zxC>BK>d+gMfp+xyrSf86JpikcX>2SQ?q!}UcApo3&xZMw11E{hu6xDuUM*d~-D?d# zLxU$s*5jYe1F!BakCs8;VmsRnx3J75fswf!?mZhps@2Lea-(5wOq5-dH&U#JMGk`X z{5q0`$a-4b7T^=>gL{_gB?r-3G^I=5_XWs$03mWg#XV5#mi)NhgJ@8JN zp2@&bZ8bwnNn$;EPD^u$tjFfda6eP!T6M4JadYWrt2ztd@pT7h^$BA7UUxmRc<(}2ZDP; z{*G%6Jl5WlL66qQSBosjB+2Xf-sAJ>;Ihe--(L1YU_F4~YzQx>Amtvm?}kisN*6sx zx5d%W2$uCi`Hm~c#CP%Lp4$Ls<`NDTG$b|VgNL)u-S%o(;|1yqk#wc|o$tMvSr3cd z=Z$b!51{a>+;CWrrpp+wA6*CWo^Y}pu?Z~^xOkJw04|?JfT*2+4m|CJ??ed{mv&>u z3X}DeX9UiAerMwgk@WxyugVRF^{|)?dQR6itY@lWgus)M)I*uu`UMxcy}(_7ZgFZ4 z3$!I8RygxA>2Ts=Vm)A<9S-Y>(#76iTn&wIS`Z+FcVzcw%#6n-kmi#C9gO`NX<;?^<_#-w&vB>Qq%% zpYGm!H>ht}d3y!XJsAPRh(~bB!0(+#(>f6CxNau=<%QVaty0C~2+Su+SF>%@2XYK9D^4UUGk0~RsRwT#*&`n`@;$t>SkLP)9zpPOBSj0HHchNl2p zu$BLtkk#0zx^m}c(b2!>;SZj>8PR5RTt5QBDn>e$AD(M`n+jx@t1L$KQ0j9rHBmI$ zhsOY)J^Qb*m&2X!HVd)U6%z2pU>w|w)zP_PAb2q#kC^E7tAVv;T5-)zlVq@M?BeYWoBuP6Q@l&!{`# z4H(j*Fvrf`rc1^PfV`VlFecpH4+3Mv>x^c_;(Nylo{U6&FfNiq>3V3lbF&cxo5K2VT-r(&$GeWhj`LnGQ2>fQx2@IjM+tZlI( z!Gh^^Dgrvy_@Ln6{z_ExQ*3lJ4+bj%V%*ccZ@~|Gzd7OE&&E>`qGzAiVzb+@;WP7B zQ+v!BXEUzT9ksWLKzdKWuXb!ubE>Kmyvei}(honI=c%}jX2V zv&<LL&j^_M9m*wZk(f7N??76GOZ<=pjQV zEhCtW=&IQK8cbPG$xEiWI#|EF11h`Skh$_`$_HWh^d57EatMk9BLVt^Pd_|=+hZ}K z+Yg*7TSk$sli&QF&%S`}0_~`aIwIs~hBdCcQkKfbb&Zjz749eG_v5{rV9Gqpsgi>{6_ST zzj`2&NAn=&ncA~?&&JFhhra>%g(7`MoL9>;H_{?< z(ESru1-TANE;pz#KR613-v#HjwG5)U4jq~~w_3~ScBe4E@Qra+L{d_h+j+((K=_fR zf|0NA%vqTEls07)7J&2=FrbAk?Imy2Z?+~|j0&tUqwVudzJoXCAj}JjA>A5=FeB8+ zd(up9H}m}30XOW~Q>?(ZMU|s~7wPE5n#>=EB+w)Ep7?^r4TN1$FgA104xF%#>ER4+ z!reG^fcM0{Ijg5x%H?H0SXfk^*P#uRIeWithxz)9@ZP4WbI$0~jQlgb5U~6S^AkJZ zw$B;tM<=<~vU6ZU|ElpK{b+KeztW{QW(svzp5sn|*NivnM0nPCnsAiw%4-Ve~`0nvd`2+@%h(mgN}n!jD?VAtbq z)^eU`Xq^l}2-Q(B{8v7uu(*DU8!$peZZ)o}-6n-Qk(4|pmC+sq4zKGB_%Rrew)|~| zjMiQ=AkWdl9_l zHf-smYkA`RsI&d;sX^Kk(18#=N5TJ1u6x9UOevE+5HT}iVUCUYsD9T}qSuTw^Dud- zhbGEQ!$0-ORZ0{rx^_BVL)-5!b7}dNY{uAg9{90{#%yC8$=z1xNFNqB+=vawu7lCA zQ~(!aYc{46^lQ-3_Bi9${!fj;{zFDJr^)^TLp;jHPGpM1ku(?772n^NG|O8hAjTqM zBFQ6{x}X!w(J94Tkl5cK0EW8@GhGH92=^+X%^8^V8h<0_sMyG{35~l4o%MA@7!tyoi3jnAgwrH6_|D- z_JJ&A&$}NZIoeW`feGqWIw$nXt4Oo-eXG}Dqv37Nd~N=v7&PrDXRo@ z#4n}g+S65Ha4KLL!ObfQ`8mtzBGdk&_*rc<1D-kL(uDDeBX@<8IQ|32!I!m{MggohbYUP+CMlgJ4` zG5ftALPOMqZ=|Pf+zzJ_t&mXKRgcMs#brd<%xJB6Z(@YSm7ms7Tn;l0Ez@ z7|kd(aT%6sK2*1QGqnj*!drHHci#`rI&P?bLApO8c}58IC~X7Pm|nuSv6$)A;)5ID zsi>fg8W8G~Gv?e8M|E|TxY{iZm6#I5jV26&0aMG0o<{X=h4s!FAdR?ZYbdG^jsoT(X<_vyxU}=_GNf<7-Rm0%peLs;4 z?aU}BF!ib+ez2#eTxBCt zk+J(oja?!uBtQAR9mH*E{hbYhqp?6MjZp%p5MABXjf)WgTaU0S`B*tCR}8Si_0A%J z(HN2muIIbyQ?Z9&_3gsQNIzQM1+ysKg=VjQ@C9V>8bsqNSLxO5pu!mbHc8!peF6ir>52DHQFn5?kQT7>#eHF>Xn$# zJ)CpC7g?3|+wG4hL&Kn`clzzmX+ZJIG$AR8Mzj#dVcp#eki!!QoZsy!x|HiZ|7TSw z^BWkYGFLCtsiXJc$V*A3om${FGa@XiTO!vFmpB6|xV{oLy=ykP5z*#+zY(8y4dsvx zr^8`WZ-4psfgzetsKj|!mc~h6*kmqa;_Ci5Gg+pxzHX}ooYu&u;u{wcsc!bE48nPw z%s^QU)(YM8x+8%ta65=e!06C>+1Hcva5~5sg7`gy@_f5YSHoNOXum6{AO?j_S&B`i={6?GiUR{AJ*bgLh)*Mrzb zyG-(-*KO~EzG2VA|FP%oW@gypaZ|lnkH+VA1y*O&t7<}*VpN>mCR+xp=p&f)?$ZwBLrAWn8%;HAMvGa-PGwv3>Ng!G z_vJMxWauvySOIFmY~}S9^RAEmm1^f%bNs+ao0^#9mSA9AxUp+#*G`m%FM(6K4b^U; zgl(Rnq-b{J&H0VYS9*MKpyBp+sPoRbq19otzQ~D%>LDmWb&pmQg`2>*{_ZPYjqYa! zi_z?d%^M=V_!gSr>20L6Fcu2SocCIFPPSkgUZ481YPAV#)ml4v#Zr}>&1pR<1|Brr z=W{2~x43M*`H>-`Ln*p;~HKMy??-25Fjet0Wl3d_5q^ISgwm@+;ev3gn!3H*Kb zg|cK8C3n4(2ATZ>q)nOva51viK)r|fvyI=N z`~H0K)K;1X?U>0s_3rgq<>q_$*p@*clW~2>?C`e|mY(;;XhEa5yKN{A6aT%&H-CvI zxiwSprX+<%O{3;#d18@y^}5rN&7$XSBbxey5r#)-7|4AUp z&3iG{)C%0A*RFu#4GSq1s*fPk0pEBZ#lyI;j~+dD4x(eHfFmOxbZ!QFquzelgN5Cz z!}$jZUF(0Pz&uolV=1NK{I5n}Md)$-;*1^9z1fAXfd$?361V2m()eU{L1}vyMbP(? z^ZD16v^cxaK*cvZ>NckF<~Gh|mjtL=Hs7i)F&{HLquuUFVypGZ_On_Cqe7z`Y^h=; zVEi)$86wZQmN3h5QQb(_53LxKm`T)wB0Ll%)ttcT3P=SjD^)eC|)daxRb~;Tj%DD{W4nQ z_*UI&8A3t|)A6-aw_E48*%1)AH~MFL=MqIT|D)Gl5!A zFGFD&Jt0*8vJ*Wc7;dc80}>whH(-zit+fna+$ihwTaF@cN>?_`0qMK=>EKb2Q!jry z*WX5NPwKsLS@~mlX5d`_{`Gf=oc!SvYRBQH^{;GBR-6o3DKDoO)hKp&X4VljR?SW0(sj)jhkr@@h1l!XXsZx z4f)qC>Q8xwUtX73a?ATu{wMl-fpr$jtKkD9-iUVB+HzN{`24G zp`)klu!-|A`}CUz^(7;vV(E%mJzdF7QIe-!U+*qUp_Tnx+fiOOXV4CR7TD50djR78 zV~|H2Ed?Wt<72t7^NprB#!>!>r z8}iFRd-!foIT2hQgRCaAf{sD-8p|Wy6<+k`rxBh7F>Ah!nwH}$`w%5LZmyNt zX(%hcXy@Nk@VaWmQfn3x?MA)`x(}oMilhNZScDE z6R;=U_VU6r0KD-+Y>}B5k43O4P@I|uG&03d?kkYx3c4RE1jDE1meg8ePw4`xm)fNh zwbA0#=-;m$cxmzsi=i=R{9Yh5yUZ%bFEKO_PFBK5LmK-7_s0QMQW8I$Je; zw^3Hp&z|&mcy@<1qMISiDvA;)x-8)62(Kum<3O)fTN+ixna! z=&8Q<^^Ge!>+NUorj9*#R!U$uZ-`AHn}${-aeZ`d8yGPIRqM6SPNR^=tfHsC(Ji$2 z;peQ(3?9^}HpXNN7ZT*C=L+YRu-PtMQU243)oUZe|2m?D*~^B8%XL}$puLMkV6Zmw z;Bk5_*&g=j5m?>}Cxx7sEt&Zds0G+%ceuQ=Xia^ROb433H`Va0=W5?fPX-%K0Yax` z&mln*QXuFq3Mm=;SHx1AvV*D{mje3f^kB?ztT^W( zBckoI%b_hQjL_nI6cHl-u+t#sETe0GJMoO|)ve;@o4W#BJ zA!POFLZ*y2Qz%Y7r+6)zlZ339+RohrhIzs7U8-Fz-2_+7ePoEcE3a`gV+X8EeRJ^U ziZHHDOkpO4O@`O7+~-i!^2xTMnwpUFbasm-u6&yKJ6A)5z;ByH7hq$)p~Sgj|7=rW$UBC6vGRMVskfVFKQFv2$!wmEvIP-G5Z@u~ zJuum?*O1PdPvOnR)$8Ps{pm0VnXY@FnsN2PF)BDo#m}56ET@D@)}R56%tMs>S2hrGIQYUPVBhAJ?i?QOE|+t0cm=xtmP`_Il9#nGA!VhfF8{ixa++%eu)5qtS_N5DMZwO3{g_ zatPCBcAX3uh-Hf)gzK8bkOvY<0NvE-a5uwlIF|ZD;^ujZotP*#E%jTL2RFGUm#=! zDqjPOlI1lOUSV{Aye>Mn?L8h7?}u^uZUv)~yiz>@%+)c*THG+~?Lun^n#1SA%~5mR zW0H2f|IxP+K{>cnq?II{X^0z;tGjvgOpk>QMnBO3DXl0mLzR zdysX}#=jOg{!f4h^ZaFe;EjUVkB+hDu{X7X>w5>>zcw#6?sMChTbj~WS`^v9+!tqU zi>5K`wR%p2l6UNr5D9gYGjKY1u+EbRxR{V(E;I>W-I`4OR{;qTX@y>uqBf925>qX{>Zz-blLta0&c4 zH^?$>P@TNwq+~$dRJSF%WQCKHvS1QK6i|@ZG+pF96o@&bsNZi}wQ0N6KWo13d0_|o z-wL$(?ddJ7C)-}VPk~eNE451SQ@5ANI4p+C*${0uKO9s(0U4oTz2O{6NGXyqdi%{i-4hz9J9n^!P(97 zqNp2_wq;cF$1(_uAsCbxag|x;kn;x+%`={ZxUIW|3!-hS;TL}BC~#uLPIZqjBf+d{ zgd9VkmMm34Gtqwwsrz?lDNRi9JmG7hVUyHLYa3$WIo%eUEsaRP$TO}0?|AK-uLo1x zAcPu(>zRlPlD0Ay-cX!UxH#qlI8i9-EPM1<75*kkdGzP?`a{@V9Y@+&B>cq zGlZYCXiKE0iTv~+Uu@<<5W=>rx1RwcdVzbbr;nqd^+06RZbG-+baQJ&YEqya9Z<@{X(J*q-6D}o&6r>!u57cdkh9{}V zqPqWrqtTf~{LyM5r*{*g?GWYI^q88ixJz>8iw_qAJiHz`iV$nfme(-IKSkERe)4Dz&WR0^8vZqSHbe3e=9k$6hErkkVnD7#Y)6~IEIo6! zcFcF`*0sMIwWQnh@2kHEyfSd^%;d?c5oH8ZwO6wMIn5|jcP>>(a|y@rak#9wDfN)4 z_m7F}L=B5minX=V{P^ZLm{Zr-UZHGqv5l2)S=9W}8s;J^_b;EzQ} zD&mWL{@5X-FOao>ZNc)dytC|oJ8SEGI&dDZH5T`CtzvM&p`(ixduduJn!veNb9Kl} zM3tWfCy1;ZTK ztg^n14YpGu?w;Cn1qei@n3bB{uEd#96;v%21&`$dS(Pq=l3^c~8Uwj-X3Pi!yR)#w ztO4pL?#IlQ+aUe^zG^Xp@prr?Dn5sz^W4ZFSdCT&iN~FjnT=`mz z?eYY7%tdbUZ!r86C9RE*h$f@+4}5kmcE9MYeI=M7x8mAWTGnF)rvmNg>1YO6ot?Jv zP8A%=Mb}*~ab0wma(-TIzFfI@-?PPSB6gS^xmOmcCQq(v>G}O692b_n#1$&`c)U;v zYpaXMiqX~dgr!!t#|B&N=Y{;1>j`gJ;fF+&N@m$S`}oX^!Ta(=8sDCkT1pEgJkS#i zvu4j+3R!7`gfwx7GNRuiL)E^NJj^k7J+?OlyM(p&+Mm7b$+j6OVaj~aZe^qhhbOBu&CWV53Z$lOd8dOyCcwKGqc;1E~&Yr%?0G(bD zEd4*Cv}bFM-;B6yho9kDpr-le!7FML6#$!rzU3El)p#qW1Y0ZXq&lz4Odhs0EkkCF z>j-ihI`O~uN*DdiUI?BIS~amtDBa*Xhnm~?ObL2b*Ys$0Yb!DqZcl=8&b=+&!z9V&BJKE(WiLZ<#L)C+Q%5 z8Nr24H_K&wImcI*wKfV$L|Mb<*7QYL$Ep1!88-J_Jhc2zJEgL?do^qZ+6>isxQlji z+ba4_=Z&iZtmUfgm!H2xY!S`#Oebo3CxrDd)p{b#e=JGMDN27boYq8g1Z9jwyc1%2 zGQ!U6BODM$E_@WWOYqlgoQnVne@2r<{TyGh!L)&yE5)6P+s zeVGr7HmZ`qMM!AmD&c@Ql=1_*dZhCWxazUj)#;BLiR*jiF%ofXhE%+ovH+is7%cpE zA@5^QDvr6QTmJ^{?kDhAK5jsLk?LjC>S2GBN$qMU>1#gq*{^E^qJKrJ6T#j5Q3!99 zq_BnayH`7H2CZEg7mDtz85!id_D}ZTqGGHWrn1gs9{y|@x3sl?U&Hj5nXlC9yz$;jg0{*(ad&s&@l9xIeH_Ho|>`g)+kMp1( zyB9seZ|ftSqc83=f7g|UdeUg|2GKcB^@a8#b7xu3s{bYo?o_xy170u1clNTuoh2Df zQ{kVhs_nr6N-f)_CY(5%KXb{uJp1Mef6EBHb~q0bt0wUo7Ytw{OS7INoQ58vzUgw> z^tg{K!vl_bBZdG2AoX!2#fD7vOeS=1(L;8H5Cq8u3tRC*vA1}41JM*@9)a7r?T}Ce z{wZFY^c(#MlW&N?c=_H5oT789FYoR1r@v0W>dzZkPm6;lM7J?tDzGk7{?6|ZAjBPN zy^xikYqPuOkCbb==8sJ4#xD;UH_V4++U+@e?T}7j5;{bpK)>!se%5N_FRd*>I?Gl_ z{BYch1mGkW&+g}RYSfkjgXZJ-T9&R5B&Amx^G(|dJpW1$VQIGc1L zW!Vnv@Ly*)3c%BseD+>q9valVH~lt{u-2CCCX&&46R;bKKiEU*SHIg!Izl7}ob`8x zn1x%EbNq}*3d&ZC*dliB+CKNijM=sxI+8g*2OFS_d~Ne|fLbzu+kbnUVqASoUUZ}- zm{N*3r`^uVhbFnHU)W(a0q+*xdc5L(fB=F$oN2cn3&&|P5&;4`q}&C}5U>H}6YCf= z;QM5a`5yWmd@;rQg$2WSBPF39+uikl8;{4<#TGkaCK06}im&Kq2Tun=4R)}S(x8rs zKtbg1V;OwucwfIa+D_|8IK8f(TB?uh`@O| zsRE-wR=ZH-CS`xjF+6w{do`39GgA7@Otq^#?G>l1MvA|((e9eUzZ~-|sx)o$p@SN{ z4j&4gF$~ufZ zz5lX)k?ZJ<2o$wq$XS_d%Ox*|7tJ>TjfSrz?LRD~)WCO1?Ajzc*mheL(PaXtt*AJ- zQMEzO1R5~g{l5?NG)p=~t&jY-Fbh{5jRXu(YtcV>5##!_lpf3WM9^a|8wE9{*P3gk z`=~kUn1CbXKe}CS#KLH#8(X*1LrV%?ktzsZkTMxj$cjSQbUM>MIc+`t&ZT$=p(38# z3FH}ta{{GRu-TjMR<)^H;3hx2n;v_W4`ot|!uWEtMXI1Dusdto55?a~stavB()4R* zH%Z7~7B0E}xo9nb(0~XA=z}6Wv&b-AAQ1z0z)m4&+2|@u3rcG>eKrgXzm3r3P7?mHEa(In ztaNNE*M-$!QhD2HAGB%h6p|c~>`_=}alm#duCiX9mFjr#FG8}YcI9JfIm}S<%|<&C zMS78PIwc_h0$Cy6Y(W9KM7>pt%J4ZQlo)!J!kRZroWMuN4KR-lG{`tV{fZGU~yP@#prLyWK=hp<)jQXD@4o&};_}@*ws$+-( zy=xv^US=?UQv@fscbLV$K>&g6MtKU;kyk|9d@!r|V6%8MLe0}R@f;a}W94Sl0a$B4omOFy0C$%QGL{L#cdUEbaRe26Jp@4k zSQB=;JQK#d9;conNOW9vphpzTMNPjCzcPN?CyZY%?H?}@x|Ck6NlA^7Y(1C^@AAwR zoe#@lg$gnfI`+5qmb-WeVSq8g7b74b>6r5O#zkWm%|y#zfRS}cLreN~2Ulg|0cV}j zjPKYiYhrp+r`7e+FVgfotGn+Slf>QJ4t`7o<_aoQJVxEb=ybMG|$QBVx$j9 zI6plqrd)V(Qn?|03J$@FPNjD=R`v=)3rmF--L@$PrIZuA)`acr5!tf5euQX>ZO3~ zxO`127)4QIfW52g!D()wnC!LNQfoJDEy?hZxL<;o3DtDfF5z264)gog2s$yG%HB>y z4hoevuTrw3MIuj$@}&=_1L}lDyn+#%y~6Z?PNYgVj+Go30I?T#QW~sF!SGvRGlkOz z77XiMCH?_E7n+QtR|IpI8dpS4+H~Nq*K}O%j2b0|iGGP#?LXJ`g;+F~Wb`O3AlUhD z9QZb?J#A#qeQ=mEd!xY$yyq$DBLWHXe>n{C2==r_MytuG8u^RoO zU3I061L5e>ar_z~!m!{>_@N~`7*I{=(E*P@5{?ANVMtZ)pA3Nf+_sSn+bOw0LNbo5y)Ei@bGtu+JN*T@PkC zh$r9jY1kTfkJ+V<21!Sn#g3E(7FB+>i14QCY&C5pQG1V!^YC5>nWY4 zpxltUYib;~+da(}W5jE}^8vMpI`BwHx53}{i}Xie7&L8aqX~k)Q9C{RN|;Jye&`~Z zFCVrMwY&)Yc8Vzn$!R1MJPgZx%A8^}Bx7Wh#otk<_=$~wXg3LPz;Z1dcE3e zeoT8#z&aLxSjqVx=Sl0$fz$?r+jfu>e5$r#s=kr7yN;H?7L7tXGdX4-`-)hoM0{lG z{$9#pCi&XJt!#gyl%X1B_2<@P5I7#yWaeVmKNiGE{0G_xZ`-5ma^24!xR@y(jfJ=n z%mZ`(~8&~l&|m&-i2_W8P0Zd>w0 zlaU8OAQWU!jo62@6y+gUmNzAGU5f4#xX;sfOqP_uzD7=MdcKO!Qe{j(^m`Vo&@w%E zyE=^X?wt!=xe0hsX^*px`QQY5zEH-lrHD9+4LKyh!jJ!G^`UN3pm*N}@*Rj2hk#(w z;2}4Hvl@J*msyPlB~OTIcTqA{<|ckOXV0yb85v8`Wx1YYpg@OQ_?5ah@Y1UXxM7K} zBPB!owA}pZrN12VyRqS@=1Rr7@Aa8IuwWvf@i=V zup8^ix>e;rA$vbdF`5U-xdosMIlPKRTHvIAv)s{#o`2Ii8>7itUh@aWzglUUy|Ch! z@yu5q3{U8=e(Jo&A3RkpxR=xW*$_&z#QoN0z539U7ZqD$}kGCciXqe+T!mD_x{6*3u#XS1DUH)5LY~yhIv% zY3jWvk+~?h~qX-d)Y;gC9=MOGmafn3HOVlF9`dXEFyZ1v_xS7f~x8Mjk zbZPIqXSOd$C&qnX5C|xkMxGx!q|)_Quy;l!HmFd?$Ox7s53-JPsBJ6_4)BpZ>0ZdY z{ZtpzUj2Z|{5>A#Ey4OnECsoZYF(j;k6jEHg_sA+@q6~@*@0;8U(`{?c*uI?2YMP8wy8&kVPZqKcBr3{Na%+8H@&;FjdwFk z7i#N*RG5nlo$3cmLMxu$rGoXs7Wf#mE&TR4fZ40QlULr3+joV~K_*tHmw~m4 z#Zr5pbzEH4MqjBoLEAI{iLpEd$ilZh%z+!z3Poeahpyu z7Q$$Oq-$~!fT5>{)`N%9!$~{=;{eb2p+|4D=A_4}f`w8ielGrIf~8uxFEIjckwXoS zx#wk~Z^^%6hGAk3(sUC&m#Gq%bz-W&^9Y`!XMpUDWz=#hdQQe8r#dvm{(a|Je1*Kxeo!>sDUk<39FPYuLE4sPnn*k_Bt_y6rIWIna7eRtu!Dl6~YJejtE?da4w#Ys>87P1A z`mW4kd2r*oGNs}?3YtbXp&F2qmZ96?vz_tulxB)0od_@3OTT+hy|(yup*ya-Q|J}8b)Bg~URD~xyIb&vCYFw>7u$}{>>+F_Z@6naZ20EA_?BUqr;gUbcf~p+uXEc159tCoV zFAAc$KK*`->?Mb$MR%B0TXipI(q|W=3DSsduMylx%5a0_IPDGyYqvKajd$bytK_C* zNL5sJ@e86J7-?N^>=(X$@NL)SO0}wIF3=Ey+a=>(@53tWhXIvo<9jx?TK5ARUGHie zS50tGm(F?_C)Vl;5hre6^{GA&=Xb z=#WeQ3VT?^MtGaQI~B%jXC!T?LelSAm}!QDDT2$2PPSQ5{lp9DfWgM*j#n z!#_^3`{myJ0&|MdPeyEQZIEyUTqw1XxS*z=u4m{2$3k(zX)6VM(Sh}P1`S1i>nQu% zo`-AD>;H6SR#?ruUcX;V*6G}r5`fw#3tXPBxckH)+N!@1`k<=}xAc065U;wb#M*kp ze#BQpFmT`+gxT= zvk-Y+>_4GfzzqzJL6Dg&Gj*5!jsmC%@ z1EuWTxVgx>)}+qz``969W$%63Ua8$`^V<|Hv`(+Zoved%0Xd4GfYyT{hN=^vRTXlc z>uoqYoH*WQaeH&>tV}Qv+UoPnk&8j5Y5iBt{=koNJp#HfRj~lg@XKgr%$xPxb(uo* zhAo*`2@ZqY!rw0?F6My#kd#js zVPNCKYvEwGbn>q%57i&}xwgrm;9vW!Zue$o2CLjSF+`K$!0_>Mi^nMR_4>5sV@C?{ z*Dn&}&mTGR02((@hhgk8`6V12oFZ0MHV@UuP8}=CG&isJkCZ=m9m{`T@4s|fjh(vA z*EPQfL^wtW=8RQ+e)G%)&wU>6!K${UeDYNdF%5!jq7yjEV*Finl=0x}!}=W(-Le!! z&4bN~<%ey>K57>MVQt5i;S<8oKW^djtGlgs54t0Pgj#~U1Ylb1yCnzVP)#W`XzRBm zUS3{&e~OD^Z{K`V66uc*kB{Zd4nI_(5b@JD{rdu98XBCb8EL|*s%$-PmsXZ~B!KD3 z^w39^Uc2`WVlwT#@21Kgt;fBc7yMU&*Z$8}U6xBc8Q+$!4XZ5YuAt%NpwI!o>t#u) z$nX8AQizHBwoD*-7y;SZZk|ZI`LZYZGMZ#32A%oPp;p-H>!o#rv!|`>hXLRc0o>D+ zhaG8Ag*C@9v$Ccg@i84hUR(^p$O5V7xPg;4sS)%IRp%#XupchnZOke;Yu0 zDci2bVSVF zJrz+;fuWk&zBK3a{c40LJfJAzdrXJ1I%z|NhYpxwqzLhMJvUk>|6q~ZPj){|R17;I zM+;DZ;3y8-L)~NrXz`-{CgS^n8<_>gZ*=xV^0WQwic$H0X!;7jsM=_2Qa~CMlx~z5 z5TsK&q(rHq8wQ5%Zt3oBknW+Rq`QXh?i@P4dB1z_KX88MInUaAuN`M?UsQ)zr!`p~ zZRb^y&OMK=%uR~j+k3Qvf+FLjganzy#9yD$72hK>udMmpvSxUlS^+04EG+zhHXZSp z%>1|y`5f1^<9n)%y}ez()V1H#v1J$gG80ZsPtU-}$upV$;fU;RAO6Ovr2T~hF{Jy$ z7QV2dI2t~d=5XbLHj<(#y7t+4kexdmkvnu=Y8z^QiRmXlP>3h7~ zCMpm;-4r~d(ed7ryD~)K2>NN-^6ogmWxcdGOYJZLaW;r{edShUL`~lzZD3<&c9y2! zYb1rPZ6hQ>PXpZ?POzxp{f{A57HA=E2%|{-% zUU_mOa9W;48tUkf%QJ+ijzwoqQtL}^wzoqG+h!CI3Y&^Eb6C$aOQ!ZuOM?EZ@VY*v zN#QEMF~v?Ym`{}M@@EwBU3?%i2Orf#ialA~9@84KS$le&#P( zXiLSQ$bLg-B-~~qlEthF_WpZVk5?b|_5yTAnp4q^%tJmYFiZoAW^5+2mDD6N|Kb-^ zgcuM4Xgf;8jlQ1jSwZAMT=@NV<+JfzPZ)3*z~Vo0EydL~2k4o>z=+(c%_FSxc$3w< zA+g({1m9FqL?MK^>VBc=dNzpCP%Lg_6krlkXSFcSZaU&jV(Yrj!NOc= z*%J)=eqPPMs$&Zz<3^f7JF%`X4HF(5MR!l5fz1V?grm1GA#x0#OlQ8qvnM|p&VWLh_cFhNwhsCl}{(?4NA-tf>;sJ z;s)>L<|lkl{iVPb@!J2HKITN6pa0x7Cz~$+a|C9Yr?=f+!va6`JPV}Z<5hM@_G&YQ z^9Reo^`zVe+xhZ+Vh5Id!Q%^nM*_V zQuQ`UwiIV*wp}3rvP7Hta+1|#Md9~!v@;gx15r%1W+Qse#el+}!v886i)lyu7qG=soKYhsERuZ{V$YzFhd^JIBOp;6P|% zf?in2oKbtkE4`j}dV3nz=;fck8n9D_P8X@x(DxcU=6el)o&9E~QpLIG>w*QNKIsA25!IBPG4>b7HsEw#NUqqV;y zd-S{67+`)yt6Fh?+h(^UYuo3aoNKtvEAJ~YJtyDce?iDV(kUKt!dENGXTzd}E|C<0b^Wg$_J2!o(vdc`^*fxgN3@jUH;U zG^{>{VM&k$;59UJVp#-u99OX=t9&sf;^6IxP1EtW)O6SVe!KH_W09)0*gYr zl%L`YaI8BiD}L>=T4!cjXJ)k~8kfHPpz3q?@;E$!{#N9RC7lN8*ZTz8(ia6}U!;>^ zY2I>f2gKi|RU~C%$FV-6K>^VMXg_|aZp1ecJvZBp{_&!LBN&};{jIox=&;k|iqcDp zy0%uB4ss}MsGY@TglkwMkhzS6=@YAEZA}0g19?! zE(_g3DZ@%DJMCfDBLgv~T@MFh_4^7vy4p>H%kr@*ns@K=TY1^PMGcmhB?_da^R7Mz zE0!j1%Q*}L>q(!ZA!K5O0EL+?5#zNqbB&P98cWVyA=K)_FE_!w9llG?AD;I*%Up&L zlf5p;h)?RZHQi7tb?)zu0!H;DdvtBAw*@J!gh{~_IrWsTua|zz5xCu9KebYjud4Wv z6`K#fh-W9hsQYpvsAH$#9wEpE*HP-e#myGV0w(unzlNC(Ck~cEU1pj}0*qY8yP$WZ3e;ife^p4%5R@>BuC8D~jwQ^ZSO{`#DVJt({mZ%wmNrv)gv=Z#taDN!mk-JG1I^EEF_CplvPJT}S2+iswkdHR#pILwcb z=Yn>FfVGR&1Zu0&v_I-~sA5eI@|C>joPCs`r?=SSQ0cz1raYrKz^ zQhUXN3`Fo2+JkzQYG%B?^WeiR0BeP~XXJ5P=g-8*04U z_|x^X+4eprrJHraOGEDi-e-Az%;^eLCd=^(?8OC7UcdL`)v7@k4PRRxf?|gKKW0jO zEvF@JXQQ+Xn3z)X?dfWBJfeT*MkBZTj)?lH_QUEj-q_G+E0qwf!%ad!)IbWqk*TbO z&-0Q=14>_DQ925pS)B=CjgPaAjFl*?oz@5z#iz1<3CF)FR~jLbNb0 z2bwlnDumUP@S?k4_w~1+xR?S*{KZe2xpzfC@yi8AP>=J9>b=kBR#DPLP%?G(cV57- zCtl^pnStxkNLWm|9*S_`Gb5T!@OqVnyrrsgsQUVzc>FQ0^%f>ezBNt?w-9I}plLYR zGW1(e5k~LMGo{GpjKd>2`&OW%y$D=k+Wye$)=6UNOjz@;(d5#;5k)`q0>$eNnoSSq z?Q*T@bPE^bxAkZ#WUO@X$ZUuVS?b#rr@OKd1O(jp`r zRP1Cw0k`d;m|@-1fagDdUL979*sw;}-nQS6beM@GeCnpAXsZunICjBH`;)NF$n5=s z2<~`Vo&{QR=Sqih6KKZ9lO+GncEOa!f&8P%B&rhERg@i`zn?M_W+@y;IjN-Cqf21% zttehuY)P|buV%T7MHKOQ!T-*?ZSLGG(O#}o(wAub-QkM+6RvaZWaF)=1O1Tn$+aR&^6vKRO>@^zPqa}Kt|oxn8z`(#^)Ll;{HPvPE+$smxeD?F7wU; zJ+x;4@`8f9qcxXh$i@nBws&=F*^wf9w|w`;Dh@lEc0#us&IQB`w{6HPpp;}iBalHq z9@V-~qq(8VM~C#i?ekybFA*XdxB#^^5=_(J6NR*2I+^6Tf0>zt7CKPNt$zzwZ^qRc zHO|Zm%9(VmRFgZqd7W;tB?nz%@8b2P3ykCh?`^a+!~-$~*0<&NRe5NSKl`| z)iI740wV7E4u4`El^C`Dtng1d>;j;!Wkr=G}jqn z-qw@TjxQ*2-}aY_nYj65o5@121<@i1U}|j2tWZS)OH)p3F3u3Xg{C`av{fxyC{hgJ z-6=%bq=*Z;9-TlMFwn^2iof%;!GdOt_^+p)@lB^#eY7+E~?b$ zJ$YgjNhx}a_&}`^9}&^`NTkXP#-@+&u>~D9N$ol9Kb~oHwap{Lu0jqmJYCFTG~)wf z>H?cP=;`4`jZ1tLjn30f$*Zr&nzKgEBT@$yI7j+Wfe|=#ym0y}6a-EatW4bZvdzfq zEbedha8Yp}Imw74i0aDRr!W@*RNY_0+*DtmDQeb9(l2l>Fj-Mhkev5sU4t66rTW^cUn_)l5%%Dv~W9jfW zgI0Q62YJhR9vufTwl|qv1s~+t2$KXNBn_O~cNQim?o$6vPjQAC*{BEo^&5C`+WCfE zrM^c~4XXx{^5QfQzM2wSitk7^pF06b3rqqj+6`b87wfG4B#I#n*~LJM%^i+6sf#uM zwV!H7eS{L1z8?Al&lgH4P0njTz3*OEgCS5MpS233#NNJjCQ9~GF^1Yl`%YBR4xX&N zHb(^JeLYy}zBsXr+PdMJ#w=%0;!-5Cc{rK2%y6S)n@Cj79yR}$p2D*> zMEca8*$a(WZsNLHdodS(*sO7qHL+FbCKQM7AVbV#(REi?fCX%ADM%D?KGj?;;Rq)k zVw=sZrm7d^K0eBo&tDNcs$}W#=i%Sx2WMTbn|#PaO}I>hJ6)zNx{J=E0clzrg7ls)UQp4ZS_>_fpy|Jj?_&1TqA$Sw3|tU@n)@D0 zhBy~Oo$1Op5Y}I!=ZNq75t1E_Nu5M!O-VXxpJl9d0v?FFT>h@#;`8lX{s-DA@jVLU zM{?&Nl0JpMGU?%@Je2{p_)8W$(wDvh?b*CKd94kttIwx@FpE!X#pT7HzexO`WDi#O zN2oi910d1LGo?wpshT(NOpA8f2;w{K!?L=t1%0Lu(9gzv!3HN|Y6CESXmb=mbcVEl z>{)z{<7lQLA5Gl(`A*S+*$ynDq@cn;PnPkz_o%vY2Yjhuo2z2O<$WAn>PH*TLi8YW z{bKZ6+<3HiP5lXpzAGIgY2A>}s=c>j<@wZQ8j#j9Ow1a&#Go>3dgIZHtSgHN7$f!w zd6du(l~GW!UNGpL^?U?Lei@ywF{jYWM5Y-kJQVf5KN5e3Z@`|x^Tf|%t=UBgw8ar=f4lJQwPK+7dQ08agda)p zO(B`PYgx-%vf=V|E;me>T)~ez-7lQN#E+*7fO`^E@IpNQ*SOPJS*u3qt4+fCbU$&= zF4vm)N{dadWxri@k9UaJ&b$tU*ljwA1iq+VI>%cFJ7VR;^+U~ctY1+M+r+haJTos2 z^VF{4Ld@i$j`C+$^KLg|IiCU)pE(p4kVUGd?KE>Xg?$eO+G@?lcw#a~Z5VS~9D%lh z;41Af;Fcn_cPBXb@2+Ry^II!TBp^zDfr(!lYPLOkZsD^JhplU4^X) z-9$gCdf+;PMS6mIWWeRO);~DbFrJ3$-KE={x(fV7;9*W#zNU2}ym2C?YxlmOz(oAp zMwapqL+DI+TQlzgg!8L(@C)W%>}OA2gn9iVYw*dU*{aZUQ9O?B-rE^-e7 zU0ICE99IPi_sW*a<(FSPZeD$aNOYbYq)O=cGXpR|)DQT7fm@m1)Rwj6pWO#oDI|VM z=oz(EGFMZx`x5{DK0Fd|UG6!NmI1LK`JU50`E@^P2FK~+SF<3hcZ&?$eRL-k#fa7S zPoixfx%MX#tdm}EgtkwRtW5U&#rd_jxt=orxWbM>45DUOUR6p>o_|WsU?3A_WmE<8l#ZC-8Cm0i_U`IWo*t~@+dk$HI)tkcG`v`S_ zwInzX(TKXKL{8a#9P?HaaWcCQrgD$EgmdQ(5uCSZd zy4$@EF&NHfrtUBgJSs@+y8gqaz;g7ZPN@n>E3art#eQsj2 z<-6+B!}xPyyC_z3>A_DF^X{#{*3;hC*%jOCReJs9z~gF{w6HJmnW*yH!B&pT@U!g< z=Gc`%{AMontbZ!Dpt6qVcYk%Rf7f!B#^?!(StRXZBcFtLk1a1)0{ zr0(?nX@=|{wCb-h+e|;RZ)~v%HX8U=z==?vcVcCancnjeDK5(gar)lDhSsPWXPv>f z0J_+#sV=MiztdN#Y^+EiqKv($KrriFwoVgtWdWz7?c!_s-JQa~U`3$+W$X|)M}qqI zZN1QTi5g2ll{C|>Zx!xHCm6#-EB0Zb${O ziY|%QbNGe``sW~Qj7Tu~GeCGr+w=Ui%%IO&3CT_9QM_UIMF%O~s~U#dpSYiUZ>l%J6_=&e8(s2C^1zbDC9gKqiS?wYh1 zo3(L@`!Nw&jF6n!6DVuBOLLK9DU>8FoT=Fr68 z-a0$b2l01~8m}>ys6erym&OJ+v7Y+|ybhR+3qn?k)d(T(4}y~di#n`_B%px7OOKwQ z$H{_tsKO)}rEqP>u${JZ??K8Q;&)6l7%7UMQVfRT8$3OP4M>ABhw#i$;}ZE{$Gs-y z!B=9Z(cZ>JHP^icfsGtD+^%{b;ez(rua3Ew8{Mr#1BAg%!t70Zx&s^@@j%iq25W80 zahH4PhuoC%R-vyC+qq#{@n0-D-@n7udXp>}=tAV>{XLtS)Jjd!7JptsnfU_;H#(rW zS%w>PNsH9r{{pFQbtZ}2I;urVMmE->Ms^*69tQXr3(xS-Trtq({ z-(?(hCwdpJ<2TLtz8}doNt@)k@5Os=deB%BvG|EEWDZ2n48+`lsb;xu6 zYBN2@ctDZe?)CGbb9pX~OkOw{0_O}00I8{uIlrofi;o^#?oaTuZqsfwehwP(up3|0 zaIi$DVefFMrdum0O8y*_&lK_3&m7WF8AT+cFb*aDF`DHzaXcUAa&N zQg^{*Ghm;0(McX>3|cj3VvYjsCc1HXQYkj`flX~S zHE_-7y`grksjT`f>_FsTs}d7e()=kRq3nCfsGvlu+;VDjkhhIXa#LNquBOzqmWfv3 zcVNI$JeuDN;#T{!AFRa`wqCSNG?X{y_B?3#6aaiRF|A78Z`Ut4p+n1M$URS-T&#b8 z`C}TXzgLNj`|xhVh!rV4Nt#Yc(AVoWFA=+asp7;1%Ej2`c4VS1xIR}JK{q{{HeaR0i_XrIZadOp%zHmK5lUNh`V}cDebW3I1zxUF6p2%g2npDa^29A z3$4@jyW{XdA@ZJ?T2uz#aYXv?cEW_J(cM|aBUXxYZMEVX430NZzJL5^!`_g)fny1{ zlv8mekDxEN7pIZ09u}%*&%4cCisJR-g8ByLrFNCqEDLN8;QGHp2G^T3So`oiT&TU##9Xh)-Grz)z0?ADEHCB$Eq}fhb_sDfesVCg=TM zE{dKOV-|O5VJ$~tAG9;ZJtv2apMKZJg*CZA?nk}j$lT>LzVed~Ue5(1HzJ~CXZ^4n zI7pN`R{fOxYkXO|1NNH4a4x8tmw5ExKUbX}FjI*yO|P7n%YCaEK> z_j%=XAmyykdBLx5n&~WQ$MLTNXQADrN_OqhBHuT;J*D9~4F8iv`=&4J_prg+L^e;= zx<;C)Ky5iVo05R-z3cXc{$m820Y>@FyxYF{ zp~9cLdi(Ez5?3$@mTLNZpul;p&k5|Xp?E3^v!e?0H}6sptyRDqo^vc}> zmSz(27T9oKS7Y;81^)k_WD7^6`V>Itet$N-$@}#p{%>Ve6BRl@$)jxUh!OBpr8$BS z-lf*jLYyDD;b|zzX!lF{My1i=hB`C%gcCphm&o+_2#2duoG)m(OPFEm*?>{9uXUoe z{I`&bp9F7ZC%60j0fIDHNQA%P-5lmw>4u0}rSBt~5IF~CaL)ue4t8s^28XG7p2!c| z3p<=fbgL*}k5l~~l*uKH&TN}Vdv3Mfnqog!lIJ7b9SjmdSVY4qEMxR|O&)6f7MmErFqn2$EN0O9h_B@<2j#d`>UsxtxD^DpQ}u0bZP9eo-0UL zz4sW1ylIS|KpG6By&^7x1w62J&p0|ymzgZR(danDoIi?N((lLcD1lks-qjNduE*Ir z@TM@qoE>126nR!O*>sZs`UM(=Yz9_+mhBk zO*UJ8k7)l)$ntWYs_hvC+?tD}#h<^)dLGxbVS}7YO^Ans0gKK$NnIwFPGkgHu31gb zw69GhtdFH|Wg^j-j)!t5!IFc^cUkqD_OX|J-vf2d?u}!L+jrVwHo{Z*hddh@=Zk~g z2^D$w9bj z@u~KuXAi=qE7WZV5@%456i1%u=SuoE;AEf}gAo3Ai$FB|-nTX*mecckCEKg#@sP7B zjHWvE8}@kq4s`q4sst`e=6FQA_Yp=Q${N#$^j~jx>{M-~W$Z?(r0fyf6!3(n4Q@hW zA$S)ELPGa6&2p4n=a~sd!N!Vn|LRKr4u>GZy;PaNw6f=iSYHD53KMLHw#G0f8W`!?)7qtg1*vR zOip}X92&J%i(a9u;Y=>HIYu8{y~-l{;$H_Zblcd1mg_elTnHyP+11)Ryzv3bb1yev zupas~*}Yy#XVly8JpI!4sr`=V{l0ghRndj;v9&#o`^_;>TVik0aiJZv z&(HKqZoe3Gc)fn#FKV!W^68W(&5fl|#2NsLVx5d8eGQ33<31Acgf~}gNmO&_e1S`6 zwE|4+rPQTa0%MTsY*yA+K=biE{KSdr%8I&qTp48 z`bE0_lv`rJ+q)!`)&j-JY4P#usk3`Ntc1@9f`vWFzVq4pRW{FSG?B>FF&uV`Q9W&N z^4;?8A>djXYhB>#;kGvDw*=aPYO$K1ne@Pm^*5AwNbkqpR2uZdF#+Lvo=^p3fbN}N ziG>L^fWcRmpecA7Xa~{^=2!WO17=SS& zf%?`dh02WXs_Br^pXKcaaW2GBw6#fXguZB89141NAb;bBdb!QY8;1F_7u;`(3aSF@ z*r$xfZs@n~P-69bHs!sEY@e|D=`HSqHU~PyOH_q_dQz>^02o5Gq+5iZBLg~#JSZH3 zr=DQlJ;h`QgvFyle|i*YPSt>Xy2mi6_nmt*u;-x12}G4DVhl#La$WjHh$|`{F(GZ- zf#NXtUPU!_99JHLLetw3JbP}rudFBm+c zh6^~e?t_DlWnb?CUuFqkspp*t{6^o-NbFqZNEIE(HWgJRucP2))Mvud4O$mmeEV$# z3m!~K4{Rf-sHbZ?izugZL@Z$7NM8ygO4|&1hQm$-BHg}msZAk9vqvmvKM* z;<4%C$ZhGsTzKKccHzPv#8R!0{`&n@l2Iv-&!2;ux@f4pdB8!Nu3X|=Y#MOiugy6> zR+V*633pU#fcl#T<{aBSq0ZrbW!M*eDKT^#&?j-4TRuod_I%Q$NKj!^`JAFwMyOu) zo9il7ZirZgZd|33LhAeWUjkj+2#ZKtGyO`TEX9=t-g0V~oc6jjh6r)}yR z5}+?eaGJ^9yuD9g@bANI>A+Nik`r$<+E%z#cY}u$e{kIxxcgdA+1+)x({$2>12Gdv z72AGT(40EL#0uShj}yoSU=g@ADet+;n42qjvz*qF zRKs7~X74=RH~acVNGT;6DO75zjx70KN(%X}K|f)pn#!AU^00OF@h99#o@J6K z)57C&P3(L&FO61@l3ht>S2V_tdiQkYjxfU~CPMZp9%|(AluOZ~VKzv+8e2t_pD!kz zju@5_b_Ripk7+yX8C0^vcdJV`9_$yuQY=O1JY%QE+2;c;>%ad>{>0XMsZ z+27|h)qnI_V*zbRcuqv+=|S^x!iHuS=9bFyo)3&^CO7`8iN4T8w<-%PakKG4P1e+> zIWE(3(C|ooRPJLj`VA_}uw`Frs+H3=QByEHp@>wQDpicOm7DO})9#}d{~_*K{8I~| zWHQf2r*R%0htHI3@F7i??wlaj*?1bx#4x>^<)2LwtW-9(29g~3KscfIHS<1YsW-c{W23ZMLe(n~8lw z?YB@s0#|T~o(qfSfq8Z5jtVTsNkLg0R$psE6Gx$jzs%x_<$uw&Uo$=K$8h*fGXR+X zUkG#rL5^LUxhUq9mve-0tF&*ry{_YVTq^^J&rWGhAQGKZ-!Kak(ay@fe@UuaoS@)8 zbxUmOcHfC#(>JMkFK2X~qfw;=V3qoKh1N*_q$GMt|9B+Dv+*QKLEW>Kj1YOM@h}L{ zPgq#Pn^Qoe%NO`pIPpl;ymEdUkC|h+`>W*%$zg0w1A!ZV28oLn)FV5*um`^+T}r^b z;R)1NmF*qp=0M#K4BJT;q6_U$@3p!6z_Bjx7^i1%2t9hA#L;t#mJIqUzFb{9*ME~+ zpgQq0q%=xb=AW7A9%T=$q3{QfD*cM~jTfo^!h>sQ*4EM{o_S)N)+s9~@s^DAw!fQl zQM%0-C&S(K(G3rm4r!f97SGvj%u`S8jqEE0 z0HDEpPtqOC@M)ia1sfLkd+We#Pxh4@mN`I;M;uLJc3?+Uo93*iR3Ks#c^{Tj#H?=n za?WIfYGvmD+76DWFI{lkV_w&Sk2E`qOAD1X4;*eGF;SNdehLt9VmXTSwSC2dbI|hQ z;D*P59Su8>>EmdiKcF2Q8b=+Kh|A3CaEGPax94koOahH!H0d88MWYj~4L(Nj5%dxG zN#T%ca@+j_4hUW^2pC(In>m@5d2TxAISQct@}%QPNi4KMr`<1!9L@s{ze@=e$)3fF z{2wdHfMQvRm6RfegCi4@7FUOybNR2nk)vPvh`Kf{xVcRp3>r}M+53Ia#8u|oqIo7R z=(c5pn`7)f=Jean-RSkU9gIER?ds_t{_BPcU)!+q$dmu-KFDS_AIrVxdhapCeJj;e zB)>!mj{F7Ac(eV$3sy)D$*!4ViR8WwNirdJZPOK{w5)Ngxfo+xrx*+TUrevo6d5vn z(-$NT=jxXBUH11Y=0z?-R!s}_X8UEsPk5>rbP)m%JdrRHPW}Qf&2=d>hDMh1} z?BPRRfLeS_`svnGHsqMFa|m?@!bRhi0>&OJkES83?j5R_goSP6GZ{6QOe?yV5vPB$ z7W=OZ-fCwLGJe*un6y)5W1-CP4TsU7k-V4c?=4M6*Y4WD`w?Kzfwg`5ZVROviMYQ4 z-y|UM*`yeAg!U*O@0r|?k4pV#<3LSlCj-B(asB!7kt7&V=res4 z3Tj;zAFU>!;~ zA(gge5`cg%7*4JGV1c?jPr~P#J>|9gnvx_x9kuJ43K6N8*JA1pFz}*0y2|2uD^Op< z|1(b8>Ap@l`odp`LY;YjqHs@M(B$`uM&?v;V8v6(6e*DgQB#z<^{Td#G+0(KyDHzDCqs|>$6_}Dl87%9zmPX378$Cbg{5??bJRvQ=TnTQusm1Rpr!lK^k4pGwB{dYEkroP8MlRVr3Y^$ZZXOP!uMbMCopEH*)#%H#~Ul zy=Du#c>;Ac>7S~IUARe{BZM2=!Ib?_WHAs~apAh=`)87z=tgulBFLTDd!zQ{p91Q9 zui5%9i{^}9_M#1utp<0YVuRfw;S1_o1Yl9{d50}|`WEzq1?o=URK>**wJXDh{SnXn74NW*3xVNKxbx>?`7W6z3_li5~Y#O1s+mmZg{4u>i_LTX4xI;9f81+ z$;H$aRGBQkY5dXMFuFq1b;;+Q0^D?d07dyzM02C{u9=eeWX)sEW7@B{Bs?pmlF|#W zq=z#q6VFSATitRW9(g?Cr92RLXEn@g;ii4(S< zzpo8ixC*jdVJ_+`{Hr?y4*Guv#@rSa?)?cde{S>Jr}rV=Qv9fU@$WTxkV`wJ5{LJ_ z*5P7R;7Ik3EPGHc?7|KtSlv5SaS*@Ypb!N#O2Ce7Zuswc{Vng8co#5xNpQOS6H>RP zM0YJKIz+)0K3&PbN`@2YK^gx>uT-Cmz#8sSyeZERbz?+14*q0!}_h$3XowWUb+fL(d&1{(r z@Q&^zHsQ`6er{YV*)1sVpwR);@jiHCK5npL;~~Y5byDy7q;zfIq4KLHRvpnbFMzr8 z(yykp1aml(wJM>_bz#*>dQJCVX@W`67IelH)9vP3Az*bII&0fb9Y?TZ7*(UCW5)D9H!-|b!N;kG|KVy0`BkCE<@ix^hLp^mET zR<4CJRqa#`3m(ypk(p_xYN-;0NBey6OK0=ag;$<*bO`tTZs9p^Jq+XN@NBmi*UB_m zAMjqgOCY>{nr4Gu90h^-{;bUb8j<;ca!t(#W=(+>d?eYYg6_S);H^YyC578z@O0Y$ zy5yEO=p}C4uQ0~UcX!E2;LO?BD|As^bOfw%FNwBr!srf}qYB7vOwbXVhG$yN4==eW zAg#df$Won*CNinb|E~043hpN0#q@fVnI~4*I5OqWuLF)tp{Gon<9a|l?XNS&?xgCV zXJTXuUi+!wg{sKypI7Ekz!qSWnkU*s=8Qn3Zqc*ulr& zF<6qW4EeqgXz+(5!5;hPoS)7QD_8}T#`lg}^Vt)X;h#OqscYEat5A?KEr9?yQiGDQ zsSlYn?24zfs@9EkegY1eX+O<_jQ;Al-|QGHQ*ODTrzN*YJ1rA!`|V3rhk!pVrFE9X z#S8P|hZar2SH>+|nzl8uAM1uTXXsPmNjZG>n&$6A>rvM<$Pbs5gBg6Kf(FQImJttk z*8ZT&``v#SdHc((jX3|8Pd=wld`{9M9i`SY!Z>XNqLa=bUZJZr*hkJ8I<0Z@KUzKfE@0RlHob?lOD^VnrT@lNzT4gn#9_#0?w`; z8ks%G765_b$S!q5<_!<)-F6pfC6d&pQsf*yO<9&Khr$iT7-==4w{&ONq*|760%<*< z-|!Gsz1*<61(P8e?AGp|eErxd*_It{MYCx_#^5K}6J8EzX2ERq%b?WjU})Hlc1u@s zA8B5i@BhdD{E=1iS6)$Z;!Az!G8{47le*8?3QlvreDyMQ|H=u*?Iua%MLP0{_UTnsLH5><=Jf8=Qgd{p9(=nk+~FCP{1Jr9(93g~1k-9tsJ|kyTs(X> zaONKhagS5YL+ilch#y>IhV;yGiQ8uVwMMfgDh?-?K>kFez*ZmR=%D>#QLF)!cyB|XsJ z)jHe6X3p#vCWE(*!|@6x|C_U3XszBFo^u;R@6tbnDMm_Xivk0byvLq3!`A zWW<`pBwDUGKT}Y@aOke!GG!5;G65YY-;*Qvja8(=%u9P)`0fAws)Y|4ylgacT~nGx zR7*Ay{V`Z<@6S<+;X+u`oE($iZ;T0C&(kIbGYsX}aL~Ag8XKp~3I6jkWBJIxJ;lQU z*RQEW9tZkdw93AkVo2Bg`p=}{@pv~H&bKv(flzIv2fJ)RGf5k9@!_`8-zK^F6jk}u zz4qF{Uinj#_8V_o`O{9-)1t)Zbe~qOeB2eSIyyH7@Wn&7|0|nU_YPJVRGX~WeR9{- zC$E02)v(dn)DA5u(j!_AxNqRM-=y% z+i%>}M-JPNxIPM_RkYre z>F zwb9ye+>2>hI0g;zUAZ6gh_AIM7EH=4qS4_IEM6f3DTZ{>WjUJ^9MNo1zm^r-4BTHd zV{&|W}ipPijyN^pwPNm^?C8+Ka0tX1ni> z2Re_!V+vn@8eFaDQyQ4n>3KY}G~HVOx15hfg^=SR3Nw6C6IcKAKRLQ8w(yJ*XLJSh z3;@U2%1_P?%dUT6eJf^Ji`C;+#aFAbwCS}+P`Pa3N0tfku=fmmyx(2cxA>Y!3N1EAAjXDKk6m=|DC_?RmGDYnSURiqK7lc#3BLaMU9ensYYub; za&O4q9oA%JqMdBPiT{~ty&H8tkhp5l{JjPXBV5F5#ql5eQc~l+3rcnE*h{{djqrh; z*;6vDG|{iS8~?Njt~DJTPyGx2?Gvnk-xXWkhRYWJCTPxbE;}Wv2ob{=8p6>%0v;v3 zV+T;F`-2R^iWMwKll6)bzMnFcW;GpLn>pk;e1!{Nr89YZfxX}&K01hgXEN1kz7$>j zljrF26JlXZkQiR0Hro$_LmvQjIBq1S+Z@h60TYz(#IuPr%`8{+9NzoxINp$YP4321 zx(NR`8o)2~R?3)3(Yiu&-$KTeP&4QS(H}7A!q0cOE|z!7ZP?%Ipl6)orj9r7n4Wi^t zNG#6xnb*Rk4a+6x>CRT%g!Y4GMjJlI)0C*(`15?JC^DEEHstj;o46ahe$=S(ki3Ry&dARbG*ilO9bR&ge z|G|LEjLXi+joM1%UZNbj*Rg+Kd}8PxL;_ivwVAbm0+E;iO;;wbHL8t)_W6yP8mG$4 z7dFmKNiV$uqj>tNz2W2RwFkM>OCdh`JsUv>m0V7eH5I)dlbiQTwLWg7D}4VlPX*b7 z9MEECMXVRBsbL==G3c(jZ{yB#&0roICmQRUPV4a#_I62w8~9(_M)=Px%+$`nm96-Z zeYb_CqgEt5)2e~R|2GR@)Y>OBE*JSE!)O0a`H zQl_@Y%*p*qp?IFd1RR!#JKisW#p`wblOT)z4Q%sOY-&*_!Ygp~8+PXCdkwYd0L<_! zF){Z6lqlM!E(fAz88b-%7B#qwRcG0QgN(q;t)aElRQENAQBK%p(VIM=??lCArlYn; zSJU4Ni-*CUB9yf3Oq*dkLniWS2B~ zqBYlWf@P$qBI>H>;SqySa7ye%Z3RS6BBO|wpz2KBUQlUSJAS)#I7N4-y;gY< z4JP;+4_&^8>KMXW013d9pRiMEVVl&ETs_Co?H9xlZCQ}vp{8GVfu?hj%PQwVW-Fl& zq>rzU#_uRj2AQ*U9gc;9vby~yc_X68QY|~5rDvMcK~-qaxB_LN^VptqaE4I6fny3D z=B|$+c%vYCej!CD=)GmOfs{4bkhE%ZLooY#5ocSXW`jX zddd_b=ke5=Tane@@I<2Lbsl5l#8TWCp7J9yrGj_{P`*=92pk@6>3LM=wRBIEjeI5_ z>s)qN^6chk%J>LkJo`?$z=f{J^%e;=+Ge_ngNJHAs_;2E{dk1xkk=$W8S?KrRf=yn z8IAu@2R|=}heW!H=XM;MPw!I zok;?(w%&ffFJ~z#U%rcfP2^n4QhAEAea=ps=eAlO#HcM4E>6x&QM`lDa6zr|_=$I3 z6a5zgiGl+O3awXkY6>mogy}y!MoJn%P(1!wl$}`OB&B|-_=lL2WeAueD^@KVXZ2q! zM6cfqiX8h&gLEH<#P$8^+24_8mWk2Rw;H}?V(h{MZ{h$*pxkkrU*XizGOqn6NmasL zXFaoVE2)Os{+UBo&iynAiu}t=VCUFu<0+F-6p0l3Z6vro13{`Gc7?9S^M2b&=<&6O ze>~PJoL$XtkL6`!Ty7X+QE1qRCNOGxwfcB@seRj}Z_$X*v1mZ? zOe?-7u+g)eMWQ|01ChkMa*7MdhT{`1BV5Oz5fmP?;_UD4P!gjL?Ej zSltbFJeUPAbg#+H8vPlGJ^tlmilWIu8;6Wd&?o(T8dl}CtE6kQ$UECQdMUZ2+i^o% z50z)B^7!( z7-}AX=~nR&u}5MOVCtvVO^ZcF%AZm3v?v2IY_F5rOphmqsZA^2aG498jegOUx`^1z zJ1R}v4eU0=EU0{FnaKWvLf6uPa`k@yRlC2OMSHbMTVJKX5AHQ|;zJO!4kh#ft}STs z_bZM%bi9`Ogd^}iAFGo^=xBOE5cjFm23?shc(UZ znJIZEBrUZgn1#l-wEAk3L9T6L^yo;0@a2s2*q>OcoL$XlYvR>Rsj$s(+R9ya^;u?y zmetLqp4?#8F(T}1dfBLou&4MX>zLj z3FCGSUwWX)LsXp?0zA;o=W;191zF4O2~>Gw|Cby$VI&A&98fk93>YawLHU-hLuVzn zRg1SUSvRG_370`ITKRPt*zPfa0DtVUY^Pe{rLO&YPyc~SXqty9xV?B{!&Y$>HKlAN zEm3J#VeF401fg?g6kEYAqpQusK}P*B9?f1fbRl3&+{0&a&! z8`wUAz&5{N{XpeXcGs3ns~0DCskFLT$rjsXt)X-M!Vn&M6}sAPlO`Jkg96$DRS!*s zv=lZ?J%jn~TC%@kxXM!j09gmTL^qv>0;nrta#O0C^?7D=52LnUu(Qrd7=IXNcWC-t zMYo@oQYLw3_m0xKJj>hHA`0cMEB9xf2gZL3{nz&T4^KvsPTR|YkA^qF_-ruhW0w^^ zEK=Yf-3)$mc=D?t<4_4_B@OI>)(6kYt!_Z51lTZebbJyqcbc;2sPh)SAwCgZM}s&Z z!7!Dh{sWudLS=--T)1heJ|p4Ahp0<1^7Wnzv9*9wx{f4_)o!KncNfur#}=%Aw<^Cz zMMpu=wOj-eU?b5H^jZt)x&We-&zEW`4I2T>h9GoMCk_2XQX1u{ zI{|~55^0gtmT<}mO!KLbV9439GGaa%&3rtH1MVl&Q!Dvs zZOVVJNbV@}2=~8>wv<|qdVdkqR4o<{yD;yJMTvEB8IW{vO}j&3Ygv3S4Jdic5P6gq zfJGY1S4S*b7}2akafl5X26b?Zxd(vwW*a;`GvFwihS?;0a9WC#Qzhh!q0WD|;HGgf zgxLb*Ah2>M_7NJmt)^zQVFpIUA|<)Hq&M2&P9$n_Bc(EcN6bd3kq6xqsNk}lvCBouk$`~F&AJj0- zf*q}{fHFY~Kn{IyxGXk^%XwkLi zSMjo}5r>~bzUWBP(EPFB+c+wW1VjIKme!E>qVBlqq*pT~W4j=Y%9gfnOXJeal!m!d znr}k}^Dh+jiZbcIs{_8A`n6ZUYR#vt8Tg-<>6k4??4kd`hY}KZ>QFe@iRJy(s3*5z z8HwW;9ktCaPDy;T(Rezwlhys}#aiLx>&IrEZ?GdOObY_IHxvaL zik6flXP~u_CjZ3M6WXNj?28@CPCWV~i5a7#_ERGFS&KvOwyTf(TnTnWI5;?}Qor7c zr6nYA+-?t5+k1)ex|q7|l`?$8#$<$cxA4drDA2cT&43!@i~@PzrGMzoRFL0m9jO%+ z6da)zfF&d(X3MtnhY0cVygG(VI`b_i9S?&=Y@k|YNpY30LTw-;sn zey{{V9PdUSoLw`L4)9zVO15t7UEg1jR#ynP(*1#6 z{F>Mx=pVGQBJ<4%s5H=W#OoC*(vOLBpQVoW^Wk}kL4R?XY_L=j^C7?RxOYRgeDkMX zSG;DXBO$aYcBef@MfZu^U{fhbefWP9Bw_iVOqoY2I!+AMbuU!S9yVTnc_`nda@t1$ z>K{!0k{AV_o9@6yVQjU6vO6{s3se<=Z80Nm-^1|>3knJ%1Lz%~b9Fp z8enER5a461H*i?_vt9Gi<#Uib8gAF#=YB)a_U+mi<2u1WN39ork2ai8_GrAykh*;C z*pc}v3!~Q&$W~x{ZiYMMd=o?nY2;4oWJB>ky^;SXBO7H3fsq74TNUqksuK&6CKjp; z-F__JrkCW)ZGO9~;tAr+IVsu>$6D2aLu;r3oA-Ry5k?Quc}>6zL|8~$JCoaKAfGO6 zu`exuS^|38zQq3om(Za2(Ma}#Pg}=l_$nlMr3}HB&O{Sy8i6I5xR#R2apR?0q%%-g zTR0eH02nqENM+dF!Xs`ZMy#>@!_#(*D>nSRWiLGV**&g@nCHn7=F6AzKR5`$FXi&7 z-Ej@S9Sg}^w$BeE7|QP6njRihe$c8Yd6}iUPS$>IqM(vo@=m)8Cl&JUj-(?EkIA}s z{FO-S@)1?Y$7r7H;YHz%cl-21vMZigxGQ5T$!YC%X)}1+t$X7V@Vj;Ua;TM1)Bc;QYdrtMakJKPQj3F* zq}d#$wa-gD8&+w0NuGnUykA^y$qOX6{4`0;X>)cSBB2BEyjFm9?#E}%vMt|@9^SPH zznk;2u&xemDj@_Wi#v;le!2%OQ*@h!Mq?9Uo6-d>Z+~!qqNXS0g~Lp&58(Q{;o|9E zWx-azrqB44iaT`GYKmd{a=aJy*1Wqw`y3R? zqy}zPac3bUnOmv+mNQljCdUmof&7kQSdD%-luNb2rU7wBdCX*7GjM*QApL2|$GajA z{Zm??$%7c#t>oj)9^QI)J(*3YpOiiTm^Q?NktXHL=Qtca`sgp_xt(aGj%%YWQC~{V&G(#WrL8!b%aJ5W=J}{6E&(c^7ioFhdyjFyPx&=Gse}VOj6DA72 zTsqA&v_t0XSN&p1p5q%0!L&lQvDorvKd1sfCOsFM6!`rzxxwYP0LIS%>yfMi#3pFQ~h4g&|Ky=D8>5{r(%Uc{p#hIU60R z($li|`Zi5XZSJS4-`c{Y19Ni~=^*(JRIN8xR0F62CBFqml`mId!2r^@$-8O~0`-bt zk#fBITr)bJZm#Vvb{#&qm#5gV8=t$={yK1sg#)vjMxNDPYAD%B9KCGuQ#{d*Q~tn5|9|JD=OADU{2O`v}pdPjw5QN zje;XLN*E?_-N1BPANs-3A>aISFoiL&)lXAi&+xPK?p;S3K^$AazoTVNEp&vYhY)xR z8=KzLeG#UPdj~FTJQMudB}c+3Ix@XiM=E0fVS-*$2jjV-Og8V_6ad1?5sr_>?TqoQ zeftfhGxDlVw&ejMAe>nU<~piv{N)(ClV56uZBN8Cz7OHe;CwsQSuP~&n?&&!sGU~D zzQju$bx&REp-?c;xLQIZc0@kiUotL9UPQ&szJ;bndoe1H?T$2c4;^97;WGKBz)#|C z|LWS#GK9AUU0k+}FQb2Zx;sRi^JRdIY}Qu%*N_oq5Ez5{*=nLXCsw<<-D17_)jJL9 z-NL9c3+y{6$H0reQ@Zzx2x?%FL-!frkZHt>nwok?n<-VtPKXGfR(Ejw(!rCUf@U1D zV8cx-EDW2NlqAPcR<@U%jBRaIE+ovrFqE9048MYjnS>cQp`xk@JHf0EJCNf=O1)SQ zHfch>t7(0tSZMY1oef;oUVrsHo^z&jBmeV<75ea=HI+ct><&m3S!;XGA9=zcX}K94 zsXk4~Og*bfznKq`6gc2Voonygom1gphjlvxr|z2jzOUD`ZT3RX`Y)c))6gw|Pc1_C zO3b>kq$sP{ti6sF+iFaAz4Pw(B3HJZ_|IE*%cI_3{9e=Ynwy$*07u|Z`GGiXNgSUT zLhc87#PkTAY2_Ac?hNhztWmS1vEgn7v33SFwxQ(gM5E1(fyC5Q+<~fkolF=JpN$Cw zBxQp}tw1;ikJGV62)=+LrLNy+4RfFUfP@{`cYZ29(J9+0Xsupd5TntSX;jAnHvLDX zfqL@tY1m7<3J07Mx(4RN*3vb zk%Q-awBpQ%H8h;*()8<*Z$iiY$4)x|w7C%Ymdna^v9YVJcbv|^Yu*?lMVKs}H;mX! z{v7(QwRA2_lp+)M9^E+MkqO&><^$(lzz+Y#TLJg;l{XeC@8_&U8>_XN{LPVh-TrAB zk#nst9vu>vI?~OHHk!l-*y&@b#9^inK(4ld?M)#`6ygAlN@CVa^5o>~Q7A+?^ThHF z)X?Bkv1-@sG}%i{R|@t%hjz7YUWKLUc*g%g+GtDvl|WbB21_^|J2|^UIf5ucG<3aWehWv6rg57pJaXQ)cH78 zcogV$A&Qd~_qw4S&$*6cLUBcXj{aj&PN+3M`9Kz~t89tz?<2hdP#3{=g~iZ$e9PEy zEVs46p{3!<((nnBpG)^^xp)hkFg*G91eRboi}`lI_JxEqS@*3!{Dp;7?e(gs-~8-t zU$qT`Y++);W8%Gi8qQ+_vpeG4Dk&&QNLW}n3-Bli)iEDa6kKr`hR{_y->hBuM5j*8 zdMEkj-dfvvkn^}cYF{5sSipeytCja3F<4++nDs;d@jEQXK6BW~C}(Y79}3u%I}4sA zjXsUlvRW3VdhX6mnv`SQ#o_+0Z-ry)Bf3S`q)`6PpE>lFk|7?kEV1vg(mBiL98~cDL6?;D9YP2BLoju{$<4G{`Z#y?R3*Y zJ1I+(cQMd;m$k|EWR?7-tjWU`{=EsiqrM=M%0dg9emehOv z;-aF9?KqI_5aceT_ZvF$=(n#u=TlS8<3YO%olHw{Wq*+a_$+nqI30U0 zGW1>lY6=m)(4jlC*$z=74l6aM3+6vYWpS~phi6}griGoVJciQp0-CmangkliBjz9n z!T+U>01+*zOW@erIt1LKB<@SX8Tq>!o$$yCbgKoN0X#1*t&DcWYX^#?b63dWhxZ0! zl6hFvEvry)(cmPiD2L#^3AwMr=2bqIs|fM#=XJp6>fcgA+0jqa`~W*W|5H_utHpiL z_395aK?uLWl=SfP2m+Fh&%)CHS$vK*7`6rn6C~n?iM7>Ya2te&q>45|qJ2iYYZ!%8 zRkFu@78j1YnZ7J!`BDVQSh?>@a#Bubu}yoG?0qUsvDbMhs@lXT=25&%Wq^PU%L| z5htoO7|_u0u3Z|CN?#l@HiVpcNfcEz7t0(}v{k4)*cchVfIwOJ9?>>Pap%T+pkin{ zV&%`E8!D;TfJK|ntr(AX-`+6bI%WTv)r(;w={q{&8lv-+WPGk>j9NTAx7!gXMl#KQ z&{tI6D2Pml#=Arnt8x8JyQZX|mYDR!_^L~vP#n%J5w!Ycc8L`q#{VV-E2o5NEo$d2uy%{gaM!Ohz_H6AKCTH&x@wA;w1CW76-E-vn>ZLkbSZQyJfOpKx}+?)vi} zua)=iU{jpxtLwwW30=|i5~KIj7VdUHLEoQSOHSMbi1|GfJ7PZx@h&&pVzt|}$Bc(% z-QBtUsi_IRs_PkUow1RzrLZ&^(9lG5c;t+&Sw0P_6G!@B+|cW&Rb?AwE_<}OHT`(% zBP=XJMo4tL>W!W{OV}n;_J>~$y090>Aa-c>k}_GcYHy7X4zsksHsRP8(G@R+j#{J{ z)^7V7eVnt?ePv_na6(z>GQl<}TDSqY#Gip2aFbPMoxi62K+`T5=l4TyzlTtlheWx>>e)7 zL|$r9uCA^k0te_7txg3FFHsuSv({E-V306~173nYpck|oH{SF+G&VK$l$_6bXUp#c z!hr11ZO$hIVELbTL<@pb;LnfZ{R*TB(}nXR=KB7n{+|-15M;0~j12UlbEx+9!13vF zCh~coa{}Ea`>l)$4Yczn5bxLWZncZJ{XwzRPbU=i^GthIapwXf+1D+qLkX{Y+5M@Z|}#Mebm99D8(oyOeFL>;jwhwLA9jZ3XN;9vKxD z9DRl6f02JUOI(oMF4`!mm#4|{T=zl;azHyD-bx?dCXOt=2Yx{`bB&YGI9@CzyJOc%#8)XaPlrQld6f9@r32eeshClG!-5U)O?AUWzL5b{LK_PiIUJsB+wA)}0@ z9}B-f6Tx~gl-z%@{&+*muLmQvOdjN{o-6au=XU(E9=K108 z0r?f&NvoUOkOUpqW00C&{x29`> zk-}~N#-21WU{z)6(R%;9y%EZ#00rlQ9pE_|?1kGOZ_R7HpVba1ksy!%cNRdEKOneT zBB)eW0nvQwQ|y?g&ckIEOFQd^n|JU4)u`2YP1|Glz8xv{${Qz3{KHkt3Iv~e&H2Es zB}C8&-{*!2WIV=msv@&BD!VU#(Pov~W!YJq?cZ~~!A{q*Obo<83I(Pb3hB{e-}MT3 zhw!EdJkVxdoEJ&e;U$7FxJhE&9-y|)&zFje?=Z_ju_-W@gP#a{LiD&=A2HavX;IXe zQfHeMJm2+leGIO0YxV|50yF|4<*Wg?M0v^WFHfa3zuO3w|) zKau~^DvYL2HDspX`k0x`)X>03zGlUyDE{C&!Z*EP@2-c^T>19xR}!&Vp-NMvYHYek z^n6%guna1=G}qfw$Mn$Prcf-m+Slxx?q!3P#a5_slC~7tp3u})$T@r z#v!)y%4>VRCKNp(NgsIGs6dKu(DE#wL5ln$@N3sj3CrC^?q$mTU(~YZs4}v(zE_aG znf8_qP32HB*zv#gT(yc>PQmH9b?o3TwCrUA^egvo8eG$OH||o zYBDMFD^HzP>>0duM|fO#UK3dWig;ASh?Lk%GUj|XKcU|9#2D-QkwApS;GmCHdk|lE zLGr$`kiC~YsZRZi`1GN_0IXh%W$aOnWX#d_$7P+AM!C2O+kLQ!Za6-)8Z-s1eU!`N zn6b9wS|mVl8Dp|W_t>aP$9-O21jr8i@No5ABJpwq)2gmq=Y0*T-XDxNvM_Rgd2MWQ znJIpL0c{IB99Ae8Jp4Plj)*Y&c*o#&!c|lwpyrRvv+69wM4A8-EbDX&I5_CT@c(0B z>0G%r-{;LXMK)Sea8&)@j)8&lW%l=Hd)%mqtP@SX^|k9e#6Yy05;xW>#FOz_H9-sB z_Gf9&PmSLs@y3WSCOYqFZCqLmZ`iz4e&8E_+~vX+!(;h%c;fr= z)7}1F7WYl-h7;8yhU*l#bF1xydM(F4jIxx4?#B^%HI&>1n~%OVJKoc0&h#SW*3CP! zy16MLmi_s|DFNL#_Y`r+Gp1i^GMT-GlmW(DHj^3cfV+%NMi#QG+gL&FK_HX)fW|fB z((e9so}QDT;6b8Kw3m5ymNs_5N3CXmtZf1_!*KtUTL%dv99}F}+;{V9voW<^(L&## zazy_gNWS?=7}!XZHX7RV>?W?_OXBuC10V}jAZ>%+V(KTIUw^67*4cG$3j~bmY;^q~ zHRsm;F;wQ3He_P7y^Td7Ko>_Muz3*FJYSjHy*j{zq`R3B9TQnj6amfd!jS`+I~=vT z18|d)Q!h7}YbuG1WPh+*%3bE4T~g%vSG4tLb^XIKe61(@_R+z1Sp{9e{!eh|{5oA0 zVt{%gi!_O}m2=f!L_cXTBk{D$kE~{7bQ=NW;^2NUTUUaHMov+=Sy2mn>#)>4FCZr* zXn8%Xg#X+fSB?oz4RFks^&^+tFKB|Bj(3$514+rwIeXSt_6MA|WUM75I&@D|TuNNo z2{kKeo@&;ru^5#H8vx1o@e?H4)<$&%a?Z^cSLt2ii!cldAi6d7gra?)RAKEn_&uzc z#&ilP-hvwE3pK%kFq&I7?nuYCFtl7w%!DwG+{AMV-|gRynB}Sn-Y0n$Ez_+vZ|UR$&-n?TWn* z2EXtaL#gcGkYsK!mNFDrNhjNyo1S^MC!*#n$Ul^8cPAf<4*^Vovoaur zUquhe7yjNOC*ztD{VBWVto9eSdA<}l;Bg|p`;{22$?*gR3~-V>_WAy<^Xal@SJkJ) z&?MHpBJV4`oa~(_0Im@DkdoGdinXJ!=QJ=95#L_wydHEv`5Ok!Bv~nhkDg>3ovn>; z>fL2_Wh=sd>EIL^$WzPKpV)`Q^H#Y2RE~V5N2m6tTb{1;6pE4y?U1z%rSVOyzXmMc zoDfTI-i3~oHUfsjj&#Icddp*OT@_Uv;lGTaL%N09GTmM;wNKx=-1x1&Fbdi=TPvuD z_t2IWzDfa~OE*Hfl}J-uXF)soj6gwDzmyS%>3xTj*Umx^RMflw_hx;gl0t4{(sS3CpPQ zF@6_=b=MW&H;hrc4fJqQYRf9w>4$rzgdv-Z?Bql*x9qXGLKo;~k%Qj{Oj6NF`u1*5 zLWTUaVy3g)#|}JA@o^JXrbEE~=+-+$mJvEm1@2k##p!^7hg0W({&(c9@Y`dnuJ^0y z{;;J?!nEl}`PqcJcc-j-r{5$qqCcEawlXyJHa1R1s41s4l}z zW(;Oh-pvKh%fLf_*04!ANkh3+QuMheG%}>~_GhMSA=$2vo0IXvkG1Br$L01Ebs2j@ z)qxHQ(+LUcFs@uU@d+s`$5+=M5`7L6nEnAp8#yI}^7YJm4-s8T-ZN`#8`^X0X|w9e$T6hJ(vsMH;JedjHZl*x&0;Y&cvIriSn?8qT}Q;OdO=% z18-e$TF?o%v6Xc`wliwx^lwc^1L94}Q~mtM_O#r5c=+yoRaIU07CG&W%}5OZ>2;ON z43kF?gJpV&j4h>C{V8i~;8Ux|d@_}r^S+wGC<5GI#D35GxEFY!RQV98tU=X$Tv53n z?+7vX>&G~h(9@)|+WSN4u^Z+7+an$i)S$z{OYmwWKj8Is20%w$`qf$`sVhrikfgi1 zFKG`r`gT{L0JOCE^PcN~_jp@*bJETzXLb!KRLIR^cO8eodzaJ+5V{M{$JU1_RY~GA z5PjF!EoOCs*^c$iu*R{gtOzyH7Iqs|rS5mW1gmNM z#MIYRQXB2NbEIG%x6)44SLU~aRC@MJ<7L~5yUVhzO$8)_$smnHyS21qM5vW=!iIM2 zWcXp8qlYencaX00KQEgV6Mi*J0xqP@kF4=<>1Yk#z6%*ZE8LRMoVykRP9=3Xl)D5| z9{=#E^IdzG9&d3xS)qxrGPcIX;DS@&LbZM}RULgYjN#HI2XbM3u_B@v$t_~ShaDqa z#nO8|5~AM-kZp>B9~icr+ztYYUlCgERPlnyuB1Muxt>e3T00J2u-)YdH2XQ;jolA? z%oEggyXte>xZ|kpdK!><H?i^+1h?do}2NF0$n5 zDJqNTSR{p06VR^sa8;x7aD@M##s`tKsllce(YWyWQF1a9TD*R5kIl|tQ&-Q6dzLj@ zg*S_WT1h(vz#OU+>Bsf=H==A_$9%s~!^s6v35WG#YdD7utf5C!6opX!ZwKn1&?-sz zYLS#n_8;1?|DB158lGvLOxruG>?{JN!sRpNZ;at~*ADTHs%yiR&MY*xVHRHocMd&Q zj+Vr|-8|o}@1xM?ekQLEbHMHk=p&ayE$lQi`g<*JlxhIxzFC>^d5&%AMjH2DZr^q8qA^Q zj5GrFLklVsS^BIFMKqwU%%N|FRoe#46@M_={qr;1DGVnom4r5PYD5BbP#hAXHH|;& zDGjH6@zk7he!RR^fYko2?^yMbPNQ6U#T&2g+c6Q~oKwS6DE{3ZHaPs4$;d+3S4+Eu zhA=nD9Riq}s=waIRTuuUqo$sGy8JqrgU7`CQ(e`O22Y>G5*Jm6_WwDxIVWc(_Gs~lv^i|;qACI z7?(PdFX`7AJ+v#KSzEP4gEK+V)Rnt8Cn+}%Qdu%BvlwJ;hml9j>%JY;Wu13A@xi#L z`ctl``iM{=hmma(+{o%IvJK@*<#@NJtiy6V!e$3$S<>y;m9Xc2MIAx3-UcmeH=E-M z5J97~xL&4oqKj)?|4V$=P~l$@lGPL{n})1 z*XFifL)6(?^Si`~>yLN4G;DMbp5n&QoN~sKD4&?d$5$QeusdfF-3qdbzMqwiO@ShlS zclNJ1onDqW#O}i}5D-Imw9!fG{4Vy;rA~^}aCdHwl%Qs1ZW?weRL(!A79ow!_P(`r z%^ojpacg>a>+fyXSm^CQI8%2jhJZ)WN&$Bs$$_M#FDFM|7nHB9F1wS{b6-~&+`AMH z64uT_f(0)zklB?qsPGsnchQLvx=whJjS+DWEX$1hCIEAEF>vX&U1D)>;t^{CMI3oi zsCznA-AjeC`;eO!p6*|TxKC{tA(hg2`kXG%T&)&~-TM~B4=pVaP}kO-N$HQ>vjo~! z7~h0sm_EDZ`J5w^+S*oD@sTzJMtODo{1#Y2^aZV!#sidC4Jx+{ zrQ_bVvkZC}jeY*zQH733#sTp9NP;4S6o)ux0y%>_6x1n5W>MYVcbZ_y+9)g~3yI*U z4l*`dCqIcJI;1thr#))LZGPz_D9Lr_y2SXHJg1M#SQI49;5y!nO~{q8ddHw^n{%MQEpYaMeeE)7DyJ(lpSRE)q^M8y`Fr z33^~L$xg;FLbWb36(F2@iq1n6@qa*Hz?_D+UOOV_VwSW)psGe_LRcDnh8lp% zo~fV5yxPNIVdKSq$$K~WU#Xayp9pVcF!P068;o>Xo`?TK8ywAH!jYDZwRLD_=9fWw zddkiZDgQTDE$%|y2W5*W8jX=JCpi8iM;PM^kJJ4v73Uq7t5?2lEbz7m%E7GI;8sCs zLUCl4_(J*Fz2bK*O$-riP0-2I@8%eh{ZuV+{@2;ZX!B>)=3K3Hi(K z1Y$5|m1OYRjIeG#9T;v--O!UFB~R=Y<&431b6+g1%L3>2v}zY@;w==yLy}#7DSSuJ zR%Y+*qYLP>LTDaA07?lo%*>05tJ2J<5{K=zTGi4Pi8@&+NDt8m-ej*qc>h+84#jg= zqp~t#!4UQ%i6^>Rs*?u5`%o`a{cC)f-9Lx?(gghSGuh};Bk)`q?2|QQzB8yg|K*6{ z-v0D>c)afCdiT`GBJ$ULIn_UKUcVec$4K$o4dwCi{-=5f5LIVYdXNRv(={w@Bso#r zV<36tjLD@%NLeamr~8LrmWl?4T|I{(GXn<_v26@-05ysp=DFjgry=x>-Gm&(Lx>)2 zFhWfF+Mi}Qd+;jd!CU*!WiyCz1$pgrThw!#zGQDk5-4!7d!lG52MTp(mW5Bf*2 zf(tb+NpON7tI$+26@q3RXnIh~eYrD@1-iIbf9p7DKNYb9)%u)QEBI*LtqWtH_Sf8VprwD6`5j5V|v^!a=CT13?PC`52+*7thZ8l&<4Vme)7Fv;Q z3NUtCuYw&so>^+oOJfu}uiaTQY$oS)=wQ+U6xh&oqZ{PJJRK(*?*Qs+REPa=KI*yl zLbW%ufGb-u0Od;K!*Kb4K|c*opgU^Xr|MPu2Xy+szFn&qU_z@lM(`5E`g~)6u-)x_ z$`i-b`;B;cY2_zCCN5PA<~wOdE9QTGsE0aNNONoR`%rm-oz8TBozrM@H)1OAY8Z)4 z)0m;s6_*=8jp?S#<_VFB4p}C?AWD$Z%g5X*=^{+eJsA8BD zPLS*dr|V$P4bpJ+#Br6cbbLgAyMrTsamAV;S;KH_!mjNv6&Ew&?xiz_-F{64G}}kS zFB{j@z$SK|{}he#AjSp8B+Q6vVHA$qy9mxFh(8-_Ppo&J_zQnZUmgXU=EM8J4ah2~ z-$4GqWlYYUOM_!T({RyR|90)}^+||j)pynURu$(RhEHQt5=o}R0AvPOHum(vL#PG< zAq?aWYmC|RvWObwNHwD~kTsQh#Awdn?#ctgzlLU046F9Q`if1b=Dgqz@(MTm{f(gy zYTASgnC#>feA@Vh)WUM7cir)3WR#w*GNl64H#-kP68T%Jzx9A+X^eBNxYp%gHg6jl z4cDsT0!OxCS5gY-`Wlxd49Dy_;5D%1TwOI7Nd)+I?;6*0x{jKlU-2DY-vf6OJCRTX zVg2HTwq?!ZIlCS;Zrz|NCWV#`wD?i=EYc|5h|?BR9X1c@-!t~bXjhLcU=Go1=YIOa ze{r)34DRwll4`PtHP>cDQgN%*P(etq)4&qV*6Z%a{VIC?q3;HDtv}xZ4wjW1krq`~ zx5N7K;%)U}@#)@hyl&ttn>p#`#{q*mhRVZ*e6rP6YRIT1NPdxc^Ky(83LQ3|k8dw)U;h}1{ab4U9kxyU<{n2I&~@g) ze;qHg5n;2$ofi(wl!LDFNgXsO;G{5BeyO9S?+uO|hy{S#}amc|raCXwy7kf?Yt*0&>q z0=?>AUM&SJ)|V~2?mah*cHw#ii|p#bCrexot122)EKz3A%C*~6F;6NkI^pF?>#TyX z`@w{W9W=n(a2=^s8a5bd+jX%)t!dI5vWy1T8Y)wJc=G9%%Fpn>t~#l^RNB5&n%)t7 zKp(j7jFAk85L2%dRp)eN4jMpE5P5Ou-TupCcxS7CtXXtlwe|lG3M^+@A2YB$_(zu> zE+@1Jp#$o38sFDs;W~V86Giz+`Xkd$rEqasBCsL~HA6`cE#P~S7&c1CZ%eqkr?@&c z+WI4s-#(v`)&aUWbC}Lv0fki&?V>-~s-)eSxw*K&Q6u)@D(BiL05V?{HZ|Gg50q%_ zqYRY<))AGlc8JzujzGWeo|^r9T!) z3+Wti`+LfYU7f5AFpnyE2SoKZV$DEfhnb-{+<(sPX!mGI59mj5rg@M>RAD%B{vIsp z04}<&4n~M;r@(go48{%qK8+R1wf($oYbYmuQSw96Mf6Cil*#iUkR!IG_HV1v7x;Ws zByFa|l5&d|8Y*by(nq@k;(1X5HupA_i?S-4A}n8}LQg5yQVf{AFEsuv+yp~MhWa*H zE5EnkJXx2|$K@v5fX}7+$U^-w!N0frJtnZbi{rxoOAa|`Lsy|QUTgipP{PZo)3vu8 z_};t&TCYGqBiP}fA(;<_;2s@{4fs6TO&(nRFA|l5fiu>jA5yTJ9AHv&;&t)&4L`s+ zH%23EnYeqZhc}MCQEhI#cT(G9tBzJswc%o%y^^4lt)WZBo&>H?6dLT5?TeoqzW(N6 z!ns1AW%;w+J2qOcO3F!no`0*wr@WMYgs8V zG&Ijq(UhfCRCu>Yx8YWdyoH1<^(g9gU}O@OgH2+BXGlBpO(1B4WP*rZ(L`uM@z!6G z%Sd_|6~CZb@0iA5{+dyC59Xq%R`xk*=Kz3Y_s7rIbS%q+LH7jlaA}}q#1)%ws1o9M z|5=B&NZ@mPF|G|P=D==3v8D%0WsG0(|#zO&%D2Q z7iabMF%k(W<@IquwbSTe0SKi;^b>q1wY(0!k@jkXKH+UQ4FECQ?HW_IM_mRca>d_; zo{nNMl!Sl9oCjE$N}fnkR2JWo!dLCi#>Xeht#Fqwr0LBY%_iu^3z;W@=(TYbeR@Y&Oj9# zQ3)lYui4#A+}$PJA=vBvIh&Urb+mxQ=DDa5&^X#{YCn6Tp|zsQkr zRP4ZR`qhd@AvQTn6DYf##N8gWYDTTVO38QW!MnhN5WSc zo?@n*5Z2OS^B6pB?Td`~KR(rWE?!@Uaexkh-(9DQ=U52-%AxYO^ieyM!K7^Eex$^x z{(*{j)e>d>RY&WNH;nBgd|Vb;MW&Vfk3e4U)S=8&+X&h3JQ+zRX3k=XeI~LEb!LE` zTH4<-amzypJfLOm)$vqXq-K|PN#6F3aVzHwJ(ji`xH zB&oI~_&f?l~fu-DS5_!x$8JZQ^Sztr7gPxcF)HK=FiJe9i%C%J|$@Sh|`G02tuu&@+Y{CiV zdT)PqXT}@Y(=2BKRn_q#n{3lASxbwgltYwDYObE%v`=CS`Y@IA=&h=H|T4_-){#W>Fm-R|?78v;+!cs}rGm@E8to29NRWOy5n zEg>TER^?wdz`0%dSJHeokB@W<_1<`#T#kyvuv*T}0QEG4#3`?@R$2+Q(F)^Gd>!id z1CMGdsV339mMGqUKK>dImi?+u ztM2{qrdRfisJS5GKZ-1Pb_GA#27BLw*oY@_xWJ*XOjMKKGW+Z19E3&l0T`awLH(aK2f_$=|jSdOVuURZ+)-_cj zSN7|cd-;`hmhb4z1E+7pO8U3_&4=E|0a&PN%AZu=fSe0I<~&qbNr6=ER_+0fH-Uw} z4X&*Y=}%VE;L3u)oxfRlk=uRb zer!nd`9##QPjUXOya74B7cDzebliVpb&PjgG1^IgI=(#XhrW-4r0V+x$*udKcsIWy z+A@!QNTyl1CTFxq-ocK+_JbuH(*s)07Zg_E5FTNOd#(RIBCRIh{N@$S@L$!+VK;_; z&X76zDvHdD*?HI0nTL3>(Ulj^wue&*Rh!zlH6)80sy?<2N_eI!&O|{s=fv0U5NHdy zs5GWy)3H%=*o0qgY}aWq=#?4D>=f@I&RC$y>9mt~0EM!>G9Y&N{Ru5_$Rq4iqP^SQu+D zt(mWB3ym6@O;~l|(bF%xHA)y?zC3dD@>WJfNq1)|rWMs`gm78%)#!>#7?VPe(tcjT zo=+2On(I8O^UR7pc{2WhXZvk*BeiwAg&b`XM{Prl{Qt4_*MCvHUHdr9&<)ZJf*{=? zU7{c&4MTUgbVxcNEjfgg0@4iK-Q7cXmy~pUx9{uzyq;g4`2+Tzb*^{W_Y5swVqJqY@UNK4$cn^g`AjrfMhB87pR`nD+&~d;WUm8vBK&->Z(j zdM2?e&S|xn^?9e1N!ztc^+qXD!YYeUDiNdSj6Ai3*q=V^D>b`}F zu37P{UBI#0VQ|gaPtH&9Aw~o@Kcf!6^7F-m6MKbIs&?d*uV7v~#5bH<{w<~wla5ZT zt%QyND}t4&cu@cjU%X$z?8int@w=-qIluZ-Owd}l-}bh}>A+%nSHALUNMj|Ev4VWq zhBV(x@$xp)fG);oTyzDa5sZ4(u(xaMvnRXiqj^V(eZj=P9As8)5Z0#8=vtmV4$-L%V% z?Sw2&;BFhW&3oKTK`EAw9gJ#b6U#RyDd`aOk7IUFV`*I0p9x+$xq1c)^qL|Gj`qGN zK>{bp{nBy(?xg`a)lx-f@erc;>(OA7$kAYFhTdR~m(sPA9HZ^GR^;0_i~%p`98k7Y zZZ3Qd2^)!cEQn?U?bDZhchETBdG|VVDZbNP$zl@f52Qj(m~>s6cIAXjjkZi1! z2>Lw=`!(LN6mW>5li0ETrS)y({=`mYSatPV8ff3&BS*$6Cpj#v)7!e+R~g^8vka_%xy% zl9h~qmP-brLD-eU*Z5Sd=rKFeNxoNC#sF(cdDh6PBkkuO?yj`@Jxm~1ouVZXt1};L z%r9a{(1>`8sfoE z=>{&IAk=%!f^{0-CmZ4Dn;-UjXkjY!gZeTF3k_B!t-Y57%4blW(g?~vmQ+gga6Nqu zKs*jtRtjlQED!cpEWg@5ZEqAZ40dOuUFXE$OTG~R=fgq{+6Pm8L*;gQHsPQsQ#o(PzelOx`-)e*dxOZ!F0tw!O-}Non`DL-s{9kT^cq$2;-$ zZIe7}`5mS|Ic1#&fHp9=ZY0*7J1Ek`nM`ofvpIpb>h=cj=Z=Y$68Y7O#rOEDzmQ`Z zw=w#+(3(Q7S=PTdN^_6D>WuWHo{c8$5A#Jp)p9Y2I7eqtu&}u2yw+zK+^8LBd=VY9 z9$$@Y7Tc4{V)zdGX+^3$qkT?@#PWxC9R5vZQ{6MDeC6}Yl;Htxug-a6MR|tcvdYKw zoK*`UxG~K?qXdL0g%$%O%?8?|VP)B9CGt?RIutjGZEJ}p>N0!q+q5oLH z(o{aTlE?0(5R^6`g8#&a!!W$agK9vpf5bI!7GFObwz^;Z)~}W+70GfoYwUK9t%gtw z7<@HZJ1`zs!%9$R$~;%OBCjJl^DXcwMY)&3L4oIbhXe^sYq58x2|2ET0!!4vU2Km9 zIWa zpx7m^4ogCY{(tX%53eVTn>&r)~Hhn?&V&dxo3Kn4jM@`5KOSR@oJ{R%)Tx z@FgO|ze_o2s~WLmXhtJ|Y+)F;pMp2eE5(?Z>$8n-MG`rTnguL76_qHYo6@QKx&x40 zO&c91c%MmHWhfkQ*QAsb_S#r*vylR{HO>gGb|XjRB%V899zVx@{#LaN^_<^y z-SCZ-zt~$EA54GwHL@!(k@L%EcNDPtv_&^3px*uK03!r(WnIjvr;FCr)~-?>!6616 z(kV?EwHwY#sm%5eEs-??>hI>EVy4tVsD%cLlW2|2(Lry?oo&2!oIUshDr2Wft?^JT zc`PQ(h9V3?12W*W6m0~RI-X@A~*eP$2P(ODZdvsCO86m&-e<-~Z{@O{=uuKLK@HL?&4~l`S>Y=*mI4K=$8%sJ-Udbc9+zqxo{0)~u=EkF?%2?ZiE%%t<523Q3O;~!1-le~M zbMwsxEX&4cJgY|v5io7y!L~!%{gH7#TSseX(2#H+iJJW}57PxfTqMP?_)=OH%H&Lb zB=ZW9O(89SGTl1h<>3M8J74>OqN=W62YXP{dgEy5Q3qiNY{ww=a{PwbQgvb5yfmm?dNJ;t2H{gVYggPBeh9GP& zsrZeE>J01U6vubiVuNLJu@7RKSL#=5vezhi%GJ29lx%&w!#k@zVB3+`IUJlXc*6>P zn`QR@yjvuiFcP6uk>_%48H}0(x)zQ?n8wF*dRc7xzWc+wx`#!UA+^<(zoCM0(i@>~ zH`dic*lZ|pOPqn83ZjV(uTf^>T4;EFiOk>mVix^cd2jA03HdF&^SKZK26t6(!pTW1 z^aOODjiJJSqPFhn(R9Y;wXaD2<*c|9o1(9sC{5@194oij*Eje0h2DO0?F*>ErWCJ4 z*VC6iL`p>dOsT`IKIMjsdMI?5tsglVv#GzOmvtV~*MKjAc;A+;?M-XR^F-CxZBjga z6mM6quB5+_VivJjn2`@ugV7h{Yr=R3a@6e5Jbol^ zxIk((ZL|rAEW*j0lC9+zmc#D_e*F~xtlQo3f=g@Cd0tlSxc=p?5}d%XgWe!7q0Ady zpYwG|(=I);fe&eSp1FLnTJ3xw+03`re^+V<|BnR{KoKglhSYRia49U*{3%^bW5xB4 zNJ&H_8D!Zbq^J|3p2{oWwTwiDRR>}jSj{I6L#H@>c>C6JyZn33NAQPWI~*&wc+F+V zcD|YQn3y|8NM|~bAmzHX#uuvS7sits_N;3`YgE4sl5J=64px5AbuhOfQPx z?{Dm_7!MIx+VzKdi=_yfj$+|v^AE{GJ0*k6dU(%2b_NS{Os8iCA2_6&+&2y7TyGqn z9RprF>3={+le&V<5V3u&PPZap>~Ir*_*tmU`J{$J(*m!EC;^jtHgAFcm%1EtAQpKY zBThX($Ehigi_P5`+{JZL+>%fJcfhx~aul;8%Tt^uq%G^(n4 zX&{vSu-|g0?+cORm9JyGq{jGFf7&%wC*8Z%@O{CG%v8HHr)UIP&2t4rlaAT*L~t3{ zqMUJQ-wiqJGJ_aPcujXy?ojHn;9J0kEzVD9lE7q50X&Zx!p@I_2G%u(C|RjhmBXmN zScYPK=^#s;pp5QA0BT)*bS3K>?*=_ygNvWe+l&YL!?sM+!~OApA|p^O6(q`u>%YOH zP)pU#NUL>jBl_DX7Jo0@iZUzz*M^CbNZC2HKSn4-nKVxzp^3BlWvt$b_*W{lk&sZ~ z*!YJh8z%nIc^H->B%(CkWH(|l`&zgT^?=VunLF1(i-n6-4Hl|ISH(v3@8;KGbu9F| zI(=F_9T)Hvq84@*MowcxOionyDtz>LF|{kz`ipl>rjekhwiuk>P&k z&8r@9MIS!yrvOtdxYq!U7!SJLip6PPaZ`I*-}cu#T8W^aZIZi>j6Xr?L7OG)@|X&` z%h@-#F#g)1LWLH?7B<;2&ULinyu;ktS04DSovr>fGyELNuW~C6*?b}D`GEJE z>n*!M=g^3PIxHM(^`3`baecCrWUx2)8t`WWjE{t;fRA@_(8Qi^ivJIXUOJ(}m^gN8 zC_`YOTJNm+RJ4VH1mL3DKGt{(YjT+SH=9(Me{8pDx#BNX%2lHqHIEN*@2)?H&_wMm zOrIT@y!O@{VAFc@mv*eZ{J8=9!)Oa3`+&^>G5*Z^eCt4gat9=j31m#W#_dMd5=PQ1 z$W2tUG?nwV%Y>{vi@=R3V-0V=N1J@JenFUG^q%u~$c1UnnnkChYtv`(E2^Fo&X%SZ zNJ6sAu$}wY$tFq#Y}f%UZg$`c(&HtxOx+>*L$yw=Qy5b>&#Dc->FT#q;|ug#ZU=@I zsEdPT%{N7&4U0{Iy`sMCyt2V>vQoH8y)=@lAiI?}I*QCCs$f}7p9RMK=mJFp&qI)jc}Ed&wk}jZi8jFb~v-gQoYtmVm%jL%%$-2Z$qK`7mn0;nz@S1hiNKB!T=Q`F09;Sx{w8C&g&y z0Ui#wL2x(h9i5QKZcS+7ESc4uDa!9DjHrRP8iFZygf&VgO22+Q(_-3n%pD)?I_%p( zEu8#s(p(K3IPh-c$2s5b|Mnk0{@JkB>#SgzG|M1NR1_7cmF+vR>9Nu4P5MQb5s;n*c9a0f^;3V0TS;fyPd^~kRrvt}0F*oF#ll}W(AfX(fP|SR%AaODQedY1 zgw2y*5GY_D+{j$=BK=qbs588*89UvA7sY;PnDbCUV|&p!e%sJ(+&`2jz=2GsD85Gz z;#$r2zJ94};EiEU>UlacehXu&p~No9xiupI(jy8%mJ$>vXlGjf*0M(Lm@aHrSj$97 zRRYyyq$z3x+Tg->_n%;lz&?R5tt}qA%2w5y&{fpWc%-Kcqni$xmzQEdi#?Wp6e0fP5xmh=ddI3n(6Cr z(Q4VTs-Q{(5Kw0&ufPKS$^7vKEP2wmpyB7;l>gF!;P|zD+|y^(8j#vrJc$fjXI^m- z38o%4v-Aj>xg-u6Edbt3nzlktAym(=uB+ojyG8I802*>a8`)TFrQY@Z z?JV+ZrWD17#*zV+-&`kJJW{v6H}duq{lA3J zs0+oed28%D5W}AEezi;ghZAMEc01G9#$Z=Yc1mWAwFAS7Y9w}%2{}LKbA$KtxgB+% zVIsXGvbkEk!_g9U1-6~c*>^|4=4^k$NHU@I1@dlTp@?>BO&!fddE4Xezbt?Y3nks) z`Rk9_-6T=MNP-t$y!wPBNvkakaDPXUcBJ9N^pDB(2!Hg~h6;LIZiQ0({h#F*yL(vh zvyMiHLP>==txA@$R}u(cg1eXsB0F_YEt-JJs<&+5e?|Sx@@nX-RB+eAu_X{z z(sb5P=PLb603xZukz9KtUGG8PyORG3%7iev#bKvG{{+MD!;t%lEapNZd`rq#&!wg| zo)!DF3cTp-$f7_14?G4ay7ZcDmXjVY^xOL!0PNE{^rX_ zxfqao68ldtI!doIxtAq54Q4O?#ndziL~u6;U?`=UjfZBs{%sE*{as%f1knz25r}B& z>*FecWMUBOj1$|=R7X&lnyiYEw7=tnT{?g4cV&^7NTPbN_`KkS?_%}!3oq~|=i#By z4N3o-f-A#c5#qJxI-$lu><}dsckmrj zi8r)^mJ2Ag2{sXyoUOX;Br?Af2nHilH$nKqw_kCrO(HT+@^)`4xm(QUb4s41rYRZ? zO|ufkr04OO;5cZ1>`Vhqsa_&v!}9I;lK;c|p&L8axeKadSSXv*+X)1Kx`oQj+0J+ zzOT>4H{k&<1Pt0+TewBHBQ!+7@TC|6;2WUFgZ~fZMJ;K{P65kz%LD*-Rr24pFhFJf zFWf3_9@^jf!%`<3|5d9PRh`+bFzH%IzEp7{1Y^=6>K)CJ2xrVlmzUTHW55+y+V|ZU z`LX!>l%%OE%KEi`&W<0!4mAzJp=c6a(k!&%Zh~Vc6a}0iA(q*!jKmnhW(PpA&j#Y> zssGglq!;*gNwa3>;e)G9BiUkq>t(}{SoO(W4B&5BoV}gNzaImk=+LhkCF0h(t9+}; z*(DW2&tW+${@4`C*NbKhLRb)i&*?<)>SHyqx+Z%kg{>%=b)?+?-HC-i4^UyiF18{B zz=X#l=?C?TW9qL9KaF(dp=sMHox3Kzz=Ouom4wg@ybECB#ba;YcdWfi z@~~yiO@{!*7y$AX07xoiB?7Cb@oJF;G~?(u_4}~5r+qij(mv`)gGB`yeFg4<_A8Up zmLhDi(W@`nV#xIo0CNd!6TOa!QoodqtK02O2HhO^LCRHD^rOYz@}ix*b2otgTKXdr z@bEYQ#q%BhIbHKpV|@7O4z_+%MB_i~+Xk@j>&DlSd{}5<)Hq-{Wq5YS#P?}SBbWEj zdNqCjj|%_{dwAEYRO!-i+MEi>A8SXvYga^mO_beTPokkPWIV*@IGZ+H=d@@PU_N_W zqt5j~;M@y+yW9%ETRAga_=5*IfQ$2{fG^w*skafmBA@vG7Z%h}w(Aa!(%zowE%GQ$ z1OtRA=C71pjM0%cIbO9(DevNZ8k@&aGNTJAwH_^&pD(Dltt65_(h;3$AB3MJmyv+? zFwO(utsRJbw4b$iWIO%dYu1r%8AePiJiLtT&t%Uj@N?TV1wrG53bWs zf)gQ}M;t$atW*Y%;(d#z8j|BjDhDD=RmYG1KX59}9M#n?SAE4qd|hTrC}SL0GE6mX zlr_TtC+T{a0KX_iK*N9%=zVlZ>9KdFO=c);4) zuKCM?zF#b@O^t}{<=N*PLk=5#{f$8Ex|jtyhrq(TKifw_Pq*HR1NDnBKb}-D2V#1Q zm`e&1FP;ghN)(1L4lH3z?BwRY&La_I|0p5OxD}T#7NQx!n~Y5!fC2sgf`ubX7`*u` z6j4Xv7Xs7v~KsDRup*;fH41Jp8u|H6e4 zM`XJ{K<|-iWv0LM&(sK28cW+>V2$6!| zLE0BZUX%0!x9J%ZrGBmp5_di@AwhrCd$tmmAxicr7U=+MDMP`}uu%9rOFotVN{j|% z@HBXv)=62hSKOQmHy+trU9)=Uce${hV5)ZK<;cA^oCvoyk(9eA*@mc1qau2BuOF}0 ziECRVSHQJf`>kb(RKt#{5XA}vU@F8J==FUwC)H)D+ zBl3eusa==u&>b+CEA7YW5UNG!{?=RrRlmQSST%efNKr4{Dp>dH@4zY^EJfQhidMro z@BeUmZB!q@f5r(>>-=?w(k#ws5_1BuPdQX(D+;8%+9^ioL0^IXwf>^1$&oJqkN4|$ zkwhgXgh1fKaZ?G9EOno+eVUqbY?G=fs&&mXUd8hN>BueiV6i2~hte}Eh#LTCd5ELB zNUl-e70b41B38z5<|}BB@x1lGXP0jzQJClU!D}}*J0c}Yt9^B6THgd_X_jO_irwlx*_7@`XP#Y9iVP-M z9CqgW^ym-dmg=Xl|4!p5eKcWBu;&&j=2H=62CL zK(V4}!a+sDKUv!|h==p9MyD7OVBfz%1pjOAVF9*#^zv@_UF(OEUqB)sZu8PzoZ`&c zMga5DImZB!D@c~)ko)@+`{tOFV@YG)-V+PCgEvw+hp4YNiHW#|Mt6i9nDF3#Ok9$Y z<&3m#3y*;z)}cO{5|#t{Y6X*#6eTAE11z{cT5a%Vm}i}Hv5|seV7$e+gRYJ3B5@}o z)SMj(2D94eQ;Efn{$ecS%f>ad#pyoAR-n-r?NPB2f9O+!;C-ZC<+knkv0wv5e)Ohe zEs7z62tD6lTshaAq~?B{%RtnNJvDUFs=9)=bpS*@1eH0P~O zcx3;KPz6G*W>b^9n9*GJu)xcEf?Nd0=&H(17_eGgY)@>PpfT?)XWaR8N;+^tL z&g=G+i(a4>G1Jr&X>ObVCPUM+rV(g2)r8MbICk=u5jQFg zP~c0z(YihkQPvX}1&VkU@SwzKN-=Vk**v)`txcvf$<#{XBd`c4lQzbva}$MMINBtU z=8ScTEN1q5U)sVQ!n2IN91(Y=g^uMIL~CduAF#syY0?(xBmb8PG}nf3{V&3xxrgo8 zIDvMH4zFZfmmlR(sYI>4i>eVmawstw;Ax{tll~EDKSt)nsgq^;C=$5jYpKS<0O5Se zb!hF)anW@T&kcB*`x~nI`(t^Ak4xHUtD34RN}+Xd&FJ6k!ejtk{lT(Tee@+5iVV9L z(dyu-n)xK-k;_g2w4AaBkMV2WI4@vs%`kB5I?}ofIYaudS~y={6T4HbB zem(W}A-}?4xS|aO1vHoh(|`uuEveGpcghOzkmZU`>z6M}d(1wSzqwp-3+|Eywwy4z z^OwlkdH%MX#|O&jpHzSXKwW(hj|5OisSGcTY`5g^NW0&rOj|Ffb=uat4v|oaL!}%3 zg@3lC(NE{o!Vn%zifK%z&=o|=nL>x4^9GN!I6lTvHb)UNq_edmCfSGp7#zym%f!%$ zsiuKaHJ;8KWw%Bt0&f|2^sIff^gUf{hDTA42GtZp&8Jzg!ojbFv|`p9#_WSk(pVG^#V!~MJMm>m{A9%$c8*$yj4e5F0O|Xs)0g^&b{lfc-JgJ`GX2lm!P7leA z^IzY0qz4r)u#SX+uH2Fc+5ZGg$8Z@hp;>t zcH2!qcIr+R+pC4zFoQfB(R5~mncUK=qh8Drz`ZRA7D=YB9bb*-y)Lv;q(F<#Bl3?z zCdzf*Z0r_XtIf%VAaNzTS2vp*wf)$Z3F2eC4wl_5{ct@OZ|%td?6n@pBrUg<7Ec#kU;V_2gc1DKSI+Ij1r%J@`e=Cb&*u|8MAv3sg9bD-X3fAR@6A z`4)#Te-B-W_og|{Zy5ZGarEPbZ|v=HlcZ&u9WBb2a^eAx7T!?fNNsr|w_YhyfbZT& z*7KSEM!v;h(t#Pj5XGelLm?qqwoYL)>RpDZ@58a>B88E{qoY6 zt0N!t^56dOTp~c9(-z3cK-pn23ZT9RJilw&j@&Lz*-5L({tE`?R`jJ>baXR$1n`l} zSq?vK5feC{(Vx00tiL_jM*QAITBi5{!d2K8KE0SH7^-MTxsb9sEAYUPy< z(AyWf;}iuE;5JUTE0#yTklM6|VCy&I@9`dN^DfL3py;cOk&IF@L0D}J6LJmRvc=PA zsW%xfJi-r4er<>3N=iI_$BGi=Tt7%FB>gQIBa-AH3_yju?|&$~PMQPEThhYqjmqCx z-*2SW)z8F&PVy#0m})?mZ^}vIv=xh6s1fy>-_E9v8vFdHQ^aH|8EgdOUh)=7Csj97 z@Tb1AaZD1^4eDUeyWc$tHeXDq9lqI;4#m>p)Eo4k;VY{Y!plsR_j3kT4;2(V@FLn1 z!5f)M7+bwAJL(NqAJg@gQ9kr{N<+1-Un&^3AWXhl$mlQb3RFz&==fWZo5{t(4`Nbk z{5AOo^P{aHcVU>KgFVzx>QE*~jbgWsmZt{3ntu6*8oSx|eGDJZ=EDKG_U{_(6cW;I zV8s>i<7LLQW8Hpuek)m$_qdz;xwhhf*Kr2r;5)>pX8QcQSM}fguj7}FX9vz}8*+Qc z8+qGj@g6+!UTx!!5&{UaO~@0V3!hWQV-ItU!Sz6_<=L*C{Gz7H*_B~0cqiD(cBMk| zwSLx$mRNEpumGM|lj#3~_Y5q%{I10Xb)#lehQ)<%5Aqwh#}|BA;5Ro~E8+ zbwJ3NIz?oB{uo5e1%tk#59G&bl0Z?)qrUNOd|k|Gz_GO;2MLs&l90FTb_T4Dj&}~y0E=!19`kYqckX$ z>Z#&1y3G`}_e8cYGNN$&$j$z_drW`klY`IbQ}67Yhia;}CM$0ArW{JS^*wKAe)HnN zwon+xcs%r*jI&!bftRa>lUwkH$zl1Q;qI?rUxU^!aXq@(3pyC2Pu@mXR{v;At4d?rPkpq)XY0^tbi68D}LMX$+3%bH@I zE&tsASzNhfXi}gOElijeEL`S?OunjKf2pA&hmU@ahpu~r^$PhJzYtMVMovRa7la8( z)+l0Cm0w_Xp-hvC&sQLlmpKxNKAwv)6J4okS(*JJY5B&gYBQ>D_e|8v^3TfhlKO&(VxSU+&9oqfA^5+9HGxTk0Hyrpfqnln3tQWyNI(NP7CkxcWiJ z_f2n8xu(7ts;)(JLhZwOu+4so8#T5VvQnZp0w2eq$Dc~#I|{%KXgDu?-{;Af<>=IC za(%@0v=6pB4?&snm7$N5f2F3oa5b}<$uUgzNM$z);^bo-@=KLUVX@y&CYd;(T*EX! zBbJ_!KyB5pCu#ku7^O-^C>=Lg7Q#oYpK9J3g)n^97`w%l@wqnOk~&nLjC`T+-&8X}vVBKv zHdl=1-9xM=`S`P9;!%~RKpL%S%i2r`g;R%8ozwgPE7oeo`#5|fC|8QQ!t?&LOP zCN#Qw;k&(1Dk+Qb4tQ1W+v-O)yy>co-PRosU9KQ%c@sW;)+YO`WDGBt94GpOXujA=)}pgoXmGC9^_7d{Du-#&B*wf5tph|yeVDy1RG z@}~okmxC+=GTnA))zr88x>;=k18G@c%Ms(eio;=h{PCgm2ND0z@$&!CX5MGU1bG$yL zKWZRysq9$nfmmkPITfV!y^!}yZpfZuq1-NUN0KdzYSK&uj-LJ@BA`uPLwba+cCBg7 zAwS6VI}<*Y#$hNf%uP^*YOFV(t>ZRWSkJiaTkKio{yvv*Gxy>su!N>{bZ-lOoAs2j z-dh$sX~<0w{#KWP;>+F8NX{&6)1F{)7Mig6KCfEiqu$TWK?!%}g<{4b`+YjL35#wR zoY)y9{^%m2Q|m_ykSE?qf8^FeGfEjwVsR@RO&9cnbg1(O^zqf))`4AWpB8CRY9E&; z_gY2@Tk>vWpZ|b;4>>x(z#HjHD+eY07ihhoib+3L{cc;op$YI$wyyFfisznZC6AK3 zwhh0dzljIU7Il)$HX@h7zOT-s2d*9xA0K9SMN_FKqI5Yn_erJWR-2!i+x7*gIJP1_ zKbs$1JnKn3A%S%w*l!&zq1seWdA&HagR>s1qH0Z#66=)j|0#AWDP0&BxJ+OdO2vSz zLarZQi*w{q#plHJcvM7(+M%Y1)Ceqm@Zvn71Gdo6uu|LAg=|9EfC7 z!z5Q=CWK@nXMkR$-E-C$##__Sh2-=^3fpM+l<527=^k45=j=mQ0+y#Zx61w6m_tgI z2_X0fJ;zI4`&)RH#`3}vLjz==HkeGyydCldq0n00kDo6;Sa|qzj)EoD&&OBNY~!KL zIyN*MISaday8cmDJP0HP9@thr8mC{E%PrFOBan;)U5bPWZ|e>FIY*n(Bl)<`=CXv_ zvD&{vX>a(ED$eqduJJUkA7lo`pn?A`l8^8)ll|=ut^TNy$mObI)j3hY%Xdl3l^{-g zCPfr*<)cpiP*`CH$!zGqBacm9QwsvuJ0Kdf0;-4__)L#lvT-yYk+5DF#2uGqx8R^j zJ7^?=k)HLy7Dj4GdCk{==L_i&O;f+;jikN3oY=YaL{|9#yq8k2(v4v?x)0#>FwHX z+2juJ-X~w?@Uxzd$M&i^G?cQ8LbscsL?GR zApB-*UVW(^)efD9fvc^N|N3u#yX>l4R64-iYKqP&mEC(YZ^Mf8j70Woi^f8HloMEg zL4p{TwT}v7@{q(5qO|^|Ng3lUs-cxiA?s5B96=MvwdcHLD`FFS7o$koz%ROwg2Uwy zY$!|MzzW(^-`eFzvon0QPmWIpWDgV9!6_q#tq(ks+DlV3>4Vx=UyAfsztkNPhyBCP zJB5b*Un@HyMkzxhS)v<)Y^#>}Oa(6$lf)-2GVR+xKzrgjs0WRUPpWiHUO1DZgN#(^ zBH$vY5m&$DkWG2tBm})6>6hYd)m|f(Xmd2w@E@icS|KbWuhaA;0uN|#iS|^5QN6h# zU6R;nnr|_**#fEx@$PJfPy9LAP&I=!@}B3(1u?1P#u_%xo|#T(#PdjI4vnLcjcIq}cY z@!GBNzRGHQb`u>DN^!?sR2$;-sUKf6FRH_?P5^?i%Iw@~~q$f932LNiU`P{f`BQEGk?5 zfUrYSG~ifDC559giAHJuM2X+WYW~*r}Rv8MSq3uWP;ASS(!izUtd!&TW~=y+uiOamr)6fIW#s`&tjL1wpQId?m81{QwxJz_QD z3+GD}@nL_+qKk1a3CujUW)CH5epGgvHBYsHdHR;-_Ab$IVZQ;J4jJj zb*Tez3h#dY0qkq~dfM~RyuqdG$rNz?S7qbnqT$n6+rMKw*X&jc6F;@ip_IGQ>!ofR z#knF*w+DhaRe467!|8_I$j9B3Nl=SmI;DHfLD?-O{=poacUa945f9uqY(}t_B_OgS zsWky1VF|lb>dx)D&$p8V`tY?_XHFup>lLWo!p@vTTRlalz<%M^RY{5}BA1`%j4UlC zhe#aHBSM5iHXRS54ADjoB{i&<>f5FlDzTc+$$-1m@|kqS)Y!d5{N~Q=@@TS$u}<*1 zcG0mJQ#*t>kb>l=gfam`$cv0+ENX2Onf!Uyqq7am3hr!_7kaBf|I{ZDzy6vQbmTZU z*IMF+Qkx4UsVJ8teti*s^z-%uWi{-CoC#Hr>wLo_&8abIr!r%iIooT;-D(RGfJ5ub z$JW%?v+m$8*fuprjM^BY_j#mH9I~^uVsAGzGEP0=PfQYCUAyg`?feIanbtR7OYBT- z$U%!lN4Putfd8Tu#Zm;=GbamrYq8mho#RnI^x#VZ#OYPkt_I3R8LtX5G;;rdz z#E#(V$S3cfoQ&4PPdAVbd%Fjw-@=!Kd87G<7bAy`Xysyk9RwFt6fD&uFu8^oMLM=ALid^(lAg#oLC>O0GW)OJ zX){{KO>-CL5O*mc1x)J)`W^N5;M2S2qn&2J2M zM?O0I5m&T;4~%4n2K^d)yQx@m8ms%sr(pvZ)mki4@Q)RicWcS~f{$Wti}(8m;fB6_ zTnCV(5hB5W26oC^0Pd)arO5(YhF6ZR*Y&roLJqg~lhMYJ1HDgmUkFwfb6d}m*jiE# z?V0F8L1pzl9v96vrMBsTB$oa^FK9D11(0-u(Cgs^?7YSZ_35_p2>ItGftcyik-h!B z#>Y%wQk6*cy zilI1$N{amIzH9i?Zuy%}aKEOkB&l&{J=stb0+M6T^{|K0df!aQ_i>1dSbYLlp;9fb?Q^4Z+j)O2eHB(RMPDQdl& zzj}Jn$jj13`=i>`{%O*(i1k%(j9uIzL&|4za$`uq{x&+7VOckyZF;+V>WB$Gbi@ZD zy_fvnTWjPP*J-5oeJycw4hFFKrelN+7lb5^_=eD%+W9c@d*N{KVo}u{z4`7~UFF5D z$UJ19^YGrW>LBPuh`L-Isob~2fqewH))*^e5@(n8`#9QWT{ql99@yw9fyu)$(R{bL zKlgtVRhWG=B)nRXT8msruF?8u`LiNPsO|L{YmAkt!7>#1`4@*98s+b=b=_x~B5Q0S z4GDWg&*rCVc~qMme<Q0Awm-qq)6SjrEcAX!=^RkdzqL-Gg zO)TKTo%j5&@g2)wk7K@VuL-h9MZu~|2!=T?A}5-=C?JxOe6ugpoYk6lld$)j=A^Xx zjGH=E9gV_D4%-XFL^5IO6T0k+?kiX|9;$XiG*Adh?hi#ND-h>ci|`86ffsB=m7Rs4*r^B}N>5GT=$^ zHB(BvHdgLC(&()^Ly211Nxw!<&eljY@Em8}DK*iHs6#+U za(vj^C)_&x+xIws)?q`j5`ODu_VHn^wk#I(ny_AXXdKcqklLQyUxoyGa|!|eI~PR= z3hY&oBCQVfy_y^;P+1Yu-&SF6W(RM$;> zLBgrrVx!q!?=@!ihLjdneP>LxFN1otP!JcjR}ZMvSIWUhGfiDg$bvrHx&+qSq0@6^MRU@GSGn$JSvVIgj|=Oq zn0{|-`Li)y9hYlN4vH6$!9FjXsa z;e4n>FB$u8!h?d2sWP?N-6bDTd_H`Bk}SmG@J=B(8S^K1;EWS~XoF5~5PO^wswyij z3BpFF*7zsHYQP$d!yo3%_6>^%QPC+uS|5Rb3ID-A6t8{9*16=z!_9PI(t$P=v+!{& zr+5<~nIp=?gt@Rn;je{YlEE%t_&y{)lp}6A*J-gA0Xk@f^g6#i0XfBFG_SJI)+QZ+ zP3Pb9qRq*9o?^{o$xw$k+U51h)aT>haG@VD_3>TKS0|jC+%X9zQ6DPXMt#f|Qq*k5 z@XNz6OsR0OB~0l+Ok`a_UeYL6yb_V~|Mx%x;DLf)2?itP&8^}C>Gkjvdd!ZQ`rxM71dKH?Wp<*=G`6(aIkO`q@gUvkj2Uh1yItldm*3=aS{-zjjUo4 z@g^onhwcbxm8f4sf?B@^(wuANewJS`SHaZ!%AWpG)Xl^RRx4$RsLE=@)xpN1M}TjE zq=u|*_ZC_IRlH2)Fd1}5m84OEMBYs$;q5ydUy0$viQA74MYZ8$`CL_nM{`tXRTV@8A!L>g|+B1^XUfw2Fdtqc(r*_$~mDEw8jC!_6+jXNN7 zaVl#(V>-=4DmOH*|M(9!$tj~JdgRPt4#C9&uMjy2SWxzpSsRdtlJs42KEh*=tZR8h z^s1wy6?vy1(W>+A5>Wv)iLdO#k6Ms9lUA9UrVob`S&`(8YjEGT%h8v{f4}d0%VQ%; z4o~jp*{HRst>)8HK-A(g#`dk!>d8ymR|^j9c1jTbK=7jkde1L`SnLj<4?&@F-c46J zf1=#Ijf}1?a2E!X&6|}zazPp@`hwU~%b`ONHk}DL#vGB^wq_B5k#BXoI;C{z1_+q6 zbrsXs{UxQeB&{0kH+Xm(D$3t^ur1Vq%J_p6u$1X3LBtCG_HJ+9StZS*%_yE5;pb3k|w)abPBC zo61h$MCw}+aFIx)zL4fdiN1+yz%5W5P)Z`JwOUJy*m<;lFKoc=iU^iS>sbU`Bob*| u33<~_<;ISLJsYWxoA|X5_>f4+>;C~C-#wB>(o$&v0000 Date: Tue, 4 Dec 2012 10:16:12 +0200 Subject: [PATCH 237/281] added logo --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 70563367..cc2cc9d6 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ DEMO: Google PageSpeed Score : [100](https://developers.google.com/speed/pagespeed/insights#url=http_3A_2F_2Fcloudcmd.cloudfoundry.com_2F&mobile=false "score") (out of 100) (or 96 if js or css minification disabled in config.json). +![Cloud Commander](https://raw.github.com/coderaiser/cloudcmd/dev/img/logo/cloudcmd.png) + Benefits --------------- - full browser compatibility *(ie6+,chrome,safari,opera,firefox)*; From 776424fc71f4ee20360b5556f7a65f5fa3f8494a Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 4 Dec 2012 10:23:30 +0200 Subject: [PATCH 238/281] Update README.md --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cc2cc9d6..0f5d0296 100644 --- a/README.md +++ b/README.md @@ -185,4 +185,8 @@ so to get it you should type a couple more commands: cd node_modules rm -rf minify git clone git://github.com/coderaiser/minify - git checkout dev \ No newline at end of file + git checkout dev + +End credits +--------------- +Logo made by Elena Zalitok. \ No newline at end of file From 338c05f02ef7ff0d0211a334627bd3d8d249246e Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 4 Dec 2012 10:25:00 +0200 Subject: [PATCH 239/281] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0f5d0296..b7e9b8ee 100644 --- a/README.md +++ b/README.md @@ -187,6 +187,6 @@ so to get it you should type a couple more commands: git clone git://github.com/coderaiser/minify git checkout dev -End credits +Special Thanks --------------- -Logo made by Elena Zalitok. \ No newline at end of file +Elena Zalitok for logo. \ No newline at end of file From 61e138130b6ec774c1881946ea013840cf5234a4 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 4 Dec 2012 10:31:59 +0200 Subject: [PATCH 240/281] added link to contact logomaker --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b7e9b8ee..8babfbed 100644 --- a/README.md +++ b/README.md @@ -189,4 +189,4 @@ so to get it you should type a couple more commands: Special Thanks --------------- -Elena Zalitok for logo. \ No newline at end of file +[Elena Zalitok](http://vk.com/politilena "Elena Zalitok") for logo. \ No newline at end of file From 9ef0d134d119588c15313ef00622472f1bb34fb1 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 4 Dec 2012 04:26:26 -0500 Subject: [PATCH 241/281] improved sendFile function --- lib/server/main.js | 41 ++++++++++++++--------------------------- 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/lib/server/main.js b/lib/server/main.js index bce4758a..15ad6789 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -184,36 +184,23 @@ lEnc = lReq.headers['accept-encoding'] || '', lGzip = lEnc.match(/\bgzip\b/), - lReadStream, + + lReadStream = fs.createReadStream(lName, { + 'bufferSize': 4 * 1024 + }); - lSendError = function(pError){ - var lJson = JSON.stringify({ - error: pError - }); - - lRes.writeHead(ERROR, 'OK'); - lRes.end(lJson); - }; - - main.fs.exists(lName, function(pExist){ - lRet = pExist; - if(pExist) - Util.tryCatch(function(){ - lReadStream = fs.createReadStream(lName, { - 'bufferSize': 4 * 1024 - }); - lRes.writeHead(OK, generateHeaders(lName, lGzip) ); - - if (lGzip) - lReadStream = lReadStream.pipe( zlib.createGzip() ); - - lReadStream.pipe(lRes); - }, lSendError); - - else - lSendError('File not Found'); + lReadStream.on('error', function(pError){ + lRes.writeHead(ERROR, 'OK'); + lRes.end(String(pError)); }); + lRes.writeHead(OK, generateHeaders(lName, lGzip) ); + + if (lGzip) + lReadStream = lReadStream.pipe( zlib.createGzip() ); + + lReadStream.pipe(lRes); + return lRet; } From 49a9299abc5a6b6fce3c5f1754227f9990a7e8c1 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 4 Dec 2012 05:17:01 -0500 Subject: [PATCH 242/281] added ability to run dropbox chooser on + --- ChangeLog | 2 ++ config.json | 12 +++++++----- lib/client.js | 32 ++++++++++++++++++++++++++++---- lib/client/keyBinding.js | 4 +++- lib/client/storage/_dropbox.js | 3 +-- lib/client/storage/_github.js | 3 +-- lib/util.js | 4 ++-- modules.json | 5 +++-- 8 files changed, 47 insertions(+), 18 deletions(-) diff --git a/ChangeLog b/ChangeLog index 10678c8b..29f3aa05 100644 --- a/ChangeLog +++ b/ChangeLog @@ -152,6 +152,8 @@ Added dropbox_id option. * Fixed bug in github show function. +* Added ability to call dropbox chooser on + . + 2012.10.01, Version 0.1.7 diff --git a/config.json b/config.json index 205537c8..bf8a5656 100644 --- a/config.json +++ b/config.json @@ -3,13 +3,15 @@ "appcache" : false, "minification" : { "js" : false, - "css" : true, + "css" : false, "html" : false, - "img" : true + "img" : false }, - "github_id" : "891c251b925e4e967fa9", - "github_secret" : "afe9bed1e810c5dc44c4c2a953fc6efb1e5b0545", - "dropbox_id" : "o7d6llji052vijk", + "github_id" : "891c251b925e4e967fa9", + "github_secret" : "afe9bed1e810c5dc44c4c2a953fc6efb1e5b0545", + "dropbox_id" : "0nd3ssnp5fp7tqs", + "dropbox_secret" : "r61lxpchmk8l06o", + "dropbox_chooser_id" : "o7d6llji052vijk", "show_keys_panel" : true, "server" : true, "logs" : false, diff --git a/lib/client.js b/lib/client.js index 342397be..9a94064f 100644 --- a/lib/client.js +++ b/lib/client.js @@ -97,8 +97,9 @@ var loadModule = function(pParams){ if( !Util.isContainStr(lPath, '.js') ) lPath += '.js'; - cloudcmd[lName] = function(pArg){ - Util.exec(lDoBefore); + cloudcmd[lName] = function(pArg){ + + lPath = Util.exec(lDoBefore) || lPath; return DOM.jsload(cloudcmd.LIBDIRCLIENT + lPath, lFunc || function(){ @@ -343,6 +344,9 @@ function initModules(pCallBack){ DOM.ajax({ url:'/modules.json', success: function(pModules){ + const DROPBOX = 'storage/_dropbox', + GITHUB = 'storage/_github'; + var lShowLoadFunc = Util.retFunc( DOM.Images.showLoad ), lDisableMenuFunc = function(){ var lFunc = document.oncontextmenu; @@ -351,12 +355,32 @@ function initModules(pCallBack){ return cloudcmd.Menu.ENABLED || false; }; }, + lStorage = function(){ + const lKEY = cloudcmd.KEY; + + var lRet = false; + + switch(event.keyCode){ + case lKEY.G: + lRet = GITHUB; + break; + + case lKEY.D: + lRet = DROPBOX; + break; + } + + return lRet && (lRet + '.js'); + }, lDoBefore = { - 'viewer' : lShowLoadFunc, 'editor/_codemirror' : lShowLoadFunc, - 'menu' : lDisableMenuFunc + 'menu' : lDisableMenuFunc, + 'viewer' : lShowLoadFunc }; + + lDoBefore[GITHUB] = + lDoBefore[DROPBOX] = lStorage; lDisableMenuFunc(); diff --git a/lib/client/keyBinding.js b/lib/client/keyBinding.js index 084a7f01..3091625a 100644 --- a/lib/client/keyBinding.js +++ b/lib/client/keyBinding.js @@ -78,8 +78,10 @@ var CloudCommander, Util, DOM; Util.exec(cloudcmd.Config); } - else if(lKeyCode === KEY.G && event.altKey) + else if( (lKeyCode === KEY.G || lKeyCode === KEY.D) && event.altKey){ Util.exec(cloudcmd.Storage); + event.preventDefault(); + } /* если нажали таб: * переносим курсор на diff --git a/lib/client/storage/_dropbox.js b/lib/client/storage/_dropbox.js index 3d4f1490..d930dbda 100644 --- a/lib/client/storage/_dropbox.js +++ b/lib/client/storage/_dropbox.js @@ -16,8 +16,7 @@ var CloudCommander, DOM, Dropbox; //o7d6llji052vijk console.log('Chose something'); } }; - - cloudcmd.Storage = {}; + /* PRIVATE FUNCTIONS */ diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index 3f79e549..86e8f093 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -14,8 +14,7 @@ var CloudCommander, Util, DOM, $, Github, cb; GithubLocal, User, GithubStore = {}; - - cloudcmd.Storage = {}; + /* temporary callback function for work with github */ cb = function (err, data){ console.log(err || data);}; diff --git a/lib/util.js b/lib/util.js index b5742814..4c2d3372 100644 --- a/lib/util.js +++ b/lib/util.js @@ -350,7 +350,7 @@ var Util, exports; * @param pCallBack * @param pArg */ - Util.exec = function(pCallBack, pArg){ + Util.exec = function(pCallBack, pArg){ var lRet = false; if( Util.isFunction(pCallBack) ) @@ -362,7 +362,7 @@ var Util, exports; /** * Gets current time in format hh:mm:ss */ - Util.getTime = function(){ + Util.getTime = function(){ var date = new Date(), hours = date.getHours(), minutes = date.getMinutes(), diff --git a/modules.json b/modules.json index b1ae657a..46d42f3a 100644 --- a/modules.json +++ b/modules.json @@ -1,7 +1,8 @@ [ "editor/_codemirror", + "menu", "viewer", "storage/_github", - "terminal", - "menu" + "storage/_dropbox", + "terminal" ] \ No newline at end of file From 1225f8c7bc1c66f285273089c33019ce455993d9 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 4 Dec 2012 06:41:20 -0500 Subject: [PATCH 243/281] minor changes --- lib/client.js | 45 ++++++++++------------------------ lib/client/keyBinding.js | 7 ++++-- lib/client/storage/_dropbox.js | 33 +++++++++++++------------ lib/client/storage/_github.js | 27 ++++++++++---------- 4 files changed, 49 insertions(+), 63 deletions(-) diff --git a/lib/client.js b/lib/client.js index 9a94064f..3e7c61ab 100644 --- a/lib/client.js +++ b/lib/client.js @@ -97,15 +97,18 @@ var loadModule = function(pParams){ if( !Util.isContainStr(lPath, '.js') ) lPath += '.js'; - cloudcmd[lName] = function(pArg){ - - lPath = Util.exec(lDoBefore) || lPath; - - return DOM.jsload(cloudcmd.LIBDIRCLIENT + lPath, lFunc || - function(){ - cloudcmd[lName].Keys(pArg); - }); - }; + if(!cloudcmd[lName]) + cloudcmd[lName] = function(pArg){ + + Util.exec(lDoBefore); + + return DOM.jsload(cloudcmd.LIBDIRCLIENT + lPath, lFunc || + function(){ + cloudcmd[lName].Keys(pArg); + }); + }; + else + cloudcmd[lName + lPath]; }; CloudClient.GoogleAnalytics = function(){ @@ -344,9 +347,6 @@ function initModules(pCallBack){ DOM.ajax({ url:'/modules.json', success: function(pModules){ - const DROPBOX = 'storage/_dropbox', - GITHUB = 'storage/_github'; - var lShowLoadFunc = Util.retFunc( DOM.Images.showLoad ), lDisableMenuFunc = function(){ var lFunc = document.oncontextmenu; @@ -355,32 +355,13 @@ function initModules(pCallBack){ return cloudcmd.Menu.ENABLED || false; }; }, - lStorage = function(){ - const lKEY = cloudcmd.KEY; - - var lRet = false; - - switch(event.keyCode){ - case lKEY.G: - lRet = GITHUB; - break; - - case lKEY.D: - lRet = DROPBOX; - break; - } - - return lRet && (lRet + '.js'); - }, lDoBefore = { 'editor/_codemirror' : lShowLoadFunc, 'menu' : lDisableMenuFunc, 'viewer' : lShowLoadFunc }; - - lDoBefore[GITHUB] = - lDoBefore[DROPBOX] = lStorage; + lDisableMenuFunc(); diff --git a/lib/client/keyBinding.js b/lib/client/keyBinding.js index 3091625a..caddda62 100644 --- a/lib/client/keyBinding.js +++ b/lib/client/keyBinding.js @@ -78,7 +78,10 @@ var CloudCommander, Util, DOM; Util.exec(cloudcmd.Config); } - else if( (lKeyCode === KEY.G || lKeyCode === KEY.D) && event.altKey){ + else if(lKeyCode === KEY.G && event.altKey) + Util.exec(cloudcmd.Storage); + + else if(lKeyCode === KEY.D && event.altKey){ Util.exec(cloudcmd.Storage); event.preventDefault(); } @@ -117,7 +120,7 @@ var CloudCommander, Util, DOM; DOM.removeCurrent(lCurrentFile); /* if f3 or shift+f3 or alt+f3 pressed */ - else if(lKeyCode === KEY.F3){ + else if(lKeyCode === KEY.F3){ var lEditor = cloudcmd[event.shiftKey ? 'Viewer' : 'Editor']; diff --git a/lib/client/storage/_dropbox.js b/lib/client/storage/_dropbox.js index d930dbda..4fc5e26e 100644 --- a/lib/client/storage/_dropbox.js +++ b/lib/client/storage/_dropbox.js @@ -1,23 +1,24 @@ -var CloudCommander, DOM, Dropbox; //o7d6llji052vijk +var CloudCommander, Util, DOM, Dropbox; /* module for work with github */ (function(){ "use strict"; - var cloudcmd = CloudCommander, - CLIENT_ID, - DropBoxStore = {}, - options = { - linkType: "direct", - success: function(files) { - console.log("Here's the file link:" + files[0].link); - }, - cancel: function() { - console.log('Chose something'); - } - }; - + const cloudcmd = CloudCommander; + var CLIENT_ID, + Storage = cloudcmd.Storage, + DropBoxStore = {}, + options = { + linkType: "direct", + success: function(files) { + console.log("Here's the file link:" + files[0].link); + }, + cancel: function() { + console.log('Chose something'); + } + }; + /* PRIVATE FUNCTIONS */ /** @@ -42,9 +43,9 @@ var CloudCommander, DOM, Dropbox; //o7d6llji052vijk Dropbox.choose(options); }; - cloudcmd.Storage.Keys = function(){ + Storage.Keys = function(){ load(); }; - cloudcmd.Storage.DropBoxStore = DropBoxStore; + Storage.DropBoxStore = DropBoxStore; })(); diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index 86e8f093..aa0d9178 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -4,17 +4,18 @@ var CloudCommander, Util, DOM, $, Github, cb; (function(){ "use strict"; - var cloudcmd = CloudCommander, - - APIURL = '/api/v1', - AuthURL = APIURL + '/auth', - GitHubIdURL = APIURL + '/github_id', - GitHub_ID, - Cache = DOM.Cache, - GithubLocal, - User, - GithubStore = {}; + const cloudcmd = CloudCommander, + Cache = DOM.Cache, + + APIURL = '/api/v1', + AuthURL = APIURL + '/auth', + GitHubIdURL = APIURL + '/github_id'; + var GitHub_ID, + GithubLocal, + User, + Storage = cloudcmd.Storage = {}, + GithubStore = {}; /* temporary callback function for work with github */ cb = function (err, data){ console.log(err || data);}; @@ -157,8 +158,8 @@ var CloudCommander, Util, DOM, $, Github, cb; User.show(null, lShowUserInfo); } - - cloudcmd.Storage.Keys = function(){ + + Storage.Keys = function(){ Util.loadOnLoad([ getUserData, init, @@ -167,5 +168,5 @@ var CloudCommander, Util, DOM, $, Github, cb; ]); }; - cloudcmd.Storage.GithubStore = GithubStore; + Storage.GithubStore = GithubStore; })(); From 0868aceeedf639c881996b5e9f4274c90670dfee Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 4 Dec 2012 07:23:54 -0500 Subject: [PATCH 244/281] changed name of Keys function to init in modules --- lib/client.js | 5 ++--- lib/client/editor/_codemirror.js | 2 +- lib/client/menu.js | 2 +- lib/client/storage/_github.js | 2 +- lib/client/terminal.js | 2 +- lib/client/viewer.js | 2 +- 6 files changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/client.js b/lib/client.js index 3e7c61ab..e9f56c6b 100644 --- a/lib/client.js +++ b/lib/client.js @@ -85,7 +85,7 @@ var loadModule = function(pParams){ if(lPath && !lName){ lName = lPath[0].toUpperCase() + lPath.substring(1); - lName = lName.replace('.js', ''); + lName = Util.removeStr(lName, '.js'); var lSlash = lName.indexOf('/'); if(lSlash > 0){ @@ -99,12 +99,11 @@ var loadModule = function(pParams){ if(!cloudcmd[lName]) cloudcmd[lName] = function(pArg){ - Util.exec(lDoBefore); return DOM.jsload(cloudcmd.LIBDIRCLIENT + lPath, lFunc || function(){ - cloudcmd[lName].Keys(pArg); + cloudcmd[lName].init(pArg); }); }; else diff --git a/lib/client/editor/_codemirror.js b/lib/client/editor/_codemirror.js index 63ed80c8..8efe50a3 100644 --- a/lib/client/editor/_codemirror.js +++ b/lib/client/editor/_codemirror.js @@ -214,7 +214,7 @@ var CloudCommander, Util, DOM, CloudFunc, CodeMirror; /** * function bind keys */ - cloudcmd.Editor.Keys = function(pReadOnly){ + cloudcmd.Editor.init = function(pReadOnly){ ReadOnly = pReadOnly; CodeMirrorEditor.show(); diff --git a/lib/client/menu.js b/lib/client/menu.js index 8a79edff..933ba63c 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -198,7 +198,7 @@ var CloudCommander, Util, DOM, CloudFunc, $; }; /* key binding function */ - Menu.Keys = function(pPosition){ + Menu.init = function(pPosition){ Position = pPosition; DOM.jqueryLoad( Util.retLoadOnLoad([ diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index aa0d9178..a738c161 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -159,7 +159,7 @@ var CloudCommander, Util, DOM, $, Github, cb; User.show(null, lShowUserInfo); } - Storage.Keys = function(){ + Storage.init = function(){ Util.loadOnLoad([ getUserData, init, diff --git a/lib/client/terminal.js b/lib/client/terminal.js index 04a862d5..ad11b84b 100644 --- a/lib/client/terminal.js +++ b/lib/client/terminal.js @@ -98,7 +98,7 @@ var CloudCommander, Util, DOM, $; /** * function bind keys */ - cloudcmd.Terminal.Keys = function(){ + cloudcmd.Terminal.init = function(){ /* loading js and css*/ DOM.jqueryLoad( Util.retLoadOnLoad([ JqueryTerminal.show, diff --git a/lib/client/viewer.js b/lib/client/viewer.js index 194e143b..d2c62cb6 100644 --- a/lib/client/viewer.js +++ b/lib/client/viewer.js @@ -187,7 +187,7 @@ var CloudCommander, Util, DOM, CloudFunc, $; } }; - cloudcmd.Viewer.Keys = function(){ + cloudcmd.Viewer.init = function(){ DOM.jqueryLoad( Util.retLoadOnLoad([ FancyBox.show, FancyBox.load From 7e5de6d3cf8778026f0e57770e1d738f841defe0 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 4 Dec 2012 08:06:19 -0500 Subject: [PATCH 245/281] minor changes --- lib/client/storage/_dropbox.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/client/storage/_dropbox.js b/lib/client/storage/_dropbox.js index 4fc5e26e..14e6d3b4 100644 --- a/lib/client/storage/_dropbox.js +++ b/lib/client/storage/_dropbox.js @@ -43,7 +43,7 @@ var CloudCommander, Util, DOM, Dropbox; Dropbox.choose(options); }; - Storage.Keys = function(){ + Storage.init = function(){ load(); }; From d30b4f7742db859f8b7e4fd84526fff0a1d9afff Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 4 Dec 2012 09:27:28 -0500 Subject: [PATCH 246/281] minor changes --- cloudcmd.js | 1 - lib/client.js | 16 ++++++++++------ lib/client/keyBinding.js | 4 ++-- lib/client/storage/_dropbox.js | 6 +++--- lib/client/storage/_github.js | 11 ++++++----- 5 files changed, 21 insertions(+), 17 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index efe5ab2f..9f6f4ad3 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -33,7 +33,6 @@ /** * additional processing of index file - * */ function indexProcessing(pData){ var lReplace_s, diff --git a/lib/client.js b/lib/client.js index e9f56c6b..c5baf5fe 100644 --- a/lib/client.js +++ b/lib/client.js @@ -333,7 +333,6 @@ CloudClient.init = function(){ }; function initModules(pCallBack){ - loadModule({ /* привязываем клавиши к функциям */ path : 'keyBinding.js', @@ -346,7 +345,8 @@ function initModules(pCallBack){ DOM.ajax({ url:'/modules.json', success: function(pModules){ - var lShowLoadFunc = Util.retFunc( DOM.Images.showLoad ), + var lStorage = 'storage/', + lShowLoadFunc = Util.retFunc( DOM.Images.showLoad ), lDisableMenuFunc = function(){ var lFunc = document.oncontextmenu; document.oncontextmenu = function(){ @@ -359,8 +359,11 @@ function initModules(pCallBack){ 'editor/_codemirror' : lShowLoadFunc, 'menu' : lDisableMenuFunc, 'viewer' : lShowLoadFunc - }; - + }, + + lNames = {}; + lNames[lStorage + '_dropbox'] = 'DropBox', + lNames[lStorage + '_github' ] = 'GitHub', lDisableMenuFunc(); @@ -369,8 +372,9 @@ function initModules(pCallBack){ var lModule = pModules[i]; loadModule({ - path: lModule, - dobefore: lDoBefore[lModule] + path : lModule, + dobefore : lDoBefore[lModule], + name : lNames[lModule] }); } diff --git a/lib/client/keyBinding.js b/lib/client/keyBinding.js index caddda62..ec5f48e0 100644 --- a/lib/client/keyBinding.js +++ b/lib/client/keyBinding.js @@ -79,10 +79,10 @@ var CloudCommander, Util, DOM; } else if(lKeyCode === KEY.G && event.altKey) - Util.exec(cloudcmd.Storage); + Util.exec(cloudcmd.GitHub); else if(lKeyCode === KEY.D && event.altKey){ - Util.exec(cloudcmd.Storage); + Util.exec(cloudcmd.DropBox); event.preventDefault(); } diff --git a/lib/client/storage/_dropbox.js b/lib/client/storage/_dropbox.js index 14e6d3b4..409c383c 100644 --- a/lib/client/storage/_dropbox.js +++ b/lib/client/storage/_dropbox.js @@ -7,7 +7,6 @@ var CloudCommander, Util, DOM, Dropbox; const cloudcmd = CloudCommander; var CLIENT_ID, - Storage = cloudcmd.Storage, DropBoxStore = {}, options = { linkType: "direct", @@ -43,9 +42,10 @@ var CloudCommander, Util, DOM, Dropbox; Dropbox.choose(options); }; - Storage.init = function(){ + DropBoxStore.init = function(){ load(); + this.init = null; }; - Storage.DropBoxStore = DropBoxStore; + cloudcmd.DropBox = DropBoxStore; })(); diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index a738c161..2972f1c7 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -14,7 +14,6 @@ var CloudCommander, Util, DOM, $, Github, cb; var GitHub_ID, GithubLocal, User, - Storage = cloudcmd.Storage = {}, GithubStore = {}; /* temporary callback function for work with github */ @@ -65,7 +64,7 @@ var CloudCommander, Util, DOM, $, Github, cb; /* PUBLICK FUNCTIONS */ GithubStore.basicLogin = function(pUser, pPasswd){ - cloudcmd.Storage.Github = GithubLocal = new Github({ + GithubLocal = new Github({ username: pUser, password: pPasswd, auth : 'basic' @@ -73,7 +72,7 @@ var CloudCommander, Util, DOM, $, Github, cb; }; GithubStore.Login = function(pToken){ - cloudcmd.Storage.Github = Github = GithubLocal = new Github({ + Github = GithubLocal = new Github({ token : pToken, auth : 'oauth' }); @@ -159,14 +158,16 @@ var CloudCommander, Util, DOM, $, Github, cb; User.show(null, lShowUserInfo); } - Storage.init = function(){ + cloudcmd.GitHub.init = function(){ Util.loadOnLoad([ getUserData, init, setConfig, load ]); + + this.init = null; }; - Storage.GithubStore = GithubStore; + cloudcmd.Github = GithubStore; })(); From 4fec011c54e9d3b77274a1684c05b28b7ce5155b Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 4 Dec 2012 09:46:15 -0500 Subject: [PATCH 247/281] added ability to read dropbox key from config --- ChangeLog | 2 ++ config.json | 4 ++-- lib/client.js | 4 +++- lib/client/storage/_dropbox.js | 22 +++++++++++++--------- lib/client/storage/_github.js | 2 +- 5 files changed, 21 insertions(+), 13 deletions(-) diff --git a/ChangeLog b/ChangeLog index 29f3aa05..4b455fbd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -154,6 +154,8 @@ Added dropbox_id option. * Added ability to call dropbox chooser on + . +* Added ability to read dropbox key from config. + 2012.10.01, Version 0.1.7 diff --git a/config.json b/config.json index bf8a5656..865518b4 100644 --- a/config.json +++ b/config.json @@ -9,9 +9,9 @@ }, "github_id" : "891c251b925e4e967fa9", "github_secret" : "afe9bed1e810c5dc44c4c2a953fc6efb1e5b0545", - "dropbox_id" : "0nd3ssnp5fp7tqs", + "dropbox_key" : "0nd3ssnp5fp7tqs", "dropbox_secret" : "r61lxpchmk8l06o", - "dropbox_chooser_id" : "o7d6llji052vijk", + "dropbox_chooser_key" : "o7d6llji052vijk", "show_keys_panel" : true, "server" : true, "logs" : false, diff --git a/lib/client.js b/lib/client.js index c5baf5fe..fd5b770c 100644 --- a/lib/client.js +++ b/lib/client.js @@ -484,7 +484,7 @@ function baseInit(pCallBack){ cloudcmd.KeyBinding(); } -CloudClient.loadConfig = function(pCallBack){ +CloudClient.getConfig = function(pCallBack){ if(!cloudcmd.Config) return DOM.ajax({ url:'/config.json', @@ -494,6 +494,8 @@ CloudClient.loadConfig = function(pCallBack){ Util.exec(pCallBack, pConfig); } }); + else + Util.exec(pCallBack, cloudcmd.Config); }; diff --git a/lib/client/storage/_dropbox.js b/lib/client/storage/_dropbox.js index 409c383c..2c174b74 100644 --- a/lib/client/storage/_dropbox.js +++ b/lib/client/storage/_dropbox.js @@ -26,16 +26,20 @@ var CloudCommander, Util, DOM, Dropbox; function load(){ console.time('dropbox load'); - var lElement = DOM.anyload({ - src : 'https://www.dropbox.com/static/api/1/dropbox.js', - not_append : true, - id : 'dropboxjs', - func : DropBoxStore.choose - + cloudcmd.getConfig(function(pConfig){ + var lElement = DOM.anyload({ + src : 'https://www.dropbox.com/static/api/1/dropbox.js', + not_append : true, + id : 'dropboxjs', + func : DropBoxStore.choose + + }); + + var lDropBoxId = pConfig.dropbox_chooser_key; + lElement.setAttribute('data-app-key', lDropBoxId); + document.body.appendChild(lElement); + console.timeEnd('dropbox load'); }); - - lElement.setAttribute('data-app-key', 'o7d6llji052vijk'); - document.body.appendChild(lElement); } DropBoxStore.choose = function(){ diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index 2972f1c7..a043ec41 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -4,7 +4,7 @@ var CloudCommander, Util, DOM, $, Github, cb; (function(){ "use strict"; - const cloudcmd = CloudCommander, + const cloudcmd = CloudCommander, Cache = DOM.Cache, APIURL = '/api/v1', From 82071db1476c2dbc0495c10b747ead4d8346e631 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 4 Dec 2012 10:05:46 -0500 Subject: [PATCH 248/281] added description --- lib/server/auth.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/server/auth.js b/lib/server/auth.js index 53a8e25c..df87ee0e 100644 --- a/lib/server/auth.js +++ b/lib/server/auth.js @@ -1,13 +1,24 @@ /* https://github.com/prose/gatekeeper */ (function(){ "use strict"; - + + if(!global.cloudcmd) + return console.log( + '# auth.js' + '\n' + + '# -----------' + '\n' + + '# Module is part of Cloud Commander,' + '\n' + + '# used for work with authentication.' + '\n' + + '# If you wont to see at work set auth' + '\n' + + '# parameters in config.json or environment' + '\n' + + '# and start cloudcmd.js or just do' + '\n' + + '# require(\'auth.js\').auth(pCode, pCallBack)' + '\n'); + var main = global.cloudcmd.main, https = main.https, qs = main.querystring, - Config = main.config, + Config = main.config, Util = main.util, GithubAuth = { From 08065167c8d1462aed68e59041f3235dbad21000 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 5 Dec 2012 03:47:07 -0500 Subject: [PATCH 249/281] fixed bug with menu load, when dir other then root --- ChangeLog | 2 ++ lib/client/menu.js | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 4b455fbd..4d8ba647 100644 --- a/ChangeLog +++ b/ChangeLog @@ -156,6 +156,8 @@ Added dropbox_id option. * Added ability to read dropbox key from config. +* Fixed bug with menu load, when dir other then root. + 2012.10.01, Version 0.1.7 diff --git a/lib/client/menu.js b/lib/client/menu.js index 933ba63c..ec6069ed 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -11,7 +11,7 @@ var CloudCommander, Util, DOM, CloudFunc, $; Menu = {}, Position; - Menu.dir = './lib/client/menu/'; + Menu.dir = '/lib/client/menu/'; /* enable and disable menu constant */ Menu.ENABLED = false; From 9c664055be968128e66e286d4fe9f8804fe8e476 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 5 Dec 2012 04:16:07 -0500 Subject: [PATCH 250/281] added confirmation before (not real) deleting file --- ChangeLog | 2 ++ lib/client/dom.js | 37 +++++++++++++++++++++++++------------ lib/client/keyBinding.js | 2 +- lib/client/menu.js | 14 ++++++++++++-- 4 files changed, 40 insertions(+), 15 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4d8ba647..6c2ead23 100644 --- a/ChangeLog +++ b/ChangeLog @@ -158,6 +158,8 @@ Added dropbox_id option. * Fixed bug with menu load, when dir other then root. +* Added confirmation before (not real) deleting file. + 2012.10.01, Version 0.1.7 diff --git a/lib/client/dom.js b/lib/client/dom.js index 9f884d85..5d30f489 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -265,6 +265,19 @@ var CloudCommander, Util, DOM, CloudFunc; DOM.Cache = new DOM.Cache(); + /** + * delete currentfile, prompt before it + * + * @pCurrentFile + */ + DOM.delete = function(pCurrentFile){ + var lMsg = 'Are you sure thet you wont delete ', + lName = DOM.getCurrentName(pCurrentFile), + lRet = confirm(lMsg + lName + '?'); + + if(lRet) + DOM.removeCurrent(pCurrentFile); + }; /** * Function gets id by src @@ -719,9 +732,11 @@ var CloudCommander, Util, DOM, CloudFunc; /** * unified way to get current file + * + * @pCurrentFile */ - DOM.getCurrentFile = function(){ - var lCurrent = DOM.getByClass(getCurrentFile())[0]; + DOM.getCurrentFile = function(pCurrentFile){ + var lCurrent = DOM.getByClass( pCurrentFile || getCurrentFile() )[0]; if(!lCurrent){ DOM.addCloudStatus({ code : -1, @@ -998,22 +1013,20 @@ var CloudCommander, Util, DOM, CloudFunc; * @pCurrent */ DOM.removeCurrent = function(pCurrent){ - var lParent = pCurrent.parentElement; + var lCurrent = pCurrent || DOM.getCurrentFile(), + lParent = lCurrent.parentElement, + lName = DOM.getCurrentName(lCurrent); - if(!pCurrent) - pCurrent = DOM.getCurrentFile(); - var lName = DOM.getCurrentName(pCurrent); - - if(pCurrent && lParent){ + if(lCurrent && lParent){ if(lName !== '..'){ - var lNext = pCurrent.nextSibling; - var lPrevious = pCurrent.previousSibling; + var lNext = lCurrent.nextSibling; + var lPrevious = lCurrent.previousSibling; if(lNext) DOM.setCurrentFile(lNext); else if(lPrevious) DOM.setCurrentFile(lPrevious); - lParent.removeChild(pCurrent); + lParent.removeChild(lCurrent); } else DOM.addCloudStatus({ @@ -1027,7 +1040,7 @@ var CloudCommander, Util, DOM, CloudFunc; msg : 'Current file (or parent of current) could not be empty' }); - return pCurrent; + return lCurrent; }; /** diff --git a/lib/client/keyBinding.js b/lib/client/keyBinding.js index ec5f48e0..51cc700d 100644 --- a/lib/client/keyBinding.js +++ b/lib/client/keyBinding.js @@ -117,7 +117,7 @@ var CloudCommander, Util, DOM; event.preventDefault();//запрет на дальнейшее действие } else if(lKeyCode === KEY.Delete) - DOM.removeCurrent(lCurrentFile); + DOM.delete(lCurrentFile); /* if f3 or shift+f3 or alt+f3 pressed */ else if(lKeyCode === KEY.F3){ diff --git a/lib/client/menu.js b/lib/client/menu.js index ec6069ed..ea38f014 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -57,11 +57,21 @@ var CloudCommander, Util, DOM, CloudFunc, $; showEditor(); }}, - 'delete': {name: 'Delete', + delete: {name: 'Delete', callback: function(key, opt){ - console.log('delete menu item choosen'); + DOM.delete(); }}, + upload: { + name: "Upload", + items: { + 'upload_to_gist': {name: 'to Gist', + callback: function(key, opt){ + console.log('Upload To Gist'); + } + }, + }, + }, download: {name: 'Download',callback: function(key, opt){ DOM.Images.showLoad(); From 71919aa2062b4f4d3447caa50b93957b441ff534 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 5 Dec 2012 06:32:59 -0500 Subject: [PATCH 251/281] added ability to upload file to gists on github thru menu --- ChangeLog | 2 + lib/client.js | 6 +- lib/client/editor/_codemirror.js | 17 ++--- lib/client/menu.js | 34 ++++++++- lib/client/storage/_github.js | 121 +++++++++++++++---------------- lib/util.js | 19 ++++- 6 files changed, 116 insertions(+), 83 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6c2ead23..57989950 100644 --- a/ChangeLog +++ b/ChangeLog @@ -160,6 +160,8 @@ Added dropbox_id option. * Added confirmation before (not real) deleting file. +* Added ability to upload file to gists on github thru menu. + 2012.10.01, Version 0.1.7 diff --git a/lib/client.js b/lib/client.js index fd5b770c..a500906c 100644 --- a/lib/client.js +++ b/lib/client.js @@ -90,7 +90,7 @@ var loadModule = function(pParams){ var lSlash = lName.indexOf('/'); if(lSlash > 0){ var lAfterSlash = lName.substr(lSlash); - lName = lName.replace(lAfterSlash, ''); + lName = Util.removeStr(lName, lAfterSlash); } } @@ -103,11 +103,9 @@ var loadModule = function(pParams){ return DOM.jsload(cloudcmd.LIBDIRCLIENT + lPath, lFunc || function(){ - cloudcmd[lName].init(pArg); + Util.exec(cloudcmd[lName].init, pArg); }); }; - else - cloudcmd[lName + lPath]; }; CloudClient.GoogleAnalytics = function(){ diff --git a/lib/client/editor/_codemirror.js b/lib/client/editor/_codemirror.js index 8efe50a3..b209215c 100644 --- a/lib/client/editor/_codemirror.js +++ b/lib/client/editor/_codemirror.js @@ -127,7 +127,7 @@ var CloudCommander, Util, DOM, CloudFunc, CodeMirror; lA = lA.href; /* убираем адрес хоста*/ - lA = lA.replace(cloudcmd.HOST, ''); + lA = Util.removeStr(lA, cloudcmd.HOST); /* checking is this link is to directory */ var lSize = DOM.getByClass('size', lCurrentFile); @@ -138,15 +138,12 @@ var CloudCommander, Util, DOM, CloudFunc, CodeMirror; * not html data */ if (lSize === '

    '){ - var lIndexOfNOJS = lA.indexOf(CloudFunc.NOJS); - if (lIndexOfNOJS === CloudFunc.FS.length){ - lA = lA.replace(CloudFunc.NOJS, ''); - /* when folder view - * is no need to edit - * data - */ - ReadOnly = true; - } + lA = Util.removeStr(lA, CloudFunc.NOJS); + /* when folder view + * is no need to edit + * data + */ + ReadOnly = true; } } diff --git a/lib/client/menu.js b/lib/client/menu.js index ea38f014..0d143c26 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -67,7 +67,33 @@ var CloudCommander, Util, DOM, CloudFunc, $; items: { 'upload_to_gist': {name: 'to Gist', callback: function(key, opt){ - console.log('Upload To Gist'); + var lCurrent = DOM.getCurrentFile(), + lA = DOM.getCurrentLink(lCurrent).href, + lName = DOM.getCurrentName(lCurrent); + /* убираем адрес хоста*/ + lA = Util.removeStr(lA, cloudcmd.HOST); + lA = Util.removeStr(lA, CloudFunc.NOJS); + + DOM.ajax({ + url : lA, + error : DOM.Images.showError, + success : function(data, textStatus, jqXHR){ + if( Util.isObject(data) ) + data = JSON.stringify(data, null, 4); + + var lGitHub = cloudcmd.GitHub; + + if('init' in lGitHub) + lGitHub.createGist(data, lName); + else + Util.exec(cloudcmd.GitHub, + function(){ + lGitHub.createGist(data, lName); + }); + } + }); + + Util.log('Uploading to gist...'); } }, }, @@ -159,9 +185,9 @@ var CloudCommander, Util, DOM, CloudFunc, $; .replace('z-index: 1', 'z-index:-1'); /* get element by point */ - var lElement = document.elementFromPoint(pEvent.x, pEvent.y); - var lTag = lElement.tagName; - var lParent; + var lElement = document.elementFromPoint(pEvent.x, pEvent.y), + lTag = lElement.tagName, + lParent; if(lTag === 'A' || lTag === 'SPAN'){ if (lElement.tagName === 'A') diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index a043ec41..5c3bf5b8 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -27,7 +27,7 @@ var CloudCommander, Util, DOM, $, Github, cb; function load(pCallBack){ console.time('github load'); - var lDir = './lib/client/storage/github/', + var lDir = '/lib/client/storage/github/', lFiles = [ lDir + 'github.js', lDir + 'lib/base64.js', @@ -53,35 +53,8 @@ var CloudCommander, Util, DOM, $, Github, cb; }); } - function saveToken(pToken){ - return Cache.set('token', pToken); - } - - function getToken(){ - return Cache.get('token'); - } - - - /* PUBLICK FUNCTIONS */ - GithubStore.basicLogin = function(pUser, pPasswd){ - GithubLocal = new Github({ - username: pUser, - password: pPasswd, - auth : 'basic' - }); - }; - - GithubStore.Login = function(pToken){ - Github = GithubLocal = new Github({ - token : pToken, - auth : 'oauth' - }); - - User = GithubLocal.getUser(); - }; - - function init(pCallBack){ - var lToken = getToken(); + function init(pCallBack){ + var lToken = Cache.get('token'); if(lToken){ GithubStore.Login(lToken); Util.exec(pCallBack); @@ -100,7 +73,7 @@ var CloudCommander, Util, DOM, $, Github, cb; lToken = pData.token; GithubStore.Login(lToken); - saveToken(lToken); + Cache.set('token', lToken); Util.exec(pCallBack); } else @@ -116,58 +89,80 @@ var CloudCommander, Util, DOM, $, Github, cb; } } - function getUserData(){ - var lName, lRepoNames, - lGetTree = function(pError ,pData){ - Util.log('Tree of ripository ' + lRepoNames[0].name + ': '); - var lTree = pData || []; - if(!pError) - for(var i = 0, n = lTree.length; i < n ; i++) - if( !Util.isContainStr(lTree[i].path, '/') ) - console.log(lTree[i].path); - else - Util.log(pError); - }, - - lShowRepos = function(pError, pRepos){ - lRepoNames = pRepos || []; - Util.log('Repositories: '); - if(!pError){ - for(var i = 0, n = pRepos.length; i < n ; i++) - console.log(pRepos[i].name); - - var lRepo = GithubLocal.getRepo(lName, pRepos[0].name); - lRepo.getTree('master?recursive=true', lGetTree); - } - else - DOM.Cache.remove('token'); - }, + function getUserData(pCallBack){ + var lName, lShowUserInfo = function(pError, pData){ if(!pError){ lName = pData.name; console.log('Hello ' + lName + ' :)!'); - User.repos(lShowRepos); } else DOM.Cache.remove('token'); }; - - + User.show(null, lShowUserInfo); + + Util.exec(pCallBack); } - cloudcmd.GitHub.init = function(){ + /* PUBLICK FUNCTIONS */ + GithubStore.basicLogin = function(pUser, pPasswd){ + GithubLocal = new Github({ + username: pUser, + password: pPasswd, + auth : 'basic' + }); + }; + + GithubStore.Login = function(pToken){ + Github = GithubLocal = new Github({ + token : pToken, + auth : 'oauth' + }); + + User = GithubLocal.getUser(); + }; + + /** + * function creates gist + */ + cloudcmd.GitHub.createGist = function(pContent, pFileName){ + if(pContent){ + if(!pFileName) + pFileName = Util.getDate(); + + var lGist = GithubLocal.getGist(), + lFiles = {}, + lOptions = { + description: "uploaded from cloudcmd", + public: true + }; + + lFiles[pFileName] ={ + content: pContent + }; + + lOptions.files = lFiles; + + lGist.create(lOptions, cb); + } + + return pContent; + }; + + cloudcmd.GitHub.init = function(pCallBack){ Util.loadOnLoad([ + Util.retExec(pCallBack), getUserData, init, setConfig, load ]); - this.init = null; + cloudcmd.GitHub.init = null; }; - cloudcmd.Github = GithubStore; + cloudcmd.Github = GithubStore; })(); diff --git a/lib/util.js b/lib/util.js index 4c2d3372..9143bc54 100644 --- a/lib/util.js +++ b/lib/util.js @@ -363,7 +363,8 @@ var Util, exports; * Gets current time in format hh:mm:ss */ Util.getTime = function(){ - var date = new Date(), + var lRet, + date = new Date(), hours = date.getHours(), minutes = date.getMinutes(), seconds = date.getSeconds(); @@ -371,7 +372,21 @@ var Util, exports; minutes = minutes < 10 ? '0' + minutes : minutes; seconds = seconds < 10 ? '0' + seconds : seconds; - return hours + ":" + minutes + ":" + seconds; + lRet = hours + ":" + minutes + ":" + seconds; + + return lRet; + }; + /** + * Gets current date in format yy.mm.dd hh:mm:ss + */ + Util.getDate = function(){ + var date = new Date(), + day = date.getDate(), + month = date.getMonth() + 1, + year = date.getFullYear(), + lRet = year + "-" + month + "-" + day + " " + Util.getTime(); + + return lRet; }; })(); \ No newline at end of file From 4a2b975d098a2e17bcc68ed297d9da744d1a827c Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 5 Dec 2012 06:55:32 -0500 Subject: [PATCH 252/281] added getCurrentPath function to Util module --- ChangeLog | 2 ++ lib/client/dom.js | 15 +++++++++++++++ lib/client/editor/_codemirror.js | 24 ++++++++---------------- lib/client/menu.js | 9 +++------ 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/ChangeLog b/ChangeLog index 57989950..fc951cef 100644 --- a/ChangeLog +++ b/ChangeLog @@ -162,6 +162,8 @@ Added dropbox_id option. * Added ability to upload file to gists on github thru menu. +* Added getCurrentPath function to Util module. + 2012.10.01, Version 0.1.7 diff --git a/lib/client/dom.js b/lib/client/dom.js index 5d30f489..4b19067b 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -888,6 +888,21 @@ var CloudCommander, Util, DOM, CloudFunc; return lRet; }; + /** + * get link from current (or param) file + * + * @param pCurrentFile - current file by default + */ + DOM.getCurrentPath = function(pCurrentFile){ + var lCurrent = pCurrentFile || DOM.getCurrentFile(), + lPath = DOM.getCurrentLink(lCurrent).href; + /* убираем адрес хоста*/ + lPath = Util.removeStr(lPath, CloudCommander.HOST); + lPath = Util.removeStr(lPath, CloudFunc.NOJS); + + return lPath; + }; + /** * get name from current (or param) file * diff --git a/lib/client/editor/_codemirror.js b/lib/client/editor/_codemirror.js index b209215c..633480b4 100644 --- a/lib/client/editor/_codemirror.js +++ b/lib/client/editor/_codemirror.js @@ -123,28 +123,20 @@ var CloudCommander, Util, DOM, CloudFunc, CodeMirror; /* getting link */ var lCurrentFile = DOM.getCurrentFile(), - lA = DOM.getCurrentLink(lCurrentFile); - lA = lA.href; - - /* убираем адрес хоста*/ - lA = Util.removeStr(lA, cloudcmd.HOST); + lPath = DOM.getCurrentPath(lCurrentFile); /* checking is this link is to directory */ var lSize = DOM.getByClass('size', lCurrentFile); if(lSize){ lSize = lSize[0].textContent; - /* if directory - load json - * not html data + /* when folder view + * is no need to edit + * data */ - if (lSize === ''){ - lA = Util.removeStr(lA, CloudFunc.NOJS); - /* when folder view - * is no need to edit - * data - */ - ReadOnly = true; - } + if (lSize === '') + ReadOnly = true; + } Loading = true; @@ -155,7 +147,7 @@ var CloudCommander, Util, DOM, CloudFunc, CodeMirror; /* reading data from current file */ DOM.ajax({ - url:lA, + url:lPath, error: function(jqXHR, textStatus, errorThrown){ Loading = false; return DOM.Images.showError(jqXHR); diff --git a/lib/client/menu.js b/lib/client/menu.js index 0d143c26..4ed5b91d 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -68,14 +68,11 @@ var CloudCommander, Util, DOM, CloudFunc, $; 'upload_to_gist': {name: 'to Gist', callback: function(key, opt){ var lCurrent = DOM.getCurrentFile(), - lA = DOM.getCurrentLink(lCurrent).href, + lPath = DOM.getCurrentPath(), lName = DOM.getCurrentName(lCurrent); - /* убираем адрес хоста*/ - lA = Util.removeStr(lA, cloudcmd.HOST); - lA = Util.removeStr(lA, CloudFunc.NOJS); - + DOM.ajax({ - url : lA, + url : lPath, error : DOM.Images.showError, success : function(data, textStatus, jqXHR){ if( Util.isObject(data) ) From fa1caadede32247bcc9a1b378009beaf17626371 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 5 Dec 2012 07:41:25 -0500 Subject: [PATCH 253/281] minor changes --- lib/client/storage/_github.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index 5c3bf5b8..58b0cdc2 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -135,8 +135,10 @@ var CloudCommander, Util, DOM, $, Github, cb; var lGist = GithubLocal.getGist(), lFiles = {}, + lHost = CloudCommander.HOST, lOptions = { - description: "uploaded from cloudcmd", + description: "Uplouded by Cloud Commander from " + lHost, + public: true }; From d46c17d46ae3a511c5a22a12b87a3c41e408401d Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 5 Dec 2012 10:25:33 -0500 Subject: [PATCH 254/281] added shell directory with shell files --- config.json | 39 ++++++++++++++---------------- lib/client/editor/_codemirror.js | 2 +- lib/client/storage/_dropbox.js | 12 +++++---- lib/server/rest.js | 24 +++++++++++++----- shell/c9kill.sh | 12 +++++++++ cloudcmd.bat => shell/cloudcmd.bat | 2 +- shell/deploy.sh | 10 ++++++++ install-dev => shell/install-dev | 0 shell/kill.js | 17 +++++++++++++ shell/secret.bat | 13 ++++++++++ shell/secret.sh | 13 ++++++++++ 11 files changed, 110 insertions(+), 34 deletions(-) create mode 100755 shell/c9kill.sh rename cloudcmd.bat => shell/cloudcmd.bat (91%) create mode 100755 shell/deploy.sh rename install-dev => shell/install-dev (100%) create mode 100644 shell/kill.js create mode 100644 shell/secret.bat create mode 100644 shell/secret.sh diff --git a/config.json b/config.json index 865518b4..741f2fe2 100644 --- a/config.json +++ b/config.json @@ -1,22 +1,19 @@ -{ - "cache" : {"allowed" : false}, - "appcache" : false, - "minification" : { - "js" : false, - "css" : false, - "html" : false, - "img" : false - }, - "github_id" : "891c251b925e4e967fa9", - "github_secret" : "afe9bed1e810c5dc44c4c2a953fc6efb1e5b0545", - "dropbox_key" : "0nd3ssnp5fp7tqs", - "dropbox_secret" : "r61lxpchmk8l06o", - "dropbox_chooser_key" : "o7d6llji052vijk", - "show_keys_panel" : true, - "server" : true, - "logs" : false, - "socket" : true, - "port" : 80, - "ip" : null, - "rest" : true +{ + "api_url" :"/api/v1", + "appcache" : false, + "cache" : {"allowed" : false}, + "minification" : { + "js" : false, + "css" : false, + "html" : false, + "img" : false + }, + "github_id" : "ec1f2e74b35a361e10bc", + "logs" : false, + "show_keys_panel" : true, + "server" : true, + "socket" : true, + "port" : 80, + "ip" : null, + "rest" : true } \ No newline at end of file diff --git a/lib/client/editor/_codemirror.js b/lib/client/editor/_codemirror.js index 633480b4..bb63ef91 100644 --- a/lib/client/editor/_codemirror.js +++ b/lib/client/editor/_codemirror.js @@ -1,4 +1,4 @@ -var CloudCommander, Util, DOM, CloudFunc, CodeMirror; +var CloudCommander, Util, DOM, CodeMirror; /* object contains editors CodeMirror */ (function(){ "use strict"; diff --git a/lib/client/storage/_dropbox.js b/lib/client/storage/_dropbox.js index 2c174b74..c6fff5c2 100644 --- a/lib/client/storage/_dropbox.js +++ b/lib/client/storage/_dropbox.js @@ -1,17 +1,18 @@ -var CloudCommander, Util, DOM, Dropbox; +var CloudCommander, DOM, Dropbox; /* module for work with github */ (function(){ "use strict"; - const cloudcmd = CloudCommander; - + const cloudcmd = CloudCommander, + CHOOSER_API = 'https://www.dropbox.com/static/api/1/dropbox.js'; + var CLIENT_ID, DropBoxStore = {}, options = { linkType: "direct", success: function(files) { - console.log("Here's the file link:" + files[0].link); + console.log("Here's the file link:" + files[0].link); }, cancel: function() { console.log('Chose something'); @@ -28,7 +29,7 @@ var CloudCommander, Util, DOM, Dropbox; cloudcmd.getConfig(function(pConfig){ var lElement = DOM.anyload({ - src : 'https://www.dropbox.com/static/api/1/dropbox.js', + src : CHOOSER_API, not_append : true, id : 'dropboxjs', func : DropBoxStore.choose @@ -38,6 +39,7 @@ var CloudCommander, Util, DOM, Dropbox; var lDropBoxId = pConfig.dropbox_chooser_key; lElement.setAttribute('data-app-key', lDropBoxId); document.body.appendChild(lElement); + console.timeEnd('dropbox load'); }); } diff --git a/lib/server/rest.js b/lib/server/rest.js index 866c4205..c28766c0 100644 --- a/lib/server/rest.js +++ b/lib/server/rest.js @@ -5,7 +5,8 @@ var main = global.cloudcmd.main, Util = main.util, - APIURL = '/api/v1', + Config = main.config, + APIURL = Config.api_url, OK = 200, Header = main.generateHeaders('api.json', false); @@ -94,7 +95,11 @@ */ function onGET(pParams){ var lResult = {error: 'command not found'}, - lCmd = pParams.command; + lCmd = pParams.command, + lConfig = main.config, + lEnv = process.env, + lEnvId, + lConfigId; switch(lCmd){ case '': @@ -102,12 +107,19 @@ break; case 'github_id': - var lEnv = process.env, - lConfig = main.config; - - lResult = lEnv.github_id || lConfig.github_id; + lEnvId = lEnv.github_id; + lConfigId = lConfig.github_id, + + lResult = lEnvId || lConfigId; break; + case 'dropbox_chooser_id': + lEnvId = lEnv.dropbox_chooser_id; + lConfigId = lConfig.dropbox_chooser_id; + + lResult = lEnvId || lConfigId; + break; + case 'kill': pParams.data = { mesage: 'Cloud Commander was killed' diff --git a/shell/c9kill.sh b/shell/c9kill.sh new file mode 100755 index 00000000..40e87d7e --- /dev/null +++ b/shell/c9kill.sh @@ -0,0 +1,12 @@ +kill -9 `ps ax|grep node-openshift|grep -v grep|awk '{print $1}'` +# print finded process +ProcessList=`ps ax|grep node-openshift` +echo $ProcessList +# getting pid of process +PID=`echo "${ProcessList}"|grep -v grep|awk '{print $1}'` +echo $PID +#kill it +if test ! $PID +then echo 'process not found' +else kill -9 $PID && echo 'killed process' +fi \ No newline at end of file diff --git a/cloudcmd.bat b/shell/cloudcmd.bat similarity index 91% rename from cloudcmd.bat rename to shell/cloudcmd.bat index 4c94262d..280f6023 100644 --- a/cloudcmd.bat +++ b/shell/cloudcmd.bat @@ -6,4 +6,4 @@ :: 866 charset to Unicode 65001 sometime :: when it's neaded. :: ------------------------------------- -node cloudcmd || chcp 866 \ No newline at end of file +node ../cloudcmd || chcp 866 \ No newline at end of file diff --git a/shell/deploy.sh b/shell/deploy.sh new file mode 100755 index 00000000..fdda2c00 --- /dev/null +++ b/shell/deploy.sh @@ -0,0 +1,10 @@ +echo 'appfog' +af update +echo 'http://cloudcmd.aws.af.cm/' +echo 'cloud foundry' +vmc update +echo 'http://cloudcmd.cloudfoundry.com/' +echo 'nodester' +git push nodester master +echo 'heroku' +git push heroku master \ No newline at end of file diff --git a/install-dev b/shell/install-dev similarity index 100% rename from install-dev rename to shell/install-dev diff --git a/shell/kill.js b/shell/kill.js new file mode 100644 index 00000000..7b08dfb5 --- /dev/null +++ b/shell/kill.js @@ -0,0 +1,17 @@ +/* c9.io kill active node process */ + +(function(){ + "use strict"; + + var exec = require('child_process').exec, + lCmd = 'kill -9' + ' ' + /* kill finded process */ + '`ps ax' + '|' + /* show all process */ + 'grep node-openshift' + '|' + /* find node-openshift */ + 'grep -v grep' + '|' + /* exlude grep command */ + 'awk "{print $1}"`'; /* show first collumn */ + + exec(lCmd, function(error, stdout, stderr){ + console.log(error || stdout || stderr); + }); + +})(); diff --git a/shell/secret.bat b/shell/secret.bat new file mode 100644 index 00000000..1c72b43e --- /dev/null +++ b/shell/secret.bat @@ -0,0 +1,13 @@ +# +# part of Cloud Commander +# win32 version +# secrets of github and dropbox +# must not be shared +# http://github.com/coderaiser/cloudcmd +# +# for using just add %-symbol on start and end of name +# like %github_secret% + +github_secret=e21b3724d84f0f7570d2d04d8e055f3cc3be3071 +dropbox_key=0nd3ssnp5fp7tqs +dropbox_chooser_key=o7d6llji052vijk diff --git a/shell/secret.sh b/shell/secret.sh new file mode 100644 index 00000000..16cb1425 --- /dev/null +++ b/shell/secret.sh @@ -0,0 +1,13 @@ +# +# part of Cloud Commander +# *nix version +# secrets of github and dropbox +# must not be shared +# http://github.com/coderaiser/cloudcmd +# +# for using just add $-symbol on start of name +# like $github_secret + +export github_secret=e21b3724d84f0f7570d2d04d8e055f3cc3be3071 +export dropbox_key=0nd3ssnp5fp7tqs +export dropbox_chooser_key=o7d6llji052vijk From 49d290c500b8a5a9d9897ea6133fcdef493b0e86 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 5 Dec 2012 10:26:08 -0500 Subject: [PATCH 255/281] minor changes --- shell/install-dev | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/install-dev b/shell/install-dev index 3e8d299d..4f486d16 100644 --- a/shell/install-dev +++ b/shell/install-dev @@ -1,5 +1,5 @@ git checkout dev -cd node_modules +cd ../node_modules git clone git://github.com/coderaiser/minify cd minify git checkout dev From b2eacd2b590495100f0f7880bf2d4b9071f4fe31 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 5 Dec 2012 10:41:38 -0500 Subject: [PATCH 256/281] github and dropbox secret's moved out from config.json to env --- ChangeLog | 3 +++ README.md | 6 ++++-- lib/server.js | 2 +- lib/server/auth.js | 5 ++--- shell/secret.sh | 0 5 files changed, 10 insertions(+), 6 deletions(-) mode change 100644 => 100755 shell/secret.sh diff --git a/ChangeLog b/ChangeLog index fc951cef..9841de6a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -164,6 +164,9 @@ Added dropbox_id option. * Added getCurrentPath function to Util module. +* GitHub and DropBox secret's moved out from +config.json to env. + 2012.10.01, Version 0.1.7 diff --git a/README.md b/README.md index 8babfbed..1b025cc1 100644 --- a/README.md +++ b/README.md @@ -99,8 +99,10 @@ Authorization --------------- Thru openID Cloud Commander could authorize clients on GitHub. All things that should be done is must be added **id** and **secret** of application -from github settings page and added to **config.json** or env varibles with names: -*github_id* and *github_secret* that is more secure way. +from github settings page and added to **config.json** (id just) and env varible (secret) +with names: *github_id*, *github_secret*, *dropbox_key*, *dropbox_secret* etc. +For more information see *config.json* and *shell/seret.bat* (**on win32**) +or **shell/secret.sh** (**on *nix**). Starting diff --git a/lib/server.js b/lib/server.js index 1e03aad2..9a9e6671 100644 --- a/lib/server.js +++ b/lib/server.js @@ -59,7 +59,7 @@ Server : {}, /* КОНСТАНТЫ */ - INDEX : main.DIR + '/' + 'index.html' + INDEX : main.DIR + 'index.html' }, DirPath = '/', diff --git a/lib/server/auth.js b/lib/server/auth.js index df87ee0e..9ff9ba17 100644 --- a/lib/server/auth.js +++ b/lib/server/auth.js @@ -48,11 +48,10 @@ function authenticate(pCode, pCallBack) { var lId = Config.github_id, - lSecret = Config.github_secret, lEnv = process.env, - lClientId = lEnv.github_id || lId, - lClientSecret = lEnv.github_secret || lSecret; + lClientId = lEnv.github_id || lId, + lClientSecret = lEnv.github_secret; var data = qs.stringify({ client_id : lClientId, diff --git a/shell/secret.sh b/shell/secret.sh old mode 100644 new mode 100755 From 39c0405850d2bee4266b1c431faae35479751b23 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 5 Dec 2012 10:56:57 -0500 Subject: [PATCH 257/281] minor changes --- README.md | 387 +++++++++++++++++----------------- config.json | 6 +- lib/client/storage/_github.js | 2 +- lib/server/appcache.js | 3 +- lib/server/auth.js | 3 +- lib/server/rest.js | 30 ++- shell/secret.bat | 2 - shell/secret.sh | 2 - 8 files changed, 223 insertions(+), 212 deletions(-) diff --git a/README.md b/README.md index 1b025cc1..3de0f5ae 100644 --- a/README.md +++ b/README.md @@ -1,194 +1,195 @@ -Cloud Commander [![Build Status](https://secure.travis-ci.org/coderaiser/cloudcmd.png?branch=master)](http://travis-ci.org/coderaiser/cloudcmd) -=============== -**Cloud Commander** - user friendly cloud file manager. -DEMO: -[cloudfoundry] (http://cloudcmd.cloudfoundry.com "cloudfoundry"), -[appfog] (http://cloudcmd.aws.af.cm "appfog"). - -Google PageSpeed Score : [100](https://developers.google.com/speed/pagespeed/insights#url=http_3A_2F_2Fcloudcmd.cloudfoundry.com_2F&mobile=false "score") (out of 100) -(or 96 if js or css minification disabled in config.json). - -![Cloud Commander](https://raw.github.com/coderaiser/cloudcmd/dev/img/logo/cloudcmd.png) - -Benefits ---------------- -- full browser compatibility *(ie6+,chrome,safari,opera,firefox)*; -- responsible design -- one full page loading, *and then just one-time json-dir-listings loading -(with refresh opportunity).* -- caching readed directories *to localStorage (for now) -(so if network will disconnected or something heppen with a signal, we -definitely will can work with cached copy of directory listings)*; -- key binding -- disabled js support *(working in limited mode)*. -- automated minification *client js-files and onstart-reading Cloud manager files on server starting.* - -**Cloud Commander** uses all benefits of js, so if js is disabled, -we moves to *limited mode*. - -Limited-mode features ---------------- -- no keybinding -- no local caching -- full loading of all web page(with styles, js-scripts, html-page etc). - -Hot keys ---------------- -In all modern web browsers (but not in IE, becouse he special) hot keys works. -There is a short list: -- **Ctrl + r** - reload dir content -- **Ctrl + d** - clear local cache (wich contains dir contents) -- **Alt + q** - disable key bindings -- **Alt + s** - get all key bindings back -- **up, down, enter** - filesystem navigation -- **Alt + g** - authorization - -Viewer's hot keys ---------------- -- **Shift + F3** - open viewer window -- **Esc** - close viewer window - -Editor's hot keys ---------------- -- **F3** - open CodeMirror editor in read only mode -- **F4** - open CodeMirror editor -- **Esc** - close CodeMirror editor - -Documentation ---------------- -JS Doc documentation could be found in [http://jsdoc.info/coderaiser/cloudcmd/](http://jsdoc.info/coderaiser/cloudcmd/) - -Installing ---------------- -**Cloud Commander** installing is very easy. All you need it's just clone -repository from github. Just 2 commands: - - git clone git://github.com/coderaiser/cloudcmd.git - cd cloudcmd -or - - npm i cloudcmd - mv node_modules/cloudcmd ./ - -Configuration ---------------- -All main configuration could be done thrue config.json. -```js -{ - "cache" : {"allowed" : true}, /* cashing of js and css files in memory */ - "appcache" : false, /* html5 feature appcache */ - "minification" : { /* minification of js,css,html and img */ - "js" : false, /* minify module neaded */ - "css" : false, /* npm i minify */ - "html" : true, - "img" : false - }, - "github_id" : "891c251b925e4e967fa9", /* github app id */ - "github_secret" : "afe9bed1e810c5dc44c4c2a953fc6efb1e5b0545", /* github app secret */ - "dropbox_id" : "o7d6llji052vijk", - "show_keys_panel" : true, /* show classic panel with buttons of keys */ - "server" : true, /* server mode or testing mode */ - "logs" : false, /* logs or console ouput */ - "socket" : true /* enable web sockets */ - "port" : 80, /* Cloud Commander port */ - "ip" : "127.0.0.1", /* Cloud Commander IP */ - "rest" : true /* enable rest interface */ -} -``` -Authorization ---------------- -Thru openID Cloud Commander could authorize clients on GitHub. -All things that should be done is must be added **id** and **secret** of application -from github settings page and added to **config.json** (id just) and env varible (secret) -with names: *github_id*, *github_secret*, *dropbox_key*, *dropbox_secret* etc. -For more information see *config.json* and *shell/seret.bat* (**on win32**) -or **shell/secret.sh** (**on *nix**). - - -Starting ---------------- -To start **Cloud Commander** only one command neaded: - - node cloudcmd -or on win platform just - - cloudcmd -After thet Cloud Commander reads config file **config.json** and start server -on 80 port, if none of port varibles(*cloud9*, *cloudfoundry* and *nodester*) -isn't exist. -Then type in browser - - http://127.0.0.1 -or - - http://localhost -Updating ---------------- -**Cloud Commander** is very alfa and it's very often updatings. -Update is doing automagically but it could be done also manualy -by typing a few commands in cloudcmd directory: - - git pull -or check new version on npm - - npm info cloudcmd - -and then, if there is new version - - npm r cloudcmd - npm i cloudcmd - -Additional modules ---------------- -**Cloud Commander's Server Side** not using additional modules for main functionality. -But for minification and optimization tricks optional can be -assingned (and installed) modules: [Minify] (https://github.com/coderaiser/minify "Minify") -and [socket.io] (https://github.com/LearnBoost/socket.io "Socket.IO"). - -Install addtitional modules: - - npm i - -**Cloud Commander's Client Side** use module jquery for ajaxing. But only for old browsers. -We could not use this module, but this way is fast: -- google cdn -- gzip -- cache - -Perhaps in the future, it will not be used, but so far it has no effect on -start loading of Cloud Commander Client Side and do things fast and stable -it is using now. - -Extensions ---------------- -**Cloud Commander** desinged to easily porting extensions. -For extend main functionality Cloud Commander use next modules: -- [CodeMirror] [CodeMirrorURL] -- [FancyBox] [FancyBoxURL] -- [jQuery-contextMenu] [jQuery-contextMenuURL] -- [jquery.terminal] [jquery.terminalURL] - -[CodeMirrorURL]: https://github.com/marijnh/CodeMirror "CodeMirror" -[FancyBoxURL]: https://github.com/fancyapps/fancyBox "FancyBox" -[jQuery-contextMenuURL]: https://github.com/medialize/jQuery-contextMenu "jQuery-contextMenu" -[jquery.terminalURL]: https://github.com/jcubic/jquery.terminal "jquery.terminal" - -Contributing ---------------- -If you would like to contribute - send pull request to dev branch. -Getting dev version of **Cloud Commander**: - - git clone git://github.com/coderaiser/cloudcmd.git - git checkout dev - -It is possible thet dev version Cloud Commander will needed dev version of Minify, -so to get it you should type a couple more commands: - - cd node_modules - rm -rf minify - git clone git://github.com/coderaiser/minify - git checkout dev - -Special Thanks ---------------- +Cloud Commander [![Build Status](https://secure.travis-ci.org/coderaiser/cloudcmd.png?branch=master)](http://travis-ci.org/coderaiser/cloudcmd) +=============== +**Cloud Commander** - user friendly cloud file manager. +DEMO: +[cloudfoundry] (http://cloudcmd.cloudfoundry.com "cloudfoundry"), +[appfog] (http://cloudcmd.aws.af.cm "appfog"). + +Google PageSpeed Score : [100](https://developers.google.com/speed/pagespeed/insights#url=http_3A_2F_2Fcloudcmd.cloudfoundry.com_2F&mobile=false "score") (out of 100) +(or 96 if js or css minification disabled in config.json). + +![Cloud Commander](https://raw.github.com/coderaiser/cloudcmd/dev/img/logo/cloudcmd.png) + +Benefits +--------------- +- full browser compatibility *(ie6+,chrome,safari,opera,firefox)*; +- responsible design +- one full page loading, *and then just one-time json-dir-listings loading +(with refresh opportunity).* +- caching readed directories *to localStorage (for now) +(so if network will disconnected or something heppen with a signal, we +definitely will can work with cached copy of directory listings)*; +- key binding +- disabled js support *(working in limited mode)*. +- automated minification *client js-files and onstart-reading Cloud manager files on server starting.* + +**Cloud Commander** uses all benefits of js, so if js is disabled, +we moves to *limited mode*. + +Limited-mode features +--------------- +- no keybinding +- no local caching +- full loading of all web page(with styles, js-scripts, html-page etc). + +Hot keys +--------------- +In all modern web browsers (but not in IE, becouse he special) hot keys works. +There is a short list: +- **Ctrl + r** - reload dir content +- **Ctrl + d** - clear local cache (wich contains dir contents) +- **Alt + q** - disable key bindings +- **Alt + s** - get all key bindings back +- **up, down, enter** - filesystem navigation +- **Alt + g** - authorization + +Viewer's hot keys +--------------- +- **Shift + F3** - open viewer window +- **Esc** - close viewer window + +Editor's hot keys +--------------- +- **F3** - open CodeMirror editor in read only mode +- **F4** - open CodeMirror editor +- **Esc** - close CodeMirror editor + +Documentation +--------------- +JS Doc documentation could be found in [http://jsdoc.info/coderaiser/cloudcmd/](http://jsdoc.info/coderaiser/cloudcmd/) + +Installing +--------------- +**Cloud Commander** installing is very easy. All you need it's just clone +repository from github. Just 2 commands: + + git clone git://github.com/coderaiser/cloudcmd.git + cd cloudcmd +or + + npm i cloudcmd + mv node_modules/cloudcmd ./ + +Configuration +--------------- +All main configuration could be done thrue config.json. +```js +{ + "api_url" :"/api/v1", + "appcache" : false, /* html5 feature appcache */ + "cache" : {"allowed" : true}, /* cashing of js and css files in memory */ + "minification" : { /* minification of js,css,html and img */ + "js" : false, /* minify module neaded */ + "css" : false, /* npm i minify */ + "html" : true, + "img" : false + }, + "github_key" : "ec1f2e74b35a361e10bc", + "dropbox_key" : "0nd3ssnp5fp7tqs", + "dropbox_chooser_key" : "o7d6llji052vijk" + "show_keys_panel" : true, /* show classic panel with buttons of keys */ + "server" : true, /* server mode or testing mode */ + "logs" : false, /* logs or console ouput */ + "socket" : true /* enable web sockets */ + "port" : 80, /* Cloud Commander port */ + "ip" : "127.0.0.1", /* Cloud Commander IP */ + "rest" : true /* enable rest interface */ +} +``` +Authorization +--------------- +Thru openID Cloud Commander could authorize clients on GitHub. +All things that should be done is must be added **id** and **secret** of application +from github settings page and added to **config.json** (id just) and env varible (secret) +with names: *github_id*, *github_secret*, *dropbox_key*, *dropbox_secret* etc. +For more information see *config.json* and *shell/seret.bat* (**on win32**) +or **shell/secret.sh** (**on *nix**). + + +Starting +--------------- +To start **Cloud Commander** only one command neaded: + + node cloudcmd +or on win platform just + + cloudcmd +After thet Cloud Commander reads config file **config.json** and start server +on 80 port, if none of port varibles(*cloud9*, *cloudfoundry* and *nodester*) +isn't exist. +Then type in browser + + http://127.0.0.1 +or + + http://localhost +Updating +--------------- +**Cloud Commander** is very alfa and it's very often updatings. +Update is doing automagically but it could be done also manualy +by typing a few commands in cloudcmd directory: + + git pull +or check new version on npm + + npm info cloudcmd + +and then, if there is new version + + npm r cloudcmd + npm i cloudcmd + +Additional modules +--------------- +**Cloud Commander's Server Side** not using additional modules for main functionality. +But for minification and optimization tricks optional can be +assingned (and installed) modules: [Minify] (https://github.com/coderaiser/minify "Minify") +and [socket.io] (https://github.com/LearnBoost/socket.io "Socket.IO"). + +Install addtitional modules: + + npm i + +**Cloud Commander's Client Side** use module jquery for ajaxing. But only for old browsers. +We could not use this module, but this way is fast: +- google cdn +- gzip +- cache + +Perhaps in the future, it will not be used, but so far it has no effect on +start loading of Cloud Commander Client Side and do things fast and stable +it is using now. + +Extensions +--------------- +**Cloud Commander** desinged to easily porting extensions. +For extend main functionality Cloud Commander use next modules: +- [CodeMirror] [CodeMirrorURL] +- [FancyBox] [FancyBoxURL] +- [jQuery-contextMenu] [jQuery-contextMenuURL] +- [jquery.terminal] [jquery.terminalURL] + +[CodeMirrorURL]: https://github.com/marijnh/CodeMirror "CodeMirror" +[FancyBoxURL]: https://github.com/fancyapps/fancyBox "FancyBox" +[jQuery-contextMenuURL]: https://github.com/medialize/jQuery-contextMenu "jQuery-contextMenu" +[jquery.terminalURL]: https://github.com/jcubic/jquery.terminal "jquery.terminal" + +Contributing +--------------- +If you would like to contribute - send pull request to dev branch. +Getting dev version of **Cloud Commander**: + + git clone git://github.com/coderaiser/cloudcmd.git + git checkout dev + +It is possible thet dev version Cloud Commander will needed dev version of Minify, +so to get it you should type a couple more commands: + + cd node_modules + rm -rf minify + git clone git://github.com/coderaiser/minify + git checkout dev + +Special Thanks +--------------- [Elena Zalitok](http://vk.com/politilena "Elena Zalitok") for logo. \ No newline at end of file diff --git a/config.json b/config.json index 741f2fe2..2af0160c 100644 --- a/config.json +++ b/config.json @@ -1,5 +1,5 @@ { - "api_url" :"/api/v1", + "api_url" : "/api/v1", "appcache" : false, "cache" : {"allowed" : false}, "minification" : { @@ -8,7 +8,9 @@ "html" : false, "img" : false }, - "github_id" : "ec1f2e74b35a361e10bc", + "github_key" : "ec1f2e74b35a361e10bc", + "dropbox_key" : "0nd3ssnp5fp7tqs", + "dropbox_chooser_key" : "o7d6llji052vijk", "logs" : false, "show_keys_panel" : true, "server" : true, diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index 58b0cdc2..716dd764 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -9,7 +9,7 @@ var CloudCommander, Util, DOM, $, Github, cb; APIURL = '/api/v1', AuthURL = APIURL + '/auth', - GitHubIdURL = APIURL + '/github_id'; + GitHubIdURL = APIURL + '/github_key'; var GitHub_ID, GithubLocal, diff --git a/lib/server/appcache.js b/lib/server/appcache.js index 290a04a7..663409db 100644 --- a/lib/server/appcache.js +++ b/lib/server/appcache.js @@ -9,7 +9,8 @@ '# Module is part of Cloud Commander,' + '\n' + '# used for work with Aplication Cache.' + '\n' + '# If you wont to see at work set appcache: true' + '\n' + - '# in config.json and start cloudcmd.js' + '\n'); + '# in config.json and start cloudcmd.js' + '\n' + + '# http://github.com/coderaiser/cloudcmd' + '\n'); var main = global.cloudcmd.main, fs = main.fs, diff --git a/lib/server/auth.js b/lib/server/auth.js index 9ff9ba17..f32cb04b 100644 --- a/lib/server/auth.js +++ b/lib/server/auth.js @@ -11,7 +11,8 @@ '# If you wont to see at work set auth' + '\n' + '# parameters in config.json or environment' + '\n' + '# and start cloudcmd.js or just do' + '\n' + - '# require(\'auth.js\').auth(pCode, pCallBack)' + '\n'); + '# require(\'auth.js\').auth(pCode, pCallBack)' + '\n' + + '# http://github.com/coderaiser/cloudcmd' + '\n'); var main = global.cloudcmd.main, diff --git a/lib/server/rest.js b/lib/server/rest.js index c28766c0..260296d2 100644 --- a/lib/server/rest.js +++ b/lib/server/rest.js @@ -3,6 +3,16 @@ (function(){ "use strict"; + if(!global.cloudcmd) + return console.log( + '# rest.js' + '\n' + + '# -----------' + '\n' + + '# Module is part of Cloud Commander,' + '\n' + + '# used for work with REST API.' + '\n' + + '# If you wont to see at work set rest: true' + '\n' + + '# and api_url in config.json' + '\n' + + '# http://github.com/coderaiser/cloudcmd' + '\n'); + var main = global.cloudcmd.main, Util = main.util, Config = main.config, @@ -98,26 +108,26 @@ lCmd = pParams.command, lConfig = main.config, lEnv = process.env, - lEnvId, - lConfigId; + lEnvKey, + lConfigKey; switch(lCmd){ case '': lResult = {info: 'Cloud Commander API v1'}; break; - case 'github_id': - lEnvId = lEnv.github_id; - lConfigId = lConfig.github_id, + case 'github_key': + lEnvKey = lEnv.github_key; + lConfigKey = lConfig.github_key, - lResult = lEnvId || lConfigId; + lResult = lEnvKey || lConfigKey; break; - case 'dropbox_chooser_id': - lEnvId = lEnv.dropbox_chooser_id; - lConfigId = lConfig.dropbox_chooser_id; + case 'dropbox_chooser_key': + lEnvKey = lEnv.dropbox_chooser_key; + lConfigKey = lConfig.dropbox_chooser_key; - lResult = lEnvId || lConfigId; + lResult = lEnvKey || lConfigKey; break; case 'kill': diff --git a/shell/secret.bat b/shell/secret.bat index 1c72b43e..cd8a30d3 100644 --- a/shell/secret.bat +++ b/shell/secret.bat @@ -9,5 +9,3 @@ # like %github_secret% github_secret=e21b3724d84f0f7570d2d04d8e055f3cc3be3071 -dropbox_key=0nd3ssnp5fp7tqs -dropbox_chooser_key=o7d6llji052vijk diff --git a/shell/secret.sh b/shell/secret.sh index 16cb1425..2d41ff9a 100755 --- a/shell/secret.sh +++ b/shell/secret.sh @@ -9,5 +9,3 @@ # like $github_secret export github_secret=e21b3724d84f0f7570d2d04d8e055f3cc3be3071 -export dropbox_key=0nd3ssnp5fp7tqs -export dropbox_chooser_key=o7d6llji052vijk From 00ec53b0446d2f86e1cc849e3e7b28767ef47f0e Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 10 Dec 2012 10:34:12 +0200 Subject: [PATCH 258/281] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3de0f5ae..1a6dcbe2 100644 --- a/README.md +++ b/README.md @@ -102,8 +102,8 @@ Thru openID Cloud Commander could authorize clients on GitHub. All things that should be done is must be added **id** and **secret** of application from github settings page and added to **config.json** (id just) and env varible (secret) with names: *github_id*, *github_secret*, *dropbox_key*, *dropbox_secret* etc. -For more information see *config.json* and *shell/seret.bat* (**on win32**) -or **shell/secret.sh** (**on *nix**). +For more information see **config.json** and **shell/seret.bat** *(on win32)* +or **shell/secret.sh** *(on nix)*. Starting From 30165530d431d1e03532675230272221a80636d9 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 10 Dec 2012 04:20:19 -0500 Subject: [PATCH 259/281] fixed bug with auth in github --- ChangeLog | 1 + README.md | 3 ++- config.json | 5 +++-- lib/server/auth.js | 12 ++++++++---- shell/secret.bat | 3 ++- shell/secret.sh | 3 ++- 6 files changed, 18 insertions(+), 9 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9841de6a..484af02c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -167,6 +167,7 @@ Added dropbox_id option. * GitHub and DropBox secret's moved out from config.json to env. +* Fixed bug with auth in github. 2012.10.01, Version 0.1.7 diff --git a/README.md b/README.md index 1a6dcbe2..33d384ab 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,8 @@ All main configuration could be done thrue config.json. "html" : true, "img" : false }, - "github_key" : "ec1f2e74b35a361e10bc", + "github_key" : "891c251b925e4e967fa9", + "github_secret" : "afe9bed1e810c5dc44c4c2a953fc6efb1e5b0545", "dropbox_key" : "0nd3ssnp5fp7tqs", "dropbox_chooser_key" : "o7d6llji052vijk" "show_keys_panel" : true, /* show classic panel with buttons of keys */ diff --git a/config.json b/config.json index 2af0160c..2cee4339 100644 --- a/config.json +++ b/config.json @@ -6,9 +6,10 @@ "js" : false, "css" : false, "html" : false, - "img" : false + "img" : true }, - "github_key" : "ec1f2e74b35a361e10bc", + "github_key" : "891c251b925e4e967fa9", + "github_secret" : "afe9bed1e810c5dc44c4c2a953fc6efb1e5b0545", "dropbox_key" : "0nd3ssnp5fp7tqs", "dropbox_chooser_key" : "o7d6llji052vijk", "logs" : false, diff --git a/lib/server/auth.js b/lib/server/auth.js index f32cb04b..04f56c83 100644 --- a/lib/server/auth.js +++ b/lib/server/auth.js @@ -48,12 +48,16 @@ }; function authenticate(pCode, pCallBack) { - var lId = Config.github_id, + var lId = Config.github_key, + lSecret = Config.github_secret, lEnv = process.env, - lClientId = lEnv.github_id || lId, - lClientSecret = lEnv.github_secret; - + lClientId = lEnv.github_key || lId, + lClientSecret = lEnv.github_secret || lSecret; + + console.log(lClientId); + console.log(lClientSecret); + var data = qs.stringify({ client_id : lClientId, client_secret : lClientSecret, diff --git a/shell/secret.bat b/shell/secret.bat index cd8a30d3..b6087e80 100644 --- a/shell/secret.bat +++ b/shell/secret.bat @@ -8,4 +8,5 @@ # for using just add %-symbol on start and end of name # like %github_secret% -github_secret=e21b3724d84f0f7570d2d04d8e055f3cc3be3071 +set github_key=435c670f44320e287ad6 +set github_secret=a8b49e8c8c1a6253b149d224149a076270d8d614 diff --git a/shell/secret.sh b/shell/secret.sh index 2d41ff9a..6b4ecfba 100755 --- a/shell/secret.sh +++ b/shell/secret.sh @@ -1,3 +1,4 @@ +#!/bin/bash # # part of Cloud Commander # *nix version @@ -8,4 +9,4 @@ # for using just add $-symbol on start of name # like $github_secret -export github_secret=e21b3724d84f0f7570d2d04d8e055f3cc3be3071 +export github_secret=afe9bed1e810c5dc44c4c2a953fc6efb1e5b0545 From 707914bcffbb01da72ca4ca43e6abe74321f1bda Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 10 Dec 2012 04:52:19 -0500 Subject: [PATCH 260/281] fixed bug with opening empty files in CodeMiror. Editor window could not be cloused --- ChangeLog | 4 ++++ lib/util.js | 17 +++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 484af02c..b79425b4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -169,6 +169,10 @@ config.json to env. * Fixed bug with auth in github. +* Fixed bug with opening empty files in CodeMiror. +Editor window could not be cloused. + + 2012.10.01, Version 0.1.7 * Changed name of menu files, fixed npm and jitsu diff --git a/lib/util.js b/lib/util.js index 9143bc54..5c7ec2ca 100644 --- a/lib/util.js +++ b/lib/util.js @@ -116,7 +116,7 @@ var Util, exports; return Util.loadOnLoad(lFunc_a, pData); }; - if(pData) + if( !Util.isUndefined(pData) ) pData = { data : pData, callback : lCallBack @@ -194,6 +194,14 @@ var Util, exports; return Util.isType(pVarible, 'boolean'); }; + /** + * functions check is pVarible is function + * @param pVarible + */ + Util.isFunction = function(pVarible){ + return Util.isType(pVarible, 'function'); + }; + /** * functions check is pVarible is object * @param pVarible @@ -211,12 +219,13 @@ var Util, exports; }; /** - * functions check is pVarible is function + * functions check is pVarible is string * @param pVarible */ - Util.isFunction = function(pVarible){ - return Util.isType(pVarible, 'function'); + Util.isUndefined = function(pVarible){ + return Util.isType(pVarible, 'undefined'); }; + /** * functions check is pVarible is pType * @param pVarible From 74cf4d5cdfa79758820b4fbe424080509830b9be Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 10 Dec 2012 05:19:26 -0500 Subject: [PATCH 261/281] added windows support on terminal command: "cloudcmd exit" --- ChangeLog | 1 + lib/server/socket.js | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index b79425b4..41e88a24 100644 --- a/ChangeLog +++ b/ChangeLog @@ -172,6 +172,7 @@ config.json to env. * Fixed bug with opening empty files in CodeMiror. Editor window could not be cloused. +* Added windows support on terminal command: "cloudcmd exit". 2012.10.01, Version 0.1.7 diff --git a/lib/server/socket.js b/lib/server/socket.js index f20f5c14..20579ca7 100644 --- a/lib/server/socket.js +++ b/lib/server/socket.js @@ -60,7 +60,10 @@ update.get(); if( Util.strCmp(pCommand, 'exit') ) - pCommand = 'kill -9 ' + process.pid; + if(main.WIN32) + pCommand = 'taskkill /PID ' + process.pid; + else + pCommand = 'kill -9 ' + process.pid; } else { var lMsg = { From f21d4a06574f506ef7045da0112d95fd6cdf8da3 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 10 Dec 2012 05:23:13 -0500 Subject: [PATCH 262/281] fixed IP on windows --- ChangeLog | 2 ++ lib/server.js | 10 +++------- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index 41e88a24..28e2b84c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -174,6 +174,8 @@ Editor window could not be cloused. * Added windows support on terminal command: "cloudcmd exit". +* Fixed IP on windows. + 2012.10.01, Version 0.1.7 * Changed name of menu files, fixed npm and jitsu diff --git a/lib/server.js b/lib/server.js index 9a9e6671..c25cffd4 100644 --- a/lib/server.js +++ b/lib/server.js @@ -12,8 +12,7 @@ Config : { server : true, socket : true, - port : 80, - ip : '127.0.0.1' + port : 80 }, /* функция, которая генерирует заголовки @@ -146,11 +145,8 @@ this.IP = process.env.IP || /* c9 */ this.Config.ip; - /* if Cloud Server started on jitsu */ - if(process.env.HOME && - !process.env.HOME.indexOf('/opt/haibu')) { - this.IP = '0.0.0.0'; - } + if(!this.IP) + this.IP = main.Win32 ? '127.0.0.1' : '0.0.0.0'; /* server mode or testing mode */ if (lConfig.server) { From 2db31011396708b9c91bd7b28db5b2df327f9f70 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 10 Dec 2012 05:30:12 -0500 Subject: [PATCH 263/281] minor changes --- lib/server.js | 2 +- lib/server/socket.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/server.js b/lib/server.js index c25cffd4..04c36819 100644 --- a/lib/server.js +++ b/lib/server.js @@ -146,7 +146,7 @@ this.Config.ip; if(!this.IP) - this.IP = main.Win32 ? '127.0.0.1' : '0.0.0.0'; + this.IP = main.WIN32 ? '127.0.0.1' : '0.0.0.0'; /* server mode or testing mode */ if (lConfig.server) { diff --git a/lib/server/socket.js b/lib/server/socket.js index 20579ca7..a01346f5 100644 --- a/lib/server/socket.js +++ b/lib/server/socket.js @@ -61,7 +61,7 @@ if( Util.strCmp(pCommand, 'exit') ) if(main.WIN32) - pCommand = 'taskkill /PID ' + process.pid; + pCommand = 'taskkill -f /PID ' + process.pid; else pCommand = 'kill -9 ' + process.pid; } From ddffc4df662c8682e5e70a6c37fe9295bbd6adef Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 10 Dec 2012 06:06:16 -0500 Subject: [PATCH 264/281] minor changes --- ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 28e2b84c..3007bf8f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -20,7 +20,7 @@ started. * Added more demo mirrors to readme (appfog, cloudfoundry). -* Removed resing event from jquery-terminal. +* Removed resize event from jquery-terminal. * Fixed bug with error code of program execution in terminal. From 5a11d0857a0d4e6d9b6c9c58e0d418d5f1a5f13a Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 10 Dec 2012 06:50:04 -0500 Subject: [PATCH 265/281] Added ability to delete files (not for real for now) thru keys panel (F8) --- ChangeLog | 4 ++++ lib/client.js | 16 ++++++++-------- lib/client/dom.js | 21 ++++++++++++++++----- lib/client/keyBinding.js | 2 +- lib/client/menu.js | 2 +- 5 files changed, 30 insertions(+), 15 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3007bf8f..825d17ee 100644 --- a/ChangeLog +++ b/ChangeLog @@ -176,6 +176,10 @@ Editor window could not be cloused. * Fixed IP on windows. +* Added ability to delete files (not for real for now) +thru keys panel (F8). + + 2012.10.01, Version 0.1.7 * Changed name of menu files, fixed npm and jitsu diff --git a/lib/client.js b/lib/client.js index a500906c..ae3e3767 100644 --- a/lib/client.js +++ b/lib/client.js @@ -386,14 +386,14 @@ function initKeysPanel(pCallBack){ lFuncs =[ null, - null, /* f1 */ - null, /* f2 */ - cloudcmd.Viewer, /* f3 */ - cloudcmd.Editor, /* f4 */ - null, /* f5 */ - null, /* f6 */ - null, /* f7 */ - null, /* f8 */ + null, /* f1 */ + null, /* f2 */ + cloudcmd.Viewer, /* f3 */ + cloudcmd.Editor, /* f4 */ + null, /* f5 */ + null, /* f6 */ + null, /* f7 */ + DOM.promptRemoveCurrent,/* f8 */ ]; for(var i = 1; i <= 8; i++){ diff --git a/lib/client/dom.js b/lib/client/dom.js index 4b19067b..13272b85 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -270,13 +270,24 @@ var CloudCommander, Util, DOM, CloudFunc; * * @pCurrentFile */ - DOM.delete = function(pCurrentFile){ - var lMsg = 'Are you sure thet you wont delete ', - lName = DOM.getCurrentName(pCurrentFile), - lRet = confirm(lMsg + lName + '?'); + DOM.promptRemoveCurrent = function(pCurrentFile){ + var lRet, + lCurrent, + lName, + lMsg = 'Are you sure thet you wont delete '; + + /* dom element passed and it is not event */ + if(pCurrentFile && !pCurrentFile.type) + lCurrent = pCurrentFile; + + lName = DOM.getCurrentName(lCurrent); + + lRet = confirm(lMsg + lName + '?'); if(lRet) - DOM.removeCurrent(pCurrentFile); + DOM.removeCurrent(lCurrent); + + return lRet; }; /** diff --git a/lib/client/keyBinding.js b/lib/client/keyBinding.js index 51cc700d..89f16980 100644 --- a/lib/client/keyBinding.js +++ b/lib/client/keyBinding.js @@ -117,7 +117,7 @@ var CloudCommander, Util, DOM; event.preventDefault();//запрет на дальнейшее действие } else if(lKeyCode === KEY.Delete) - DOM.delete(lCurrentFile); + DOM.promptRemoveCurrent(lCurrentFile); /* if f3 or shift+f3 or alt+f3 pressed */ else if(lKeyCode === KEY.F3){ diff --git a/lib/client/menu.js b/lib/client/menu.js index 4ed5b91d..ab18b7b1 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -59,7 +59,7 @@ var CloudCommander, Util, DOM, CloudFunc, $; delete: {name: 'Delete', callback: function(key, opt){ - DOM.delete(); + DOM.promptRemoveCurrent(); }}, upload: { From da450096ea6bcea0f801b6bb263994504d547213 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 10 Dec 2012 06:56:04 -0500 Subject: [PATCH 266/281] changed "Upload" menu item to "Upload to" --- lib/client/menu.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/client/menu.js b/lib/client/menu.js index ab18b7b1..6babe02e 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -63,9 +63,9 @@ var CloudCommander, Util, DOM, CloudFunc, $; }}, upload: { - name: "Upload", + name: "Upload to", items: { - 'upload_to_gist': {name: 'to Gist', + 'upload_to_gist': {name: 'Gist', callback: function(key, opt){ var lCurrent = DOM.getCurrentFile(), lPath = DOM.getCurrentPath(), From dc1c95818f556ff4f524e135bda439aa449945ce Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 10 Dec 2012 06:58:15 -0500 Subject: [PATCH 267/281] changed "Upload" menu item to "Upload to" --- ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog b/ChangeLog index 825d17ee..39b05793 100644 --- a/ChangeLog +++ b/ChangeLog @@ -179,6 +179,7 @@ Editor window could not be cloused. * Added ability to delete files (not for real for now) thru keys panel (F8). +* Changed "Upload" menu item to "Upload to". 2012.10.01, Version 0.1.7 From 9f4e81a9c290063d1dc98d896254c5407841cd9f Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 10 Dec 2012 07:03:46 -0500 Subject: [PATCH 268/281] minor changes --- shell/deploy.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/shell/deploy.sh b/shell/deploy.sh index fdda2c00..316dbf69 100755 --- a/shell/deploy.sh +++ b/shell/deploy.sh @@ -1,3 +1,4 @@ +cd .. echo 'appfog' af update echo 'http://cloudcmd.aws.af.cm/' @@ -7,4 +8,5 @@ echo 'http://cloudcmd.cloudfoundry.com/' echo 'nodester' git push nodester master echo 'heroku' -git push heroku master \ No newline at end of file +git push heroku master +cd shell \ No newline at end of file From 6dd8a0b1fc9d74f9f7cd24f5f180c324c841afab Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 11 Dec 2012 04:29:42 -0500 Subject: [PATCH 269/281] fixed bug with minified styles --- ChangeLog | 3 +++ config.json | 4 ++-- index.html | 6 +++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/ChangeLog b/ChangeLog index 39b05793..b575453f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -181,6 +181,9 @@ thru keys panel (F8). * Changed "Upload" menu item to "Upload to". +* Fixed bug with minified styles. + + 2012.10.01, Version 0.1.7 * Changed name of menu files, fixed npm and jitsu diff --git a/config.json b/config.json index 2cee4339..7f9801a6 100644 --- a/config.json +++ b/config.json @@ -4,9 +4,9 @@ "cache" : {"allowed" : false}, "minification" : { "js" : false, - "css" : false, + "css" : true, "html" : false, - "img" : true + "img" : false }, "github_key" : "891c251b925e4e967fa9", "github_secret" : "afe9bed1e810c5dc44c4c2a953fc6efb1e5b0545", diff --git a/index.html b/index.html index e6e42b00..021bdf21 100644 --- a/index.html +++ b/index.html @@ -5,9 +5,9 @@ Cloud Commander - - - + + + - @@ -44,6 +38,8 @@ diff --git a/lib/server/minify.js b/lib/server/minify.js index 3ef7e2b1..3bcf6ff4 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -4,7 +4,8 @@ "use strict"; var main = global.cloudcmd.main, - DIR = main.DIR; + DIR = main.DIR, + LIBDIR = main.LIBDIR; exports.Minify = { /* pathes to directories */ @@ -68,7 +69,7 @@ lStyleCSS = DIR + 'css/style.css', lResetCSS = DIR + 'css/reset.css'; if (this._allowed.js) { - lOptimizeParams.push(DIR + 'client.js'); + lOptimizeParams.push(LIBDIR + 'client.js'); } if (this._allowed.html) From a81dc6e75f8b30b195dc00977f0b463d6fb76b9e Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 11 Dec 2012 09:39:57 -0500 Subject: [PATCH 279/281] disabled travis email notifications --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7c07d30d..015e3dc5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,5 +7,5 @@ notifications: #webhooks: #http://requestb.in/12h5bl71 email: - on_success: change + on_success: never on_failure: change \ No newline at end of file From fb2faec364a8fcd14e197044671175f3efd99074 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 11 Dec 2012 09:53:04 -0500 Subject: [PATCH 280/281] fixed bug with appcache --- ChangeLog | 2 ++ cloudcmd.js | 2 +- config.json | 2 +- lib/client/storage/_dropbox.js | 34 ++++++++++++++++------------------ lib/server/appcache.js | 4 ++-- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/ChangeLog b/ChangeLog index cda9df8f..d4707bf5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -188,6 +188,8 @@ Just propose install git and clone from github repo. * Fixed bug with client.js minifying. +* Fixed bug with appcache. + 2012.10.01, Version 0.1.7 diff --git a/cloudcmd.js b/cloudcmd.js index 2dc1851f..e0a36212 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -80,7 +80,7 @@ {'//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js' : './lib/client/jquery.js'}]; if(srv.Minify._allowed.css) - lFiles.push('./min/all.min.css'); + lFiles.push('node_modules/minify/min/all.min.css'); lAppCache.addFiles(lFiles); lAppCache.createManifest(); diff --git a/config.json b/config.json index 3c8d7ff1..f21f6665 100644 --- a/config.json +++ b/config.json @@ -3,7 +3,7 @@ "appcache" : false, "cache" : {"allowed" : false}, "minification" : { - "js" : true, + "js" : false, "css" : true, "html" : true, "img" : true diff --git a/lib/client/storage/_dropbox.js b/lib/client/storage/_dropbox.js index c6fff5c2..5532e1a9 100644 --- a/lib/client/storage/_dropbox.js +++ b/lib/client/storage/_dropbox.js @@ -4,20 +4,19 @@ var CloudCommander, DOM, Dropbox; (function(){ "use strict"; - const cloudcmd = CloudCommander, - CHOOSER_API = 'https://www.dropbox.com/static/api/1/dropbox.js'; - - var CLIENT_ID, - DropBoxStore = {}, - options = { - linkType: "direct", - success: function(files) { - console.log("Here's the file link:" + files[0].link); - }, - cancel: function() { - console.log('Chose something'); - } - }; + var cloudcmd = CloudCommander, + CHOOSER_API = 'https://www.dropbox.com/static/api/1/dropbox.js', + CLIENT_ID, + DropBoxStore = {}, + options = { + linkType: "direct", + success: function(files) { + console.log("Here's the file link:" + files[0].link); + }, + cancel: function() { + console.log('Chose something'); + } + }; /* PRIVATE FUNCTIONS */ @@ -35,7 +34,7 @@ var CloudCommander, DOM, Dropbox; func : DropBoxStore.choose }); - + var lDropBoxId = pConfig.dropbox_chooser_key; lElement.setAttribute('data-app-key', lDropBoxId); document.body.appendChild(lElement); @@ -43,14 +42,13 @@ var CloudCommander, DOM, Dropbox; console.timeEnd('dropbox load'); }); } - + DropBoxStore.choose = function(){ Dropbox.choose(options); }; - + DropBoxStore.init = function(){ load(); - this.init = null; }; cloudcmd.DropBox = DropBoxStore; diff --git a/lib/server/appcache.js b/lib/server/appcache.js index 663409db..e30028a2 100644 --- a/lib/server/appcache.js +++ b/lib/server/appcache.js @@ -59,11 +59,11 @@ exports.createManifest = function(){ - var lAllNames = main.require('hashes'); + var lAllNames = main.require('node_modules/minify/hashes'); if(lAllNames) for(var lName in lAllNames){ if(lName.indexOf('min') > 0) - lName = './min/' + lName; + lName = 'node_modules/minify/min/' + lName; exports.watch(lName); } processManifest(); From 9e11a202369db64ee2fc7ec9283e03a1ded65435 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 11 Dec 2012 10:59:36 -0500 Subject: [PATCH 281/281] minor changes --- cloudcmd.js | 10 +++++----- config.json | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index e0a36212..596e188a 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -49,16 +49,16 @@ lReplace_s = ''; - lData = Util.removeStr(lData, lReplace_s); - lData = lData.replace('/css/style.css', lPath + 'all.min.css'); + lData = Util.removeStr(lData, lReplace_s) + .replace('/css/style.css', lPath + 'all.min.css'); } lReplace_s = '
    '; - lData = lData.replace(lReplace_s, lReplace_s + lAdditional); /* меняем title */ - lData = lData.replace('Cloud Commander', - '' + CloudFunc.getTitle() + ''); + lData = lData.replace(lReplace_s, lReplace_s + lAdditional) + .replace('Cloud Commander', + '' + CloudFunc.getTitle() + ''); if(!srv.Config.appcache) lData = Util.removeStr(lData, ' manifest="/cloudcmd.appcache"'); diff --git a/config.json b/config.json index f21f6665..3c8d7ff1 100644 --- a/config.json +++ b/config.json @@ -3,7 +3,7 @@ "appcache" : false, "cache" : {"allowed" : false}, "minification" : { - "js" : false, + "js" : true, "css" : true, "html" : true, "img" : true